【问题标题】:Issues with SQL Server triggerSQL Server 触发器的问题
【发布时间】:2020-12-24 02:32:38
【问题描述】:

我创建了一个 SQL Server 触发器,如下所示。

create trigger testtrigger on testdb
for insert
as
declare @sql varchar(8000)

SELECT @sql = 'bcp "select * from testdb" queryout c:/test.txt -c -t -T -S localhost'

exec xp_cmdshell @sql

go

但是当在testdb中插入一行时,创建了文本文件,但是没有内容。有人可以帮我解决这个问题吗?

【问题讨论】:

  • 触发器应该非常小、快速和灵活 - 您应该绝对不从触发器调用外部服务,例如 bcp 实用程序!您需要重新考虑您的设计
  • 触发器还应该处理插入在一个语句中的多行(是的,可能,阅读 sql 语法)。
  • @TomTom 尽管存在其他问题,但此触发器确实支持多行插入。
  • 那么您推荐的解决方案是什么,可以将每个插入的行导出到文本文件。

标签: sql-server tsql triggers


【解决方案1】:

除了关于不使用该方法的建议之外,我想问您是否确定插入已完成。

最可能的情况是您面临死锁,以及一种 SQL 无法处理的死锁,因为其中一个资源超出了 SQL 范围(命令行)。

问题是,当您完成插入时,触发器会被激活,而不会释放在testtrigger 中获取的独占锁。然后尝试对testtrigger 进行选择,但由于仍然存在锁定,因此选择需要等待这些锁定被释放,这永远不会发生,因为您在等待选择时没有关闭事务。

所以文件被创建了,但是由于死锁,插入和选择永远不会完成。 您将通过添加更新命令来看到它的工作:

'bcp "select * from inserted with(nolock)" queryout c:/test.txt -c -t -T -S localhost'

当然不推荐整个方法,但我希望它可以帮助您了解情况。

【讨论】:

  • 我会试试这个命令。但是对于每行插入,将行导出为文本的解决方案是什么。
  • 感谢它与添加的 nolock 一起使用。那么你推荐什么解决方案来实现这个要求。
  • 这取决于这样做的目的。如果只是为了跟踪更改并且您使用的是 SQL 2016 或更高版本,则可以启用临时表 (docs.microsoft.com/en-us/sql/relational-databases/tables/…)。如果您确实需要导出文件,但您可以按需运行它(而不是在表获取新行时生成文件),您可以指定特定的日期/时间并拥有表的确切快照当时。
  • 满足特定条件时,我需要立即将行导出到文本文件。例如,当有人在特定门上刷门禁卡时,我需要导出该行。不需要所有的行。
  • 好的,但是该文件的目的是什么,用于审核,供某人阅读,供另一个进程在文件出现时触发某些内容?如果这是一个流量和数据量很小的小型解决方案,我会继续采用这种方法。但是,如果您需要更健壮、可扩展的东西等,我会: - 实现 Service Broker - 使用 AFTER INSERT 触发器仅发送插入到服务代理中的队列的信息 - 使用 CLR 存储过程编写文件插入的新数据
【解决方案2】:

尝试在插入后创建触发器,然后从“插入”伪表而不是实际表“testdb”导出。新插入的行将被导出。

create trigger testtrigger on testdb
after insert
as
if exists (select * from inserted)
    begin
        declare @sql        varchar(8000),
                @guidName   char(36)=newid();
        
        SELECT @sql = concat('bcp "select * from inserted" queryout c:/',
                             @guidName, '.txt -c -t -T -S localhost');

        exec xp_cmdshell @sql
    end
go

【讨论】:

  • 这永远行不通。插入的表在触发器外不可见。
  • 查询在触发器范围内执行。当然可以。在触发器中执行的动态 SQL 可以引用伪表。我可以为您提供一个示例,但您应该自己尝试一下
  • 它在没有锁定命令的情况下工作。那么您能否建议在满足特定条件时将行导出到文本文件的推荐解决方案。
  • @Subu,您在上面写道:“......为每行插入将行导出为文本的解决方案是什么” 代码已从引用“testdb”表切换到“插入”表. 'inserted' 表只包含新插入的行,所以它应该满足要求。要进一步筛选导出哪些行,您可以将连接条件或 WHERE 子句添加到动态 SQL。 “插入”被称为“虚拟(或伪)表”,因为它不是永久分配的,只能在触发器范围内访问。
  • 我创建了一个触发器来将所需的行移动到另一个表中,如下所示。使用 test go alter TRIGGER trigger2 ON testtable1 在 INSERT AS INSERT INTO testtable2 SELECT * FROM insert where leaf = 'test22' go 之后。现在如何将每一行导出到路径中的文本文件中。我可以在插入新行后立即将它们导出到文本文件中。但是文本文件包含以前插入的所有行。我只需要插入的新行,每个文本文件只需要一行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-09-21
  • 1970-01-01
  • 1970-01-01
  • 2011-09-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多