Вот простой запрос, который дает одинаковые желаемые результаты, и его намного легче модифицировать, чтобы разместить другое количество столбцов или изменять имена столбцов, поскольку единственными отличиями являются столбцы PK + одна строка в не-PK-столбцом в CROSS APPLY
. Мне пришлось добавить столбец ChangeDate
- без него нет способа узнать порядок строк, вставленных в таблицу аудита.
WITH ColValues AS (
SELECT
Grp = Row_Number() OVER (
PARTITION BY H.OrderID, U.ColName ORDER BY H.ChangeDate ASC, X.Which
)/2,
H.OrderID,
H.ChangeDate,
U.*,
X.Which
FROM
dbo.OrderHistory H
CROSS APPLY (VALUES
('DeliveryDate', Convert(varchar(1000), DeliveryDate, 121)),
('Quantity', Convert(varchar(1000), Quantity)),
('SpecialNotes', Convert(varchar(1000), SpecialNotes))
) U (ColName, Value)
CROSS JOIN (VALUES (1), (2)) X (Which)
)
SELECT
V.OrderID,
V.ColName,
DateChanged = Max(V.ChangeDate),
OldValue = Max(F.Value),
NewValue = Max(T.Value)
FROM
ColValues V
OUTER APPLY (SELECT V.ColName, V.Value WHERE V.Which = 2) F
OUTER APPLY (SELECT V.ColName, V.Value WHERE V.Which = 1) T
GROUP BY
V.OrderID,
V.ColName,
V.Grp
HAVING
Count(*) = 2
AND EXISTS (
SELECT Max(F.Value)
EXCEPT SELECT Max(T.Value)
)
;
See a live demo of this query at SQL Fiddle.
В SQL 2012 это было бы лучше с аналитической функцией LEAD
или LAG
. CROSS JOIN
и Row_Number
в моем запросе имитируют это путем дублирования каждой строки и назначения этих дублированных строк парами в свои собственные группы (где каждая группа имеет две строки, представляющие смежные строки истории аудита). Затем путем стратегического использования агрегатов мы можем иметь дело с группированными парами, чтобы выбирать и сравнивать их значения.
Кроме того, я изначально написал запрос с UNPIVOT
, но, увы, он не сохраняет NULL - серьезный надзор со стороны Microsoft, на мой взгляд. Разработчикам было бы легко добавить условие удаления NULL, если это необходимо, но способ, которым он равен UNPIVOT
, вообще не может быть использован при желании сохранить NULL. По иронии судьбы, полученный код более компактен и на 2 строки короче, используя CROSS APPLY
для UNPIVOT - теперь преобразование и разворачивание происходят за один шаг вместо 2.
Мои данные выборки:
ChangeDate OrderID DeliveryDate Quantity SpecialNotes
----------------------- ------- ----------------------- -------- ----------------------------------------------------
2013-03-01 11:28:00.000 1 2013-04-01 00:00:00.000 25 NULL
2013-03-01 11:56:00.000 1 2013-04-01 00:00:00.000 30 NULL
2013-03-05 10:18:00.000 1 2013-04-02 00:00:00.000 30 Customer called to ask for delivery date adjustment.
2013-03-01 11:37:00.000 2 2013-03-05 00:00:00.000 17 NULL
Полученный набор строк:
OrderID ColName DateChanged OldValue NewValue
------- ------------ ----------------------- ----------------------- ---------------------------------------------------
1 DeliveryDate 2013-03-05 10:18:00.000 2013-04-01 00:00:00.000 2013-04-02 00:00:00.000
1 Quantity 2013-03-01 11:56:00.000 25 30
1 SpecialNotes 2013-03-05 10:18:00.000 NULL Customer called to ask for delivery date adjustment.
Примечание: поскольку мой запрос имеет только одну функция ранжирования и нет JOIN
с, это будет работать очень хорошо даже в очень большие таблицы - на порядок лучше, возможно, чем решения, которые используют JOIN
, где нет индекса поддержки. Было бы лучше, если бы таблица аудита имела кластерный индекс на PK, ChangeDate
.
Вы также не укажете время изменения (или, по крайней мере, некоторые последовательные значения, указывающие порядок изменений)? –
Еще одной проблемой могут быть типы проверенных столбцов. Если они разные, вам, вероятно, придется преобразовать все их в строки, чтобы они могли выводить их в тех же столбцах (а именно «предыдущее значение» и «новое значение»). –
Да, мне нужно будет вернуть дату проведения аудита, и да, мне придется отдать данные в varchar. – drunkcamel