使用 CDC 或带 CDC 的 T-SQL 跟踪字段更改
Tracking changes for field using CDC or T-SQL with CDC
以下 CDC 结果集用于更新操作。仅 area
字段已更新。
在之前的截图中只有 table 中包含的部分 gields。字段更多。他们不时更新一些内容。在以下查询中,我尝试在可用视图中按字段显示更改统计信息。
with History AS (
SELECT
cz.GUID as Id,
cz.category,
isnull(cz.area, 0) as area,
isnull(cz.oilwidthmin,0) as oilwidthmin,
isnull(cz.oilwidthmax,0) as oilwidthmax,
isnull(cz.efectivwidthmin,0) as efectivwidthmin,
isnull(cz.efectivwidthmax,0) as efectivwidthmax,
isnull(cz.koafporistmin,0) as koafporistmin,
isnull(cz.koafporistmax,0) as koafporistmax,
CASE cz.__$operation
WHEN 1 THEN 'DELETE'
WHEN 2 THEN 'INSERT'
WHEN 3 THEN 'Before UPDATE'
WHEN 4 THEN 'After UPDATE'
END operation,
map.tran_begin_time as beginT,
map.tran_end_time as endT
FROM cdc.fn_cdc_get_all_changes_dbo_EXT_GeolObject_KategZalezh(sys.fn_cdc_get_min_lsn('dbo_EXT_GeolObject_KategZalezh'), sys.fn_cdc_get_max_lsn(), 'all') AS cz
INNER JOIN [cdc].[lsn_time_mapping] map
ON cz.[__$start_lsn] = map.start_lsn
)
SELECT field, val, operation, beginT, endT FROM History
unpivot ( [val] for field in
(
--category,
area,
oilwidthmin,
oilwidthmax,
efectivwidthmin,
efectivwidthmax,
koafporistmin,
koafporistmax))t where id = '2D166098-7CBD-4622-9EB0-000070506FE6'
查询结果如下:
但是之前的结果包含额外的数据。
预期结果必须如下:
我知道 CDC 按行跟踪变化。或者也许我错了?如果不是,我如何为 SQL 中的 val
字段做一些比较?我对 t-sql 没有很深的了解,我想到的一切都以某种方式被游标使用。有任何想法吗?
也许以某种方式使用 CT(更改跟踪)?也许以某种方式使用 group by
?
几乎正确答案。以下查询 return 预期结果:
WITH History AS (
SELECT
*,
CASE cz.__$operation
WHEN 1 THEN 'DELETE'
WHEN 2 THEN 'INSERT'
WHEN 3 THEN 'Before UPDATE'
WHEN 4 THEN 'After UPDATE'
END operation,
map.tran_begin_time as beginT,
map.tran_end_time as endT
FROM cdc.fn_cdc_get_all_changes_dbo_EXT_GeolObject_KategZalezh(sys.fn_cdc_get_min_lsn('dbo_EXT_GeolObject_KategZalezh'), sys.fn_cdc_get_max_lsn(), 'all') AS cz
INNER JOIN [cdc].[lsn_time_mapping] map
ON cz.[__$start_lsn] = map.start_lsn
where cz.GUID = '2D166098-7CBD-4622-9EB0-000070506FE6'
),
UnpivotedValues AS(
SELECT guid, field, val, operation, beginT, endT
FROM History
UNPIVOT ( [val] FOR field IN
(
area,
oilwidthmin,
oilwidthmax,
efectivwidthmin,
efectivwidthmax,
koafporistmin,
koafporistmax
))t
),
UnpivotedWithLastValue AS (
SELECT
*,
--Use LAG() to get the last value for the same field
LAG(val, 1) OVER (PARTITION BY field ORDER BY BeginT) LastVal
FROM UnpivotedValues
)
--Filter out record where the value equals the last value for the same field
SELECT * FROM UnpivotedWithLastValue WHERE val <> LastVal OR LastVal IS NULL ORDER BY guid
此查询的结果如下所示:
但是当 WHERE cz.GUID =
不存在或者如果在 WHERE
谓词中使用多个 GUID 进行查询时,我得到以下结果:
这是两个 GUID 的结果。第一行中 LastVal
的值必须为 16691。例如第 4 行中的 val
。
在您的情况下,您可以使用 ROW_NUMBER 函数对更改进行顺序编号 - 在此之后,您可以将每个顺序更改与前一个(基于字段和 ID)连接起来,并仅输出行具有不同的值。
像这样:
WITH
History AS
(
SELECT
cz.GUID as Id,
cz.category,
isnull(cz.area, 0) as area,
isnull(cz.oilwidthmin,0) as oilwidthmin,
isnull(cz.oilwidthmax,0) as oilwidthmax,
isnull(cz.efectivwidthmin,0) as efectivwidthmin,
isnull(cz.efectivwidthmax,0) as efectivwidthmax,
isnull(cz.koafporistmin,0) as koafporistmin,
isnull(cz.koafporistmax,0) as koafporistmax,
CASE
cz.__$operation
WHEN 1 THEN 'DELETE'
WHEN 2 THEN 'INSERT'
WHEN 3 THEN 'Before UPDATE'
WHEN 4 THEN 'After UPDATE'
END operation,
map.tran_begin_time as beginT,
map.tran_end_time as endT,
ROW_NUMBER() OVER (PARTITION BY cz.GUID ORDER BY map.tran_end_time ASC) as rn
FROM
cdc.fn_cdc_get_all_changes_dbo_EXT_GeolObject_KategZalezh(sys.fn_cdc_get_min_lsn('dbo_EXT_GeolObject_KategZalezh'), sys.fn_cdc_get_max_lsn(), 'all') AS cz
INNER JOIN
[cdc].[lsn_time_mapping] map ON cz.[__$start_lsn] = map.start_lsn
),
History2 AS
(
SELECT id, field, val, operation, beginT, endT, rn FROM History
unpivot ( [val] for field in
(
--category,
area,
oilwidthmin,
oilwidthmax,
efectivwidthmin,
efectivwidthmax,
koafporistmin,
koafporistmax))t
where id = '2D166098-7CBD-4622-9EB0-000070506FE6'
)
-- return the values that were inserted first
SELECT
a.*
FROM
History2 a
WHERE
a.rn=1
UNION ALL
-- ... and then return only the values that are different from the previous ones
SELECT
a.*
FROM
History2 a
INNER JOIN
History2 b ON a.id = b.id AND a.field=b.field AND a.rn = b.rn-1 AND a.value<>b.value
WHERE
a.rn>1
顺便说一句;您还可以将 CDC 配置为仅跟踪特定列中的更改,而不是整个 table 中的更改。查看sys.sp_cdc_enable_table存储过程的@captured_column_list
您不能将 CDC 设置为仅跟踪已更改列的值。但是,您可以很容易地过滤掉查询中未更改的值。
考虑以下查询,它是原始查询的简化副本:
WITH History AS (
SELECT
*,
CASE cz.__$operation
WHEN 1 THEN 'DELETE'
WHEN 2 THEN 'INSERT'
WHEN 3 THEN 'Before UPDATE'
WHEN 4 THEN 'After UPDATE'
END operation,
map.tran_begin_time as beginT,
map.tran_end_time as endT
FROM cdc.fn_cdc_get_all_changes_Dbo_YourTable(sys.fn_cdc_get_min_lsn('Dbo_YourTable'), sys.fn_cdc_get_max_lsn(), 'all') AS cz
INNER JOIN [cdc].[lsn_time_mapping] map
ON cz.[__$start_lsn] = map.start_lsn
),
UnpivotedValues AS(
SELECT id, field, val, operation, beginT, endT, t.tran_id
FROM History
UNPIVOT ( [val] FOR field IN
(Column1, Column2, Column3))t
),
UnpivotedWithLastValue AS (
SELECT
*,
--Use LAG() to get the last value for the same field
LAG(val, 1) OVER (PARTITION BY id, field ORDER BY BeginT) LastVal
FROM UnpivotedValues
)
--Filter out record where the value equals the last value for the same field
SELECT * FROM UnpivotedWithLastValue WHERE val <> LastVal OR LastVal IS NULL
ORDER BY Id, beginT
在此查询中,我使用了 LAG() 函数来获取每个字段的最后一个值。根据此值,您可以在最终查询中过滤掉未更改的记录,如上所示。
以下 CDC 结果集用于更新操作。仅 area
字段已更新。
在之前的截图中只有 table 中包含的部分 gields。字段更多。他们不时更新一些内容。在以下查询中,我尝试在可用视图中按字段显示更改统计信息。
with History AS (
SELECT
cz.GUID as Id,
cz.category,
isnull(cz.area, 0) as area,
isnull(cz.oilwidthmin,0) as oilwidthmin,
isnull(cz.oilwidthmax,0) as oilwidthmax,
isnull(cz.efectivwidthmin,0) as efectivwidthmin,
isnull(cz.efectivwidthmax,0) as efectivwidthmax,
isnull(cz.koafporistmin,0) as koafporistmin,
isnull(cz.koafporistmax,0) as koafporistmax,
CASE cz.__$operation
WHEN 1 THEN 'DELETE'
WHEN 2 THEN 'INSERT'
WHEN 3 THEN 'Before UPDATE'
WHEN 4 THEN 'After UPDATE'
END operation,
map.tran_begin_time as beginT,
map.tran_end_time as endT
FROM cdc.fn_cdc_get_all_changes_dbo_EXT_GeolObject_KategZalezh(sys.fn_cdc_get_min_lsn('dbo_EXT_GeolObject_KategZalezh'), sys.fn_cdc_get_max_lsn(), 'all') AS cz
INNER JOIN [cdc].[lsn_time_mapping] map
ON cz.[__$start_lsn] = map.start_lsn
)
SELECT field, val, operation, beginT, endT FROM History
unpivot ( [val] for field in
(
--category,
area,
oilwidthmin,
oilwidthmax,
efectivwidthmin,
efectivwidthmax,
koafporistmin,
koafporistmax))t where id = '2D166098-7CBD-4622-9EB0-000070506FE6'
查询结果如下:
但是之前的结果包含额外的数据。
预期结果必须如下:
我知道 CDC 按行跟踪变化。或者也许我错了?如果不是,我如何为 SQL 中的 val
字段做一些比较?我对 t-sql 没有很深的了解,我想到的一切都以某种方式被游标使用。有任何想法吗?
也许以某种方式使用 CT(更改跟踪)?也许以某种方式使用 group by
?
几乎正确答案。以下查询 return 预期结果:
WITH History AS (
SELECT
*,
CASE cz.__$operation
WHEN 1 THEN 'DELETE'
WHEN 2 THEN 'INSERT'
WHEN 3 THEN 'Before UPDATE'
WHEN 4 THEN 'After UPDATE'
END operation,
map.tran_begin_time as beginT,
map.tran_end_time as endT
FROM cdc.fn_cdc_get_all_changes_dbo_EXT_GeolObject_KategZalezh(sys.fn_cdc_get_min_lsn('dbo_EXT_GeolObject_KategZalezh'), sys.fn_cdc_get_max_lsn(), 'all') AS cz
INNER JOIN [cdc].[lsn_time_mapping] map
ON cz.[__$start_lsn] = map.start_lsn
where cz.GUID = '2D166098-7CBD-4622-9EB0-000070506FE6'
),
UnpivotedValues AS(
SELECT guid, field, val, operation, beginT, endT
FROM History
UNPIVOT ( [val] FOR field IN
(
area,
oilwidthmin,
oilwidthmax,
efectivwidthmin,
efectivwidthmax,
koafporistmin,
koafporistmax
))t
),
UnpivotedWithLastValue AS (
SELECT
*,
--Use LAG() to get the last value for the same field
LAG(val, 1) OVER (PARTITION BY field ORDER BY BeginT) LastVal
FROM UnpivotedValues
)
--Filter out record where the value equals the last value for the same field
SELECT * FROM UnpivotedWithLastValue WHERE val <> LastVal OR LastVal IS NULL ORDER BY guid
此查询的结果如下所示:
但是当 WHERE cz.GUID =
不存在或者如果在 WHERE
谓词中使用多个 GUID 进行查询时,我得到以下结果:
LastVal
的值必须为 16691。例如第 4 行中的 val
。
在您的情况下,您可以使用 ROW_NUMBER 函数对更改进行顺序编号 - 在此之后,您可以将每个顺序更改与前一个(基于字段和 ID)连接起来,并仅输出行具有不同的值。
像这样:
WITH
History AS
(
SELECT
cz.GUID as Id,
cz.category,
isnull(cz.area, 0) as area,
isnull(cz.oilwidthmin,0) as oilwidthmin,
isnull(cz.oilwidthmax,0) as oilwidthmax,
isnull(cz.efectivwidthmin,0) as efectivwidthmin,
isnull(cz.efectivwidthmax,0) as efectivwidthmax,
isnull(cz.koafporistmin,0) as koafporistmin,
isnull(cz.koafporistmax,0) as koafporistmax,
CASE
cz.__$operation
WHEN 1 THEN 'DELETE'
WHEN 2 THEN 'INSERT'
WHEN 3 THEN 'Before UPDATE'
WHEN 4 THEN 'After UPDATE'
END operation,
map.tran_begin_time as beginT,
map.tran_end_time as endT,
ROW_NUMBER() OVER (PARTITION BY cz.GUID ORDER BY map.tran_end_time ASC) as rn
FROM
cdc.fn_cdc_get_all_changes_dbo_EXT_GeolObject_KategZalezh(sys.fn_cdc_get_min_lsn('dbo_EXT_GeolObject_KategZalezh'), sys.fn_cdc_get_max_lsn(), 'all') AS cz
INNER JOIN
[cdc].[lsn_time_mapping] map ON cz.[__$start_lsn] = map.start_lsn
),
History2 AS
(
SELECT id, field, val, operation, beginT, endT, rn FROM History
unpivot ( [val] for field in
(
--category,
area,
oilwidthmin,
oilwidthmax,
efectivwidthmin,
efectivwidthmax,
koafporistmin,
koafporistmax))t
where id = '2D166098-7CBD-4622-9EB0-000070506FE6'
)
-- return the values that were inserted first
SELECT
a.*
FROM
History2 a
WHERE
a.rn=1
UNION ALL
-- ... and then return only the values that are different from the previous ones
SELECT
a.*
FROM
History2 a
INNER JOIN
History2 b ON a.id = b.id AND a.field=b.field AND a.rn = b.rn-1 AND a.value<>b.value
WHERE
a.rn>1
顺便说一句;您还可以将 CDC 配置为仅跟踪特定列中的更改,而不是整个 table 中的更改。查看sys.sp_cdc_enable_table存储过程的@captured_column_list
您不能将 CDC 设置为仅跟踪已更改列的值。但是,您可以很容易地过滤掉查询中未更改的值。
考虑以下查询,它是原始查询的简化副本:
WITH History AS (
SELECT
*,
CASE cz.__$operation
WHEN 1 THEN 'DELETE'
WHEN 2 THEN 'INSERT'
WHEN 3 THEN 'Before UPDATE'
WHEN 4 THEN 'After UPDATE'
END operation,
map.tran_begin_time as beginT,
map.tran_end_time as endT
FROM cdc.fn_cdc_get_all_changes_Dbo_YourTable(sys.fn_cdc_get_min_lsn('Dbo_YourTable'), sys.fn_cdc_get_max_lsn(), 'all') AS cz
INNER JOIN [cdc].[lsn_time_mapping] map
ON cz.[__$start_lsn] = map.start_lsn
),
UnpivotedValues AS(
SELECT id, field, val, operation, beginT, endT, t.tran_id
FROM History
UNPIVOT ( [val] FOR field IN
(Column1, Column2, Column3))t
),
UnpivotedWithLastValue AS (
SELECT
*,
--Use LAG() to get the last value for the same field
LAG(val, 1) OVER (PARTITION BY id, field ORDER BY BeginT) LastVal
FROM UnpivotedValues
)
--Filter out record where the value equals the last value for the same field
SELECT * FROM UnpivotedWithLastValue WHERE val <> LastVal OR LastVal IS NULL
ORDER BY Id, beginT
在此查询中,我使用了 LAG() 函数来获取每个字段的最后一个值。根据此值,您可以在最终查询中过滤掉未更改的记录,如上所示。