简体   繁体   English

跟踪T-SQL中每个表列的数据更改

[英]Tracking data changes per table column in T-SQL

Given the table below I want to get the history of changes for the two columns price and qty for each value of fruit . 给定下表,我想获取两列price和每个fruit值的qty变化的历史记录。 I'm using MS SQL Server 2012. 我正在使用MS SQL Server 2012。

So for example: 因此,例如:

  • the change between row 1 and 3 was that the price of apples went from 10 to 30 which gives row 1 in the result table below. 第1行和第3行之间的变化是苹果的价格从10升至30,这在下面的结果表中给出了第1行。
  • the change between row 2 and 4 was that the price of bananas went from 20 to 30 and thw quantity went from 2 to 3 which gives rows 2 and 3 in the result table. 第2行和第4行之间的变化是,香蕉的价格从20变为30,数量从2变为3,这在结果表中给出了第2和3行。

Is there a way to do this? 有没有办法做到这一点? Is there also a way to do this efficiently? 有没有办法有效地做到这一点?

Source table 源表

+----+---------+--------+--------+---------+
| id | fruit   | price  | qty    | created |
+----+---------+--------+--------+---------+
| 1  | apples  | 10     | 1      | 1/1/16  |
+----+---------+--------+--------+---------+
| 2  | bananas | 20     | 2      | 1/1/16  |
+----+---------+--------+--------+---------+
| 3  | apples  | 30     | 1      | 2/1/16  |
+----+---------+--------+--------+---------+
| 4  | bananas | 30     | 3      | 2/1/16  |
+----+---------+--------+--------+---------+
| 5  | apples  | 30     | 2      | 3/1/16  |
+----+---------+--------+--------+---------+
| 6  | apples  | 30     | 3      | 7/1/16  |
+----+---------+--------+--------+---------+

Results table 结果表

+----+----+--------+--------+--------+---------+
| id | fk | col    | oldval | newval | changed |
+----+----+--------+--------+--------+---------+
| 1  | 3  | price  | 10     | 30     | 2/1/16  |
+----+----+--------+--------+--------+---------+
| 2  | 4  | price  | 20     | 30     | 2/1/16  |
+----+----+--------+--------+--------+---------+
| 3  | 4  | qty    | 2      | 3      | 2/1/16  |
+----+----+--------+--------+--------+---------+
| 4  | 5  | qty    | 1      | 2      | 3/1/16  |
+----+----+--------+--------+--------+---------+
| 5  | 6  | qty    | 2      | 3      | 7/1/16  |
+----+----+--------+--------+--------+---------+

Here's one way: 这是一种方法:

;WITH LagCTE AS (
  SELECT  id, fruit, price, qty, created,
          LAG(price) OVER (PARTITION BY fruit 
                           ORDER BY created) AS prevPrice,
          LAG(qty) OVER (PARTITION BY fruit 
                         ORDER BY created) AS prevQty
  FROM mytable
)
SELECT ROW_NUMBER() OVER (ORDER BY changed) AS id,
       fk, col, oldval, newval, changed
FROM (       
  SELECT id AS fk, fruit, 'price' AS col, 
         prevPrice AS oldval, price AS newval, 
         created AS changed
  FROM LagCTE
  WHERE prevPrice <> price

  UNION ALL 

  SELECT id AS fk, fruit, 'qty' AS col, 
         prevQty AS oldval, qty AS newval, 
         created AS changed
  FROM LagCTE
  WHERE prevQty <> qty) AS t

Demo here 在这里演示

Preconditions for the following answer are that price and qty have the same datatype and id is an IDENTITY column. 以下答案的前提条件是priceqty具有相同的数据类型,并且idIDENTITY列。

The first step is to find changes. 第一步是找到更改。 You can do that by numering all records of the same fruit ordered by it's id and then join subsequent records. 您可以通过对id排序的同一水果的所有记录进行id ,然后加入后续记录来做到这一点。 Then you can UNPIVOT the result and filter unchanged colums out. 然后,你可以UNPIVOT结果,并进行过滤不变colums。

WITH
   SourceNumbered AS
      (
         SELECT
               ROW_NUMBER() OVER(PARTITION BY fruit ORDER BY id) AS nr,
               id, fruit, price, qty, created
            FROM
               SourceTable
      ),
   SourceUnpivoted AS
      (
         SELECT
               U.id, U.fk, U.col
            FROM
               (
                  SELECT
                        L.id, R.id AS fk,
                        L.price - R.price AS price,
                        L.qty - R.qty AS qty
                     FROM
                        SourceNumbered L
                        INNER JOIN SourceNumbered R
                           ON R.fruit = L.fruit
                              AND R.nr = L.nr + 1
               ) D
            UNPIVOT (value FOR col IN (price, qty)) U
            WHERE
               value != 0
      )
SELECT
      U.id, U.fk, U.col,
      CASE U.col
         WHEN 'price'
            THEN O.price
         WHEN 'qty'
            THEN O.qty
      END AS oldval,
      CASE U.col
         WHEN 'price'
            THEN N.price
         WHEN 'qty'
            THEN N.qty
      END AS oldval,
      N.created AS changed
   FROM
      SourceUnpivoted U
      INNER JOIN SourceTable O
         ON O.id = U.id
      INNER JOIN SourceTable N
         ON N.id = U.fk;

Since you can't unpivot more than one column, the case in the final SELECT is unavoidable. 由于您不能取消旋转多于一列,因此最终SELECT情况不可避免。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM