动机:
我希望有一个触发器可以在更新或插入任何表的任何文本字段时执行某些操作。
代码不得硬编码任何表知识。即它应该适用于任意表。
到目前为止我在哪里
CREATE_TABLE
上的DDL触发器,为新表添加了FOR INSERT,UPDATE
DML触发器_ColumnMask
表将基于1的整数映射到与COLUMNS_UPDATED
格式兼容的相应VARBINARY(MAX)掩码。拥有_ColumnMask
表和一些aaaCulture
表,我可以编写以下查询:
;WITH aux AS (
SELECT (MAX(column_id) + 7) / 8 mask_len
FROM sys.columns
WHERE object_id = OBJECT_ID('aaaCulture')
)
SELECT c.name, column_id, CASE WHEN pad.zero IS NULL THEN cm.mask ELSE cm.mask + pad.zero END mask
FROM sys.columns c
JOIN aux ON 1 = 1
JOIN sys.types t ON t.user_type_id = c.user_type_id
JOIN _ColumnMask cm ON cm.n = c.column_id
LEFT JOIN _ColumnMask pad ON pad.n = aux.mask_len - (c.column_id + 7) / 8
WHERE object_id = OBJECT_ID('aaaCulture') AND (t.name LIKE '%char' OR t.name LIKE '%text')
ORDER BY c.column_id
屈服
name column_id mask
Text1 2 0x020000000000
Text2 4 0x080000000000
Text3 6 0x200000000000
Text4 8 0x800000000000
Text5 10 0x000200000000
Text7 14 0x002000000000
Text8 16 0x008000000000
Text9 18 0x000002000000
Text10 20 0x000008000000
Text11 21 0x000010000000
Text21 24 0x000080000000
Text22 26 0x000000020000
Text23 28 0x000000080000
Text24 30 0x000000200000
Text25 32 0x000000800000
Text26 34 0x000000000200
Text27 36 0x000000000800
Text28 38 0x000000002000
Text29 40 0x000000008000
XRefCode 42 0x000000000002
Text12 44 0x000000000008
例如,假设我更新字段Text9
和Text23
:
UPDATE dbo.aaaCulture SET Text9 = 'haha', Text23 = 'xoxo'
DML触发器报告的相应COLUMNS_UPDATED
值为:
0x000002080000
实际上,根据上述查询结果:
Text9
的掩码为0x000002000000
Text23
的掩码为0x000000080000
在两个掩码之间执行按位OR会产生0x000002080000
因此,我似乎拥有拼图的所有部分,以便能够识别COLUMNS_UPDATED()
包含任何文本列。除此之外,我不知道怎么做有效(我想可以写一些丑陋的WHILE循环)。
有什么想法吗?
编辑1
我们通过向某个sql脚本(在版本控制下)添加数据库升级步骤来更新数据库。我的目标是阻止开发人员检查更新或插入任何xyzCulture表中任何文本字段的任何数据库步骤,包括那些新数据库步骤创建的那些。我们不讨论为什么我们首先要有xyzCulture表。有叹息,这是给予的。
我可能会采取措施阻止对这些表格中的任何字段进行更新,但首先我要真正理解更具体的更难。
答案 0 :(得分:0)
Helas,除了以下实现之外,我无法想出任何其他内容:
CREATE FUNCTION VarBinaryBitwiseOR(@x VARBINARY(MAX), @y VARBINARY(MAX))
RETURNS VARBINARY(MAX)
AS
BEGIN
DECLARE @pos INT = 0
DECLARE @res VARBINARY(MAX)
DECLARE @tmp VARBINARY(MAX)
WHILE @pos + 8 <= DATALENGTH(@x)
BEGIN
SET @tmp = CAST(CAST(SUBSTRING(@x,@pos + 1,8) AS BIGINT) | CAST(SUBSTRING(@y,@pos + 1,8) AS BIGINT) AS varbinary(MAX))
SET @res = ISNULL(@res + @tmp, @tmp)
SET @pos = @pos + 8
END
IF @pos + 4 <= DATALENGTH(@x)
BEGIN
SET @tmp = CAST(CAST(SUBSTRING(@x,@pos + 1,4) AS INT) | CAST(SUBSTRING(@y,@pos + 1,4) AS INT) AS varbinary(MAX))
SET @res = ISNULL(@res + @tmp, @tmp)
SET @pos = @pos + 4
END
IF @pos + 2 <= DATALENGTH(@x)
BEGIN
SET @tmp = CAST(CAST(SUBSTRING(@x,@pos + 1,2) AS SMALLINT) | CAST(SUBSTRING(@y,@pos + 1,2) AS SMALLINT) AS varbinary(MAX))
SET @res = ISNULL(@res + @tmp, @tmp)
SET @pos = @pos + 2
END
IF @pos + 1 <= DATALENGTH(@x)
BEGIN
SET @tmp = CAST(CAST(SUBSTRING(@x,@pos + 1,1) AS TINYINT) | CAST(SUBSTRING(@y,@pos + 1,1) AS TINYINT) AS varbinary(MAX))
SET @res = ISNULL(@res + @tmp, @tmp)
SET @pos = @pos + 1
END
RETURN @res
END
仅当两个参数的DATALENGTH
相同时才有效,这完全是我的情况。
因此,它既缓慢又不通用(datalength
必须相同)。