oc检查表并输出一个字符串,该字符串是正确的CREATETRIGGER语句。。。喜欢代码自动生成。我更喜欢动态sql而不是代码生成。是否也可以检查“错误”更新?像更新t1 set c1=c1-不应该跟踪这些内容…您使用的是什么版本的SQL Server?自
oc检查表并输出一个字符串,该字符串是正确的CREATETRIGGER语句。。。喜欢代码自动生成。我更喜欢动态sql而不是代码生成。是否也可以检查“错误”更新?像更新t1 set c1=c1-不应该跟踪这些内容…您使用的是什么版本的SQL Server?自,sql,sql-server,triggers,sql-server-2012,Sql,Sql Server,Triggers,Sql Server 2012,oc检查表并输出一个字符串,该字符串是正确的CREATETRIGGER语句。。。喜欢代码自动生成。我更喜欢动态sql而不是代码生成。是否也可以检查“错误”更新?像更新t1 set c1=c1-不应该跟踪这些内容…您使用的是什么版本的SQL Server?自SQL 2008以来,我们已经进行了更改数据捕获。此外,在SQL 2016中,我们得到了时态表。这将起作用,但我正在寻找一种更通用的解决方案,在这种解决方案中,我不需要为每个表指定每个触发器中的每一列。我希望有一个更具活力和可恢复性的解决方案。
oc检查表并输出一个字符串,该字符串是正确的CREATETRIGGER语句。。。喜欢代码自动生成。我更喜欢动态sql而不是代码生成。是否也可以检查“错误”更新?像更新t1 set c1=c1-不应该跟踪这些内容…您使用的是什么版本的SQL Server?自SQL 2008以来,我们已经进行了更改数据捕获。此外,在SQL 2016中,我们得到了时态表。这将起作用,但我正在寻找一种更通用的解决方案,在这种解决方案中,我不需要为每个表指定每个触发器中的每一列。我希望有一个更具活力和可恢复性的解决方案。对于交叉应用,仍然+1。@Jaster,关系数据库被设计为具有稳定的模式,其中列是预先知道的,并且不会经常更改。快速搜索“sp_executesql in trigger”会告诉您,不能在引用
插入的
或删除的
表的触发器中运行动态SQL。动态SQL在不同的单独上下文中执行。人们建议将插入的表复制到某个临时表中,并在动态SQL中引用该临时表。我不确定它是否会工作,您仍然需要以某种方式为temp表定义列。我将仔细查看更改数据捕获
功能。看起来需要sql Generarator。。。但是你为什么要把身份证给nvarchar?(该列不需要跟踪)@Jaster,你是对的,包含ID
列没有多大意义,因为它不应该更改。同时,我没有包括Version
列,因为它总是变化,并且它的实际值通常没有有用的(历史)意义。我希望查询的总体轮廓能够清楚地说明如何根据需要添加更多的列。我正在寻找一种更通用的解决方案,可以应用于多个表,而无需指定每一列+1进行正确的空检查并理解版本列。我只能考虑SQLCLR触发器,它可以动态地构建和执行SQL。根据vladimirs的回答,我猜这就是我的方向。。。只需要对所需的转换有更多的了解。@Jaster,将生成DML触发器代码的动态SQL代码放在。因此,您将保证记录数据更改的主DML触发器始终与表模式同步。但是,我不知道是否可以更改
或在DDL触发器中删除/创建
DML触发器。是否可以修改cdc过程中写入的数据。e、 更改用户/时间?您的意思是更改cdc表吗?是的,您可以更改CDC数据。CDC扫描程序读取事务日志并填充CDC表。然后,您可以对这些表执行任何DML命令,但在读取CDC表并填充history tablesSmart解决方案时不需要它,但我不需要动态脚本来生成触发器。我的问题是关于作为触发器一部分的动态sql,这似乎不是一个选项。
CREATE TABLE [dbo].[History](
[Id] [uniqueidentifier] NOT NULL CONSTRAINT [DF_History_Id] DEFAULT (newsequentialid()),
[ObjectId] [uniqueidentifier] NOT NULL,
[Timestamp] [datetime] NOT NULL CONSTRAINT [DF_History_Timestamp] DEFAULT (getdate()),
[ChangingUser] [varchar](max) NOT NULL CONSTRAINT [DF_History_ChangingUser] DEFAULT (suser_sname()),
[Column] [varchar](max) NOT NULL,
[OldValue] [nvarchar](max) NULL,
[NewValue] [nvarchar](max) NULL,
CONSTRAINT [PK_History] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
CREATE TRIGGER [dbo].[T1_TraceUpdate]
on [dbo].[T1]
AFTER update
AS
BEGIN
set nocount on;
-- pseudo insert!
insert into History select * from inserted;
END
CREATE TABLE [dbo].[T1](
[Id] [uniqueidentifier] NOT NULL CONSTRAINT [DF_T1_Id] DEFAULT (newsequentialid()),
[Title] [text] NULL,
[Amount] [int] NULL,
[Price] [decimal](18, 7) NULL,
[Version] [timestamp] NOT NULL,
CONSTRAINT [PK_T1] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
1d118497-bf69-e611-9e7d-40a8f04d1095 Abc 3 37,2500000
9cf095a8-bf69-e611-9e7d-40a8f04d1095 NULL 1 27,3000000
cc38386d-fe69-e611-9e7d-40a8f04d1095 Storm Catcher 10 NULL
update T1 set price = isnull(Price,100)*0.7 where Amount > 2
4848D80B-4E73-E611-BD43-40A8F04D1095 1D118497-BF69-E611-9E7D-40A8F04D1095 2016-09-05 11:49:33.473 sa Price 37,2500000 26.0750000
E80EAB18-4E73-E611-BD43-40A8F04D1095 CC38386D-FE69-E611-9E7D-40A8F04D1095 2016-09-05 11:49:33.473 sa Price NULL 70
CREATE TRIGGER [dbo].[T1_TraceUpdate]
on [dbo].[T1]
AFTER update
AS
BEGIN
set nocount on;
WITH
CTE_Inserted
AS
(
SELECT ID, ColumnName, ColumnValue
FROM
inserted
CROSS APPLY
(VALUES
('Title', Title),
('Amount', CAST(Amount AS nvarchar(max))),
('Price', CAST(Price AS nvarchar(max)))
) AS V(ColumnName, ColumnValue)
)
,CTE_Deleted
AS
(
SELECT ID, ColumnName, ColumnValue
FROM
deleted
CROSS APPLY
(VALUES
('Title', Title),
('Amount', CAST(Amount AS nvarchar(max))),
('Price', CAST(Price AS nvarchar(max)))
) AS V(ColumnName, ColumnValue)
)
INSERT INTO dbo.History([ObjectId], [Column], [OldValue], [NewValue])
SELECT
CTE_Inserted.Id AS [ObjectId]
,CTE_Inserted.ColumnName
,CTE_Deleted.ColumnValue AS [OldValue]
,CTE_Inserted.ColumnValue AS [NewValue]
FROM
CTE_Inserted
INNER JOIN CTE_Deleted
ON CTE_Deleted.Id = CTE_Inserted.Id
AND CTE_Deleted.ColumnName = CTE_Inserted.ColumnName
WHERE
ISNULL(CTE_Inserted.ColumnValue, N'') <> ISNULL(CTE_Deleted.ColumnValue, N'')
OR (CTE_Inserted.ColumnValue IS NULL AND CTE_Deleted.ColumnValue IS NOT NULL)
OR (CTE_Inserted.ColumnValue IS NOT NULL AND CTE_Deleted.ColumnValue IS NULL)
;
END
CREATE TRIGGER [dbo].[T1_TraceUpdate]
on [dbo].[T1]
AFTER update
AS
BEGIN
set nocount on;
-- Changing Id breaks the logic of the trigger
IF UPDATE(ID)
BEGIN
RAISERROR ('ID cannot change.', 16, 1);
--
END
INSERT INTO dbo.History(ObjectId, [Column], OldValue, NewValue)
SELECT inserted.ID, ColumnName, OldValue, NewValue
FROM inserted
INNER JOIN Deleted ON Deleted.Id = Inserted.Id
CROSS APPLY
(SELECT ColumnName='Title', OldValue=deleted.Title, NewValue=inserted.Title
WHERE isnull(nullif(inserted.Title,deleted.Title), nullif(deleted.Title,inserted.Title)) IS NOT NULL
UNION ALL
SELECT 'Amount', CAST(deleted.Amount AS nvarchar(max)), CAST(inserted.Amount AS nvarchar(max))
WHERE isnull(nullif(inserted.Amount,deleted.Amount), nullif(deleted.Amount,inserted.Amount)) IS NOT NULL
UNION ALL
SELECT 'Price', CAST(deleted.Price AS nvarchar(max)), CAST(inserted.Price AS nvarchar(max))
WHERE isnull(nullif(inserted.Price, deleted.Price), nullif(deleted.Price,inserted.Price)) IS NOT NULL
UNION ALL
-- you may want to skip version (`timestamp` type) as it's always updated by server
SELECT 'Version', CONVERT(varchar(max),CONVERT(VARBINARY,deleted.Version),1), CONVERT(varchar(max),CONVERT(VARBINARY,inserted.Version),1)
WHERE isnull(nullif(inserted.Version, deleted.Version), nullif(deleted.Version,inserted.Version)) IS NOT NULL
) t;
END;
IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME= 'History')
CREATE TABLE [dbo].[History](
[Id] [uniqueidentifier] NOT NULL CONSTRAINT [DF_History_Id] DEFAULT (newsequentialid()),
[Tablename] VARCHAR(150),
[ObjectId] VARCHAR(1500) NOT NULL,
[Timestamp] [datetime] NOT NULL CONSTRAINT [DF_History_Timestamp] DEFAULT (getdate()),
[ChangingUser] [varchar](max) NOT NULL CONSTRAINT [DF_History_ChangingUser] DEFAULT (suser_sname()),
[Column] [varchar](max) NOT NULL,
[OldValue] [nvarchar](max) NULL,
[NewValue] [nvarchar](max) NULL,
[ModifiedDate] Datetime NULL
CONSTRAINT [PK_History] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
IF NOT EXISTS(SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME= 'T1')
BEGIN
CREATE TABLE [dbo].[T1](
[Id] [uniqueidentifier] NOT NULL CONSTRAINT [DF_T1_Id] DEFAULT (newsequentialid()),
[Title] text NULL,
[Amount] [int] NULL,
[Price] [decimal](18, 7) NULL,
[Version] [datetime] NOT NULL,
CONSTRAINT [PK_T1] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
INSERT INTO [T1] ([Title],[Amount],[Price],[Version]) VALUES ('Abc',3,372500000 ,'2016-09-01 11:49:33.473'),
(NULL,1,273000000 ,'2016-09-02 11:49:33.473'),
('Storm Catcher',10,NULL,'2016-09-01 11:49:33.473')
END
IF OBJECT_ID('tempdb..#FiltedTableList') IS NOT NULL
DROP TABLE #FiltedTableList
GO
CREATE TABLE #FiltedTableList
( ID INT IDENTITY(1,1),
TableName VARCHAR(150))
INSERT INTO #FiltedTableList (TableName)
SELECT 'T1' --here we have only one table to track the changes
DECLARE @TableName sysname,@Counter INT,@Dynamsql varchar(MAX)
SELECT @Counter=MAX(ID) FROM #FiltedTableList
SET NOCOUNT ON
WHILE (@Counter !=0)
BEGIN
SELECT @TableName= TableName
FROM #FiltedTableList
WHERE ID=@Counter
DECLARE @ColumnFilter VARCHAR (MAX)
SELECT @ColumnFilter=Stuff(( Select ', ' + C.COLUMN_NAME
From INFORMATION_SCHEMA.COLUMNS As C Where C.TABLE_NAME = @TableName
AND c.DATA_TYPE NOT IN ('text','ntext','image')
Order By C.ORDINAL_POSITION For Xml Path('') ), 1, 2, '')
IF OBJECT_ID('tempdb..##MagInserted') IS NOT NULL
DROP TABLE ##MagInserted
IF OBJECT_ID('tempdb..##MagDeleted') IS NOT NULL
DROP TABLE ##MagDeleted
EXEC('IF OBJECT_ID (''' + @TableName+ '_LogTracker'', ''TR'') IS NOT NULL DROP TRIGGER ' + @TableName+ '_LogTracker')
SELECT @Dynamsql =
'CREATE TRIGGER ' + @TableName+ '_LogTracker ON ' + @TableName+ ' FOR INSERT, UPDATE, DELETE
AS
DECLARE @column int , @maxColumn int , @char int,@columnname varchar(128) , @TableName varchar(128) , @KeyColumn varchar(1000) , @Dynamsql varchar(2000) ,@dataquery VARCHAR(MAX),
@ModifiedDate varchar(21), @UserName varchar(128) , @key int , @Type char(1) , @PKFieldSelect varchar(1000),@PKValueSelect varchar(1000)
SELECT @TableName = ''' + @TableName+ ''';
SELECT @UserName = system_user , @ModifiedDate = convert(varchar(8), getdate(), 112) + '' '' + convert(varchar(12), getdate(), 114);
SELECT '+ @ColumnFilter+' INTO ##MagInserted FROM inserted;SELECT '+ @ColumnFilter+' INTO ##MagDeleted FROM deleted;
SELECT @KeyColumn = COALESCE(@KeyColumn + '' and'', '' on'') + '' i.'' + c.COLUMN_NAME + '' = d.'' + c.COLUMN_NAME
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS kc JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE c ON c.TABLE_NAME = kc.TABLE_NAME and c.CONSTRAINT_NAME = kc.CONSTRAINT_NAME
WHERE kc.TABLE_NAME = @TableName AND CONSTRAINT_TYPE = ''PRIMARY KEY''
SELECT @PKFieldSelect = COALESCE(@PKFieldSelect+''+'','''') + '''''''' + COLUMN_NAME + ''''''''
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS kc JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE c ON c.TABLE_NAME = kc.TABLE_NAME and c.CONSTRAINT_NAME = kc.CONSTRAINT_NAME
WHERE kc.TABLE_NAME = @TableName AND CONSTRAINT_TYPE = ''PRIMARY KEY''
SELECT @PKValueSelect = coalesce(@PKValueSelect+''+'','''') + ''convert(varchar(100), coalesce(i.'' + COLUMN_NAME + '',d.'' + COLUMN_NAME + ''))''
FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS kc JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE c ON c.TABLE_NAME = kc.TABLE_NAME and c.CONSTRAINT_NAME = kc.CONSTRAINT_NAME
WHERE kc.TABLE_NAME = @TableName AND CONSTRAINT_TYPE = ''PRIMARY KEY''
SELECT @column = 0 , @maxColumn = max(ORDINAL_POSITION) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @TableName
WHILE @column < @maxColumn
BEGIN
SELECT @column = min(ORDINAL_POSITION) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = @TableName AND ORDINAL_POSITION > @column
SELECT @key = (@column - 1 )% 8 + 1; SELECT @key = power(2,@key - 1);SELECT @char = ((@column - 1) / 8) + 1
IF SUBSTRING(COLUMNS_UPDATED(),@char, 1) & @key > 0 OR @Type IN (''I'',''D'')
BEGIN
SELECT @columnname = COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = @TableName and ORDINAL_POSITION = @column
SELECT @Dynamsql = ''insert History (TableName,[ObjectId], [Column], OldValue, NewValue, ModifiedDate, ChangingUser)''
SELECT @Dynamsql = @Dynamsql + '' select '''''' + @TableName + ''''''''
SELECT @Dynamsql = @Dynamsql + '','' + @PKValueSelect
SELECT @Dynamsql = @Dynamsql + '','''''' + @columnname + ''''''''
SELECT @Dynamsql = @Dynamsql + '',convert(varchar(1000),d.'' + @columnname + '')''
SELECT @Dynamsql = @Dynamsql + '',convert(varchar(1000),i.'' + @columnname + '')''
SELECT @Dynamsql = @Dynamsql + '','''''' + @ModifiedDate + ''''''''
SELECT @Dynamsql = @Dynamsql + '','''''' + @UserName + ''''''''
SELECT @Dynamsql = @Dynamsql + '' from ##MagInserted i full outer join ##MagDeleted d''
SELECT @Dynamsql = @Dynamsql + @KeyColumn
SELECT @Dynamsql = @Dynamsql + '' where i.'' + @columnname + '' <> d.'' + @columnname
SELECT @Dynamsql = @Dynamsql + '' or (i.'' + @columnname + '' is null and d.'' + @columnname + '' is not null)''
SELECT @Dynamsql = @Dynamsql + '' or (i.'' + @columnname + '' is not null and d.'' + @columnname + '' is null)''
EXEC (@Dynamsql)
END END '
SELECT @Dynamsql
EXEC(@Dynamsql)
SET @Counter=@Counter-1
END