sql - SQL Server 2014中数据发生变化时如何提取列名?

标签 sql sql-server t-sql

我有一个每天晚上都会插入的表,它是数据的快照。在任何时间点,列中的数据都可能会发生变化(AccountNo 保持不变,RunKey 增加 1,RunDate 增加 1 天;所有其他列都可以临时更改)。以下是数据外观的示例:

|AccountNo | RunKey |   RunDate  |     Address     | Salary | PromotionDate|
----------------------------------------------------------------------------
|  12345   |    2   | 06/20/2017 | 123 Main Street | 60,000 |   01/15/2017 |
|  12345   |    3   | 06/21/2017 | 123 Main Street | 60,000 |   01/15/2017 |
|  12345   |    4   | 06/22/2017 | 123 Main Street | 65,000 |   06/21/2017 |

使用 LAG 函数和 CASE 表达式,我能够确定何时发生更改(标志为 1 表示发生更改):

|AccountNo | RunKey |   RunDate  |     Address     |    AddressLAG   |AddressFlag| Salary | SalaryLAG |SalaryFlag| PromotionDate|PromotionDateLag|PromotionFlag|
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
|  12345   |    2   | 06/20/2017 | 123 Main Street | 123 Main Street |     0     | 60,000 |   60,000  |    0     |   01/15/2017 |   01/15/2017   |      0      |
|  12345   |    3   | 06/21/2017 | 123 Main Street | 123 Main Street |     0     | 60,000 |   60,000  |    0     |   01/15/2017 |   01/15/2017   |      0      |
|  12345   |    4   | 06/22/2017 | 123 Main Street | 123 Main Street |     0     | 65,000 |   60,000  |    1     |   01/15/2017 |   06/21/2017   |      1      |

我只需将更改的记录插入到新表中,新表将如下所示:

| RunKey | AccountNo |  ChangedCol   | PrevRunDate |  RunDate   | PrevValue  |  NewValue  |
-------------------------------------------------------------------------------------------
|   4    |   12345   |    Salary     | 06/21/2017  | 06/22/2017 |    60,000  | 65,000     |
|   4    |   12345   | PromotionDate | 06/21/2017  | 06/22/2017 | 01/15/2017 | 06/21/2017 |

每列更改都会有一条新记录。因此,如果多个列发生更改,则每个更改都将记录在新行中。这是我需要帮助的地方,我不知道如何动态地将仅更改的列插入新表中。

最佳答案

因此,通常这是通过触发器来完成的。每当对表进行插入或更新时,都会对审计表进行后续插入。所以,我真的会调查一下。但是,如果您不想走这条路,或者这是一个无法添加触发器的第三方系统,您可以通过几种方式插入更改。一种快速的方法是使用 except。基本上,当记录不完全匹配时,它会将源表中的记录插入审计表中。这是一个例子。

declare @source table (
                        AccountNo int
                        ,Address varchar(256)
                        ,Salary decimal(16,4)
                        ,PromotionDate datetime)
insert into @source
values
(12345,'123 Main Street',60000,'20170115')


declare @audit table (
                        AccountNo int
                        ,Address varchar(256)
                        ,Salary decimal(16,4)
                        ,PromotionDate datetime
                        ,RunDate datetime)

--load the audit table with the current version of the source table
insert into @audit
select *, getdate() from @source

--show that the tables match currently
select * from @source
select * from @audit

--insert into @audit if there are any changes (notice we haven't made any updates yet)
insert into @audit
select AccountNo, Address, Salary, PromotionDate, getdate() from @source
except 
select AccountNo, Address, Salary, PromotionDate, getdate() from @audit

--show that a record WAS NOT inserted since there was no change. There is only 1 record, the orignal version
select * from @audit

--update the promotion and salary
update @source
set
    PromotionDate = '20170331'
    ,Salary = '65000'

--insert into @audit if there are any changes
insert into @audit
select AccountNo, Address, Salary, PromotionDate, getdate() from @source
except 
select AccountNo, Address, Salary, PromotionDate, getdate() from @audit

--show that a record was inserted since there was a change
select * from @audit

然后,您所要做的就是从 @audit 表中进行选择并按 RunDate 排序,您就可以轻松地快速看到更改的内容,而不是旋转数据并为每个帐户的每次更改保留 1 行。在此示例中,尽管工资和晋升数据发生了变化,但您仅看到 1 行额外的行。您可以使用 LEAD 和 LAG 函数,或者在顶部 1 上使用自连接,其中 RunDate < RunDate 来标记列是否发生更改,但实际上没有必要。

关于sql - SQL Server 2014中数据发生变化时如何提取列名?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44708276/

相关文章:

mysql - 数据传输-mysql和sql

sql-server - 使用 Filestream 是个好主意吗?

sql - 获取远程服务器版本 MS SQL

java - 在饼图中显示百分比值

SQLite - 在 SELECT INTO 语句上生成 GUID/UUID

sql - 在表上创建 View 可以提高 sql 2000 的查询性能吗?

php - 连接 Php、html 和 mysql

sql - SQL脚本中的Week()函数

t-sql - 按最大日期更新同一个表

sql - 消除sql语句中的重复值