【问题标题】:SQL Stored Procedure with Input parameters with While loop带输入参数的 SQL 存储过程和 While 循环
【发布时间】:2018-07-05 23:59:18
【问题描述】:

我在下面有一个代码,应该将记录插入表中,但不幸的是,如果插入、更新或删除多条记录,此代码将不起作用。我应该如何重写程序代码以遍历所有插入/删除的记录?而且我确实需要使用带有输入参数的存储过程(不仅仅是简单的插入...选择...从...)

IF EXISTS (SELECT * FROM MyDB.sys.triggers WHERE object_id = OBJECT_ID(N'[dbo].[MyTable_DEL_UPD_INS]'))
    DROP TRIGGER [dbo].[MyTable_DEL_UPD_INS]
GO

CREATE TRIGGER [dbo].[MyTable_DEL_UPD_INS]
ON [MyDB].[dbo].[MyTable] 
AFTER DELETE, UPDATE, INSERT
NOT FOR REPLICATION
AS 
BEGIN
    DECLARE @PKId INT, 
            @Code VARCHAR(5), 
            @AuditType VARCHAR(10)

    SET @Code = 'TEST'

    IF EXISTS (SELECT * FROM deleted d)
       AND NOT EXISTS (SELECT * FROM inserted i) 
    BEGIN 
        SELECT TOP 1 
            @PKId = d.[MyTable_PK],
            @AuditType = 'DELETE' 
        FROM 
            deleted d WITH (NOLOCK)

        IF @PKId IS NOT NULL 
           AND @Code IS NOT NULL 
            EXEC MyDB.[dbo].[SP_Audit] @PKId, @Code, @AuditType 
    END

    IF EXISTS (SELECT * FROM deleted d)
        AND EXISTS (SELECT * FROM inserted i) 
    BEGIN 
        SELECT TOP 1 
            @PKId = d.[MyTable_PK],
            @AuditType = 'UPDATE' 
        FROM 
            deleted d WITH (NOLOCK)

        IF @PKId IS NOT NULL 
           AND @Code IS NOT NULL 
            EXEC MyDB.[dbo].[SP_Audit] @PKId, @Code, @AuditType 
    END

    IF NOT EXISTS (SELECT * FROM deleted d)
        AND EXISTS (SELECT * FROM inserted i) 
    BEGIN 
        SELECT TOP 1 
            @PKId = d.[MyTable_PK],
            @AuditType = 'INSERT' 
        FROM 
            deleted d WITH (NOLOCK)

        IF @PKId IS NOT NULL 
           AND @Code IS NOT NULL 
            EXEC MyDB.[dbo].[SP_Audit] @PKId, @Code, @AuditType 
    END
END 
GO

ALTER TABLE [MyDB].[dbo].[MyTable] ENABLE TRIGGER [MyTable_DEL_UPD_INS]

【问题讨论】:

  • 您可以修改 SP_Audit 以接受表值吗?
  • 在触发器中有一个循环就像一个非常糟糕的主意。它会严重影响性能。再加上你有一个 SP 的事实,就目前而言,它需要一个进一步的循环才能使用(这样对于多行,你循环遍历每一行),你最终得到一个简单的 @ 987654322@ 包含 100 左右的行,需要大量资源。 SP_Audit 在做什么?你能提供它的SQL吗?如果您必须使用它,这可能意味着您需要更改它;这意味着您可能需要更改引用它的所有内容...
  • 另外,WITH (NOLOCK) 不会在您的触发器中实现任何功能。事实上,我建议你删除它;因为它更可能导致问题而不是解决任何问题。
  • 我同意你的看法!性能会非常糟糕。 Th SP 只是 Select Into... Insert ... From ... 仅此而已:)。我可能会试图说服我的队友放弃使用 SP。他非常渴望使用它,因为在他看来它很优雅......
  • 对 WITH (NOLOCK) 用法的好评。在我的公司里,他们甚至把它放在 CTE 和 Temp 表上。看不到任何意义......我可以说同意在物理对象上更有意义。

标签: sql-server stored-procedures while-loop triggers do-while


【解决方案1】:

您应该避免在触发器中使用循环。
触发器应该尽可能快地运行,因为 SQL Server 不会将控制权返回给触发触发器 until the trigger is completed.

的任何语句

因此,您应该修改 SP_Audit 过程以处理多条记录而不是单条记录,而不是循环。
通常,这很容易使用表值参数来完成。
如果您也可以发布SP_Audit,我们可以为您提供完整的解决方案。
由于您没有发布它,您可以使用以下指南作为开始:

首先,您创建一个用户定义的表类型:

CREATE TYPE dbo.Ids AS TABLE
(
    Id int NOT NULL PRIMARY KEY
)

GO

然后,您创建使用它的过程:

CREATE PROCEDURE [dbo].[STP_Audit_MultipleRecords]
(
    @IDs dbo.Ids readonly, 
    @Code CHAR(4), 
    @AuditType CHAR(6)
)
AS
-- Implementation here
GO

最后,你写你的触发器是这样的:

CREATE TRIGGER [dbo].[MyTable_DEL_UPD_INS]
ON [MyDB].[dbo].[MyTable] 
AFTER DELETE, UPDATE, INSERT
NOT FOR REPLICATION
AS 
BEGIN

    DECLARE @HasDeleted bit = 0, 
            @HasInserted bit = 0,
            @AuditType CHAR(6),
            @Code CHAR(4)

    SET @Code = 'TEST'

    DECLARE @IDs as dbo.Ids

    IF EXISTS (SELECT * FROM deleted d)
        SET @HasDeleted = 1

    IF EXISTS (SELECT * FROM inserted i) 
        SET @HasInserted = 1


    IF @HasDeleted = 1 
    BEGIN
        IF @HasInserted = 1
        BEGIN
             SET @AuditType = 'UPDATE' 
        END
        ELSE
        BEGIN
            SET @AuditType = 'DELETE' 
        END
    END
    ELSE
    IF @HasInserted = 1
        BEGIN
            SET @AuditType = 'INSERT' 
        END

    INSERT INTO @IDs (Id)
    SELECT [MyTable_PK]
    FROM inserted
    UNION 
    SELECT [MyTable_PK]
    FROM deleted 

    EXEC [dbo].[STP_Audit_MultipleRecords] @IDs, @Code, @AuditType 

END 
GO

注意事项:

  • @HasDeleted@HasInserted 变量允许您对每个过程只执行一次 EXISTS 查询。

  • 使用单个 union 查询从 deletedinserted 表中获取主键值。由于union 消除了重复值,因此您只需编写一次此查询。如果您愿意,您可以为每种审计类型编写不同的查询,但是您必须重复相同的查询 3 次(使用不同的表)

  • 我已将您的 @code@AuditType 变量的数据类型更改为 char,因为它们具有固定长度。

【讨论】:

    猜你喜欢
    • 2014-08-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-24
    • 2018-03-23
    • 1970-01-01
    • 1970-01-01
    • 2012-09-23
    相关资源
    最近更新 更多