汇总后如何从审计 table 中分离出变更类型?

How can I split out change types from an audit table when they have been rolled up?

我正在尝试基于审计 table 构建查询,以便可以将对数据的更改归类为插入、修改或删除。

基本上我有一个请求 tables 存储每个请求的两个属性的旧值和新值:

请求ID Value1Old Value1New Value2Old Value2New
1 50
2 50 100 100
3 100 200 100 300
4 300 500
5 200 0 600 1000
6 1000 0

我正在寻找基于此生成的报告,拆分出每个请求的更改类别以及旧值和新值。从上面的 table 可以看出,对于每个请求,都可以有更改类别的组合,例如插入 Value1 和修改 Value2;或 Value1 和 Value2 的修改。

对于请求中的每个更改类别,都会有一个单独的行,即在上面的数据中,请求 2 和 5 有多个更改类别,因此被分开:

请求ID 改变 Value1Old Value1New Value2Old Value2New
1 插入 50
2 插入 100
2 修改 50 100
3 修改 100 200 100 300
4 修改 300 500
5 修改 600 1000
5 删除 200 0
6 删除 1000 0

我已经能够通过冗长的查询获得我想要的结果,但是一定有更好的方法吗?它的可扩展性不是很好,最终我希望在大约 15 个值上执行此操作。

SELECT
  *
FROM
  (SELECT
    RequestID,
    UpdateTypes.[Name],
    CASE
         WHEN UpdateTypes.[Name] = 'Insert' THEN NULL
         WHEN UpdateTypes.[Name] = 'Delete' AND Value1New = 0 THEN Value1Old
         WHEN UpdateTypes.[Name] = 'Amend' AND Value1New <> 0 THEN Value1Old
         ELSE NULL
    END Value1Old,
    CASE
         WHEN UpdateTypes.[Name] = 'Insert' AND Value1Old IS NULL THEN Value1New
         WHEN UpdateTypes.[Name] = 'Delete' AND Value1New = 0 THEN 0
         WHEN UpdateTypes.[Name] = 'Amend' AND Value1Old <> 0 AND Value1New <> 0 THEN Value1New
         ELSE NULL
    END Value1New,
    CASE
         WHEN UpdateTypes.[Name] = 'Insert' THEN NULL
         WHEN UpdateTypes.[Name] = 'Delete' AND Value2New = 0 THEN Value2Old
         WHEN UpdateTypes.[Name] = 'Amend' AND Value2Old <> 0 AND Value2New <> 0 THEN Value2Old
         ELSE NULL
    END Value2Old,
    CASE
         WHEN UpdateTypes.[Name] = 'Insert' AND Value2Old IS NULL THEN Value2New
         WHEN UpdateTypes.[Name] = 'Delete' AND Value2New = 0 THEN 0
         WHEN UpdateTypes.[Name] = 'Amend' AND Value2Old <> 0 AND Value2New <> 0 THEN Value2New
         ELSE NULL
    END Value2New
  FROM
    Requests,
    (SELECT 'Insert' [Name] UNION SELECT 'Amend' UNION SELECT 'Delete') AS UpdateTypes)  Updates
WHERE
    ((Updates.[Name] = 'Insert')
AND  ((Value1Old IS NULL AND Value1New IS NOT NULL)
OR    (Value2Old IS NULL AND Value2New IS NOT NULL)))
OR  ((Updates.[Name] = 'Delete')
AND  ((Value1Old IS NOT NULL AND Value1New = 0)
OR    (Value2Old IS NOT NULL AND Value2New = 0)))
OR  ((Updates.[Name] = 'Amend')
AND  ((Value1Old IS NOT NULL AND Value1New > 0)
OR    (Value2Old IS NOT NULL AND Value2New > 0)))
ORDER BY
  RequestID

这是一个 SQL Fiddle 数据,我当前的查询和预期结果:http://sqlfiddle.com/#!18/4ed0e/1/0

我建议一个可能的改进是将确定更改的逻辑移动到一个函数中:

create or alter function fnChange (@Old int, @New int) 
returns varchar(10) as
begin
    return (
    select 
        case
            when IsNull(@Old,0)=IsNull(@New,0) then null
        else
            case when @New=0 or @new is null then 'Delete' else
                case when @Old is null then 'Insert'
                else 'Amend' end
            end
        end 
    )
end

然后你可以重新使用它来简化。我相信以下内容会为您提供所需的输出:

with r as (
    select RequestId, dbo.fnChange(Value1Old,Value1New) Change, Value1Old, Value1New, null Value2Old, null Value2New
    from Requests
    union all
    select RequestId, dbo.fnChange(Value2Old,Value2New) Change, null Value1Old, null Value1New, Value2Old, Value2New
    from Requests
)
select requestId, Change, 
    Max(Value1Old) Value1Old, Max(Value1New) value1New,
    Max(Value2Old) Value2Old, Max(Value2New) value2New
from r where change is not null
group by requestId, Change
order by requestId, change desc

DBFiddle