触发器上的光标

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了触发器上的光标相关的知识,希望对你有一定的参考价值。

我正在使用SQL Server 2008。

我有一个在表中定义的INSERT,UPDATE和DELETE操作的后触发器。我的问题是,目前我的触发器一次插入一条记录,我需要多条记录

SELECT TOP 1 @ParentID FROM ... WHERE ID = @ID 

返回多个唯一记录。

(请参阅下面的这条评论“ - 此子查询返回的值超过1,因此我需要在搜索审计表中插入与返回时一样多的ParentID”)

我相信我需要使用游标,但我不确定在哪里确切地声明和打开游标。

--CREATE PROCEDURE [dbo].[SP_Auditing]
--      @ID INT, @Code VARCHAR(3), @AuditType VARCHAR(10), @ParentCode VARCHAR(3) = NULL, @ParentID INT = NULL
--AS
--BEGIN
--          INSERT INTO myDB.dbo.Table1 (ID, Code, AuditType, ParentCode, ParentID)

--          VALUES(@ID, @Code, @AuditType, @ParentCode, @ParentID)
--END

GO

CREATE TRIGGER [dbo].[Tr_MyFavouriteTable_UPD_INSERT_DEL]    ON [dbo].[MyFavouriteTable] AFTER INSERT, DELETE, UPDATE    NOT FOR REPLICATION 

AS   

BEGIN
            DECLARE @ID INT,            @Code VARCHAR(3),           @AuditType VARCHAR(10),             @ParentCode VARCHAR(3),             @ParentID INT       SET     @Code = 'DOC'

    IF EXISTS (SELECT 1 FROM inserted)          AND 
       NOT EXISTS (SELECT 1 FROM deleted) 
                BEGIN 
                        SELECT TOP 1  
                @ID = ins.ID, 
                @ParentID = (
                    SELECT TOP 1 CAST(RIGHT(parentId,LEN(parentId) - LEN(LEFT(parentId,3))) AS INT) 
                    FROM [MyDB].[dbo].[MyFavouriteTable] t WITH (NOLOCK)
                    INNER JOIN [MyDB2].[dbo].[MyView] v WITH (NOLOCK)
                        ON t.Id = v.ID
                    WHERE v.ID = @ID --284 
                ), **-- this subquery returns more than 1 value, so I need to insert in the search Audit table as many ParentIDs as it returns**
                @AuditType = 'INSERT'           FROM inserted ins
                        IF @ID IS NOT NULL 
               AND 
               @ParentID IS NOT NULL
               AND 
               @ParentCode IS NOT NULL    

            EXEC [MyDB].[dbo].SP_Auditing] @ID, @Code, @AuditType, @ParentCode, @ParentID 
                END

--  below is the same logic for UPDATE and DELETE actions...

上面的存储过程只是将数据插入Audit表中。

答案

切勿在触发器中使用标量变量,因为插入,更新和删除可能会影响多行。关于你的触发器,尝试这样的事情。

CREATE TRIGGER [dbo].[Tr_MyFavouriteTable_UPD_INSERT_DEL]
    ON [dbo].[MyFavouriteTable] AFTER INSERT, DELETE, UPDATE
        NOT FOR REPLICATION 
AS   

BEGIN
    ;with act as (
    select isnull(i.id,d.id) id, --either deleted or inserted is not null
    case when i.id is not null and d.id is not null then 'update'
         when i.id is not null then 'insert'
         else 'delete' end auditType
    from inserted i full outer join deleted d on i.id = d.id
    ),
    audit_cte as (
    SELECT act.id, 'DOC' Code,
           CAST(RIGHT(parentId,LEN(parentId) - LEN(LEFT(parentId,3))) AS INT) parentid,
           act.auditType, 'parentcode' parentCode
    FROM [MyDB].[dbo].[MyFavouriteTable] t WITH (NOLOCK)
    INNER JOIN [MyDB2].[dbo].[MyView] v WITH (NOLOCK)  ON t.Id = v.ID
    inner join act on act.id = t.id
    )
    insert myDB.dbo.Table1 (ID, Code, AuditType, ParentCode, ParentID)
    select id,code,AuditType, ParentCode, ParentID
    from audit_cte
    where parentCode is not null and parentid is not null
end
另一答案

为什么你需要逐个获取记录?根据我的理解,你想保留日志。

IF EXISTS (SELECT 1 FROM inserted)          AND 
   NOT EXISTS (SELECT 1 FROM deleted) 
            BEGIN 
            INSERT INTO [Your_Log_Table] 
            SELECT
            ins.ID, [Code],'INSERT',[PrentCode],
            (SELECT TOP 1 CAST(RIGHT(parentId,LEN(parentId) - 
                LEN(LEFT(parentId,3))) AS INT) 
                FROM [MyDB].[dbo].[MyFavouriteTable] t WITH (NOLOCK)
                INNER JOIN [MyDB2].[dbo].[MyView] v WITH (NOLOCK)
                    ON t.Id = v.ID
                WHERE v.ID = ins.ID --284 
            )
                FROM inserted ins
            END      
另一答案

见Alex Kudryashev的回答。我需要稍微调整一下他的逻辑来整理具有相同ParentID的重复记录,以便插入到Audit表中。我在Alex的cte_Audit下面添加了一个cte,如下所示

CREATE TRIGGER [dbo].[Tr_MyFavouriteTable_UPD_INSERT_DEL]
    ON [dbo].[MyFavouriteTable] AFTER INSERT, DELETE, UPDATE
        NOT FOR REPLICATION 
AS   

BEGIN
    ;with act as (
    select isnull(i.id,d.id) id, --either deleted or inserted is not null
    case when i.id is not null and d.id is not null then 'update'
         when i.id is not null then 'insert'
         else 'delete' end auditType
    from inserted i full outer join deleted d on i.id = d.id
    ),
    audit_cte as (
    SELECT act.id, 'DOC' Code,
           CAST(RIGHT(parentId,LEN(parentId) - LEN(LEFT(parentId,3))) AS INT) parentid,
           act.auditType, 'parentcode' parentCode
    FROM [MyDB].[dbo].[MyFavouriteTable] t WITH (NOLOCK)
    INNER JOIN [MyDB2].[dbo].[MyView] v WITH (NOLOCK)  ON t.Id = v.ID
    inner join act on act.id = t.id
    )
    insert myDB.dbo.Table1 (ID, Code, AuditType, ParentCode, ParentID)
    select id,code,AuditType, ParentCode, ParentID
    from audit_cte
    where parentCode is not null and parentid is not null
         ,CTE_dupsCleanup AS (
                SELECT DISTINCT
                Code,
                Id, 
                AuditType,
                ParentCode,
                ParentId,

  --   ROW_NUMBER() OVER(PARTITION BY ParentId, ParentCode, AuditType ORDER BY ParentId) AS Rn

        FROM AUDIT_CTE 
                WHERE ParentCode IS NOT NULL 
                    AND ParentId IS NOT NULL )


Then using Rn = 1 inserted only unique records into the Auidt table. Like this: 

                    INSERT [ISSearch].[dbo].[SearchAudit] (Code, ID, AuditType, ParentCode, ParentID)
                SELECT
                Code,
                ID, 
                AuditType,
                ParentCode,
                ParentId
                FROM CTE_dupsCleanup 
                --  WHERE Rn = 1
END

以上是关于触发器上的光标的主要内容,如果未能解决你的问题,请参考以下文章

Javascript事件

当光标隐藏在其他 HTML 元素后面时如何触发 mouseenter 事件?

如何使用光标和循环显示来自 sqlite 的片段的 recyclerview

Android - 使用片段和简单的光标适配器填充列表视图

如何设置 vscode 的代码片段,以便在自动完成后自动触发 vscode 的智能感知?

如何在文本区域标签中的特定光标位置插入选择标签下拉值作为文本片段?