触发器上的光标
Cursor on a trigger
我正在使用 SQL Server 2008。
我在 table 中定义了插入、更新和删除操作的后触发器。我的问题是目前我的触发器一次插入一条记录,而我需要多条记录作为一条
SELECT TOP 1 @ParentID FROM ... WHERE ID = @ID
returns 多个唯一记录。
(请参阅下面的评论“-- 此子查询 returns 超过 1 个值,因此我需要在搜索中插入与它 returns 一样多的 ParentIDs table” )
我想我需要使用游标,但我不确定在哪里声明和打开游标。
--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...
上面的存储过程只是将数据插入审计 table。
为什么要一条一条的获取记录?据我了解,您想保留日志。
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
切勿在触发器中使用标量变量,因为插入、更新和删除可能会影响多行。至于你的触发器,试试这样的东西。
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
查看 Alex Kudryashev 的回答。我需要稍微调整一下他的逻辑,以将具有相同 ParentID 的重复记录分类出来,以便插入到审计 table 中。我在 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
我正在使用 SQL Server 2008。
我在 table 中定义了插入、更新和删除操作的后触发器。我的问题是目前我的触发器一次插入一条记录,而我需要多条记录作为一条
SELECT TOP 1 @ParentID FROM ... WHERE ID = @ID
returns 多个唯一记录。
(请参阅下面的评论“-- 此子查询 returns 超过 1 个值,因此我需要在搜索中插入与它 returns 一样多的 ParentIDs table” )
我想我需要使用游标,但我不确定在哪里声明和打开游标。
--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...
上面的存储过程只是将数据插入审计 table。
为什么要一条一条的获取记录?据我了解,您想保留日志。
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
切勿在触发器中使用标量变量,因为插入、更新和删除可能会影响多行。至于你的触发器,试试这样的东西。
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
查看 Alex Kudryashev 的回答。我需要稍微调整一下他的逻辑,以将具有相同 ParentID 的重复记录分类出来,以便插入到审计 table 中。我在 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