用于捕获审计中更改的列的触发器 table
Trigger for capturing changed columns in audit table
我有 2 个 table,一个主 table 和一个审计 table。
create sequence dbo.users_seq;
create table dbo.users
(
id bigint primary key default(next value for dbo.users_seq),
name varchar(100) not null, --user's full name
user_data nvarchar(max) not null check(isjson(user_data) = 1),
timestamp datetime2 not null default sysdatetime(),
updated_timestamp datetime2 not null default sysdatetime()
);
create sequence dbo.users_audit_seq;
create table dbo.users_audit
(
id bigint primary key default(next value for dbo.users_audit_seq),
users_id bigint not null, --id from `users` table
old nvarchar(max) not null check(isjson(old) = 1), --original row from `users` table
new nvarchar(max) not null check(isjson(new) = 1), --new row from `users` table
query varchar(max) not null, --query used for update
updated_by varchar(100) not null, --username info
timestamp datetime2 not null default sysdatetime()
);
我想在 users
main table 上创建一个 after update
触发器,可用于捕获 users_audit
[=62] 中更改的列(不包括时间戳) =]. (示例如下)
我可以通过 json_modify()
和 OPENJSON(@json
手动执行此操作,但无法通过触发器自动执行此操作
初始插入:
id
name
user_data
timestamp
updated_timestamp
1
John
{"email":"jdoe@abc.com"}
2021-05-08 18:10:02.0474381
2021-05-08 18:10:02.0474381
示例更新:
id
name
user_data
timestamp
updated_timestamp
1
John Doe
{"email":"jdoe@abc.com","address":"123 Main St"}
2021-05-08 18:10:02.0474381
2021-05-08 18:12:06.0474381
经过上述更新审核后 table 应该如下所示:
id
users_id
old
new
query
updated_by
timestamp
1
1
{"name":"John","user_data":{"email":"jdoe@abc.com"}}
{"name":"John Doe","user_data":{"email":"jdoe@abc.com","address":"123 Main St"}}
update query
username
2021-05-08 18:12:06.0474381
示例更新 2:
id
name
user_data
timestamp
updated_timestamp
1
John
{"email":"jdoe@abc.com","address":"123 Main St"}
2021-05-08 18:10:02.0474381
2021-05-08 18:14:16.0474381
在上面的 update2 审计之后 table 应该是这样的:
(old
和 new
没有捕获 user_data 因为它没有改变)
id
users_id
old
new
query
updated_by
timestamp
1
1
{"name":"John","user_data":{"email":"jdoe@abc.com"}}
{"name":"John Doe","user_data":{"email":"jdoe@abc.com","address":"123 Main St"}}
update query
username
2021-05-08 18:12:06.0474381
2
1
{"name":"John Doe"}
{"name":"John"}
update query
username
2021-05-08 18:14:16.0474381
注意:时间 tables 或 SQL 审核 方法将不起作用
这是一种方法。
原理与之前回答中提到的基本相同。主要区别是:
- 使用
dm_exec_input_buffer
获取起始批次。为此,您需要 server-level 权限。
FOR JSON
不会显示具有 NULL
值的键,因此我们可以使用 SELECT...EXCEPT
删除 inserted
和 [= 之间相同的值16=].
JSON_QUERY
是防止现有 JSON 个对象的 double-escaping 所必需的
CREATE OR ALTER TRIGGER TR_users ON users
AFTER UPDATE
AS
SET NOCOUNT ON; -- prevent issues with bad client drivers
IF NOT EXISTS (SELECT 1 FROM inserted) AND NOT EXISTS (SELECT 1 FROM deleted)
RETURN; -- early bail-out
-- needs sa permissions
DECLARE @inputBuf nvarchar(max) /* = (
SELECT b.event_info
FROM sys.dm_exec_input_buffer(@@SPID, NULL) b
);*/
INSERT users_audit (users_id, old, new, query, updated_by)
SELECT
i.id,
(
SELECT
-- SELECT EXCEPT will null this out if they are the same
name = (SELECT i.name EXCEPT SELECT d.name),
user_data = JSON_QUERY((SELECT i.user_data EXCEPT SELECT d.user_data))
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
),
(
SELECT
name = (SELECT d.name EXCEPT SELECT i.name),
user_data = JSON_QUERY((SELECT d.user_data EXCEPT SELECT i.user_data))
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
),
ISNULL(@inputBuf, ''),
SUSER_SNAME()
FROM inserted i
JOIN deleted d ON d.id = i.id -- join to match by all primary key columns
WHERE NOT EXISTS (
SELECT i.name, i.user_data -- add other columns here
INTERSECT -- because INTERSECT deals correctly with nulls
SELECT d.name, d.user_data
);
go
我有 2 个 table,一个主 table 和一个审计 table。
create sequence dbo.users_seq;
create table dbo.users
(
id bigint primary key default(next value for dbo.users_seq),
name varchar(100) not null, --user's full name
user_data nvarchar(max) not null check(isjson(user_data) = 1),
timestamp datetime2 not null default sysdatetime(),
updated_timestamp datetime2 not null default sysdatetime()
);
create sequence dbo.users_audit_seq;
create table dbo.users_audit
(
id bigint primary key default(next value for dbo.users_audit_seq),
users_id bigint not null, --id from `users` table
old nvarchar(max) not null check(isjson(old) = 1), --original row from `users` table
new nvarchar(max) not null check(isjson(new) = 1), --new row from `users` table
query varchar(max) not null, --query used for update
updated_by varchar(100) not null, --username info
timestamp datetime2 not null default sysdatetime()
);
我想在 users
main table 上创建一个 after update
触发器,可用于捕获 users_audit
[=62] 中更改的列(不包括时间戳) =]. (示例如下)
我可以通过 json_modify()
和 OPENJSON(@json
手动执行此操作,但无法通过触发器自动执行此操作
初始插入:
id | name | user_data | timestamp | updated_timestamp |
---|---|---|---|---|
1 | John | {"email":"jdoe@abc.com"} | 2021-05-08 18:10:02.0474381 | 2021-05-08 18:10:02.0474381 |
示例更新:
id | name | user_data | timestamp | updated_timestamp |
---|---|---|---|---|
1 | John Doe | {"email":"jdoe@abc.com","address":"123 Main St"} | 2021-05-08 18:10:02.0474381 | 2021-05-08 18:12:06.0474381 |
经过上述更新审核后 table 应该如下所示:
id | users_id | old | new | query | updated_by | timestamp |
---|---|---|---|---|---|---|
1 | 1 | {"name":"John","user_data":{"email":"jdoe@abc.com"}} | {"name":"John Doe","user_data":{"email":"jdoe@abc.com","address":"123 Main St"}} | update query | username | 2021-05-08 18:12:06.0474381 |
示例更新 2:
id | name | user_data | timestamp | updated_timestamp |
---|---|---|---|---|
1 | John | {"email":"jdoe@abc.com","address":"123 Main St"} | 2021-05-08 18:10:02.0474381 | 2021-05-08 18:14:16.0474381 |
在上面的 update2 审计之后 table 应该是这样的:
(old
和 new
没有捕获 user_data 因为它没有改变)
id | users_id | old | new | query | updated_by | timestamp |
---|---|---|---|---|---|---|
1 | 1 | {"name":"John","user_data":{"email":"jdoe@abc.com"}} | {"name":"John Doe","user_data":{"email":"jdoe@abc.com","address":"123 Main St"}} | update query | username | 2021-05-08 18:12:06.0474381 |
2 | 1 | {"name":"John Doe"} | {"name":"John"} | update query | username | 2021-05-08 18:14:16.0474381 |
注意:时间 tables 或 SQL 审核 方法将不起作用
这是一种方法。
原理与之前回答中提到的基本相同。主要区别是:
- 使用
dm_exec_input_buffer
获取起始批次。为此,您需要 server-level 权限。 FOR JSON
不会显示具有NULL
值的键,因此我们可以使用SELECT...EXCEPT
删除inserted
和 [= 之间相同的值16=].JSON_QUERY
是防止现有 JSON 个对象的 double-escaping 所必需的
CREATE OR ALTER TRIGGER TR_users ON users
AFTER UPDATE
AS
SET NOCOUNT ON; -- prevent issues with bad client drivers
IF NOT EXISTS (SELECT 1 FROM inserted) AND NOT EXISTS (SELECT 1 FROM deleted)
RETURN; -- early bail-out
-- needs sa permissions
DECLARE @inputBuf nvarchar(max) /* = (
SELECT b.event_info
FROM sys.dm_exec_input_buffer(@@SPID, NULL) b
);*/
INSERT users_audit (users_id, old, new, query, updated_by)
SELECT
i.id,
(
SELECT
-- SELECT EXCEPT will null this out if they are the same
name = (SELECT i.name EXCEPT SELECT d.name),
user_data = JSON_QUERY((SELECT i.user_data EXCEPT SELECT d.user_data))
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
),
(
SELECT
name = (SELECT d.name EXCEPT SELECT i.name),
user_data = JSON_QUERY((SELECT d.user_data EXCEPT SELECT i.user_data))
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
),
ISNULL(@inputBuf, ''),
SUSER_SNAME()
FROM inserted i
JOIN deleted d ON d.id = i.id -- join to match by all primary key columns
WHERE NOT EXISTS (
SELECT i.name, i.user_data -- add other columns here
INTERSECT -- because INTERSECT deals correctly with nulls
SELECT d.name, d.user_data
);
go