У меня есть таблица на сервере SQL, которая выглядит следующим образом:
Id |Version |Name |date |fieldA |fieldB ..|fieldZ
1 |1 |Foo |20120101|23 | ..|25334123
2 |2 |Foo |20120101|23 |NULL ..|NULL
3 |2 |Bar |20120303|24 |123......|NULL
4 |2 |Bee |20120303|34 |-34......|NULL
Я работаю над хранимой процедурой diff, которая принимает входные данные и номер версии. Входные данные имеют столбцы из Имя до поля Z. Ожидается, что большинство столбцов полей будут NULL, т. Е. Каждая строка обычно содержит данные только для первых нескольких полей, остальные - NULL. Имя, дата и версия образуют уникальное ограничение для таблицы.
Мне нужно различить данные, которые вводятся относительно этой таблицы, для данной версии. Каждая строка должна быть диффузной - строка идентифицируется по имени, дате и версии, и любое изменение в любом из значений в столбцах поля должно отображаться в diff.
Обновление: все поля не обязательно должны быть десятичного типа. Некоторые из них могут быть nvarchars. Я бы предпочел, чтобы diff происходил без преобразования типа, хотя вывод diff мог конвертировать все в nvarchar, поскольку он должен использоваться только для отображения.
Предположим, что ввод следующий, и запрашиваемая версия 2,:
Name |date |fieldA |fieldB|..|fieldZ
Foo |20120101|25 |NULL |.. |NULL
Foo |20120102|26 |27 |.. |NULL
Bar |20120303|24 |126 |.. |NULL
Baz |20120101|15 |NULL |.. |NULL
Разница должна быть в следующем формате:
name |date |field |oldValue |newValue
Foo |20120101|FieldA |23 |25
Foo |20120102|FieldA |NULL |26
Foo |20120102|FieldB |NULL |27
Bar |20120303|FieldB |123 |126
Baz |20120101|FieldA |NULL |15
Мое решение до сих пор состоит в том, чтобы сначала сгенерировать diff, используя EXCEPT и UNION. Затем преобразуйте diff в желаемый формат вывода, используя JOIN и CROSS APPLY. Хотя это, кажется, работает, мне интересно, есть ли более чистый и эффективный способ сделать это. Количество полей близко к 100, и каждое место в коде, которое имеет ..., на самом деле представляет собой большое количество строк. Ожидается, что и входная таблица, и существующая таблица со временем будут достаточно большими. Я новичок в SQL и все еще пытаюсь научиться настройке производительности.
Вот SQL для этого:
CREATE TABLE #diff
( [change] [nvarchar](50) NOT NULL,
[name] [nvarchar](50) NOT NULL,
[date] [int] NOT NULL,
[FieldA] [decimal](38, 10) NULL,
[FieldB] [decimal](38, 10) NULL,
.....
[FieldZ] [decimal](38, 10) NULL
)
--Generate the diff in a temporary table
INSERT INTO #diff
SELECT * FROM
(
(
SELECT
'old' as change,
name,
date,
FieldA,
FieldB,
...,
FieldZ
FROM
myTable mt
WHERE
version = @version
AND mt.name + '_' + CAST(mt.date AS VARCHAR) IN (SELECT name + '_' + CAST(date AS VARCHAR) FROM @diffInput)
EXCEPT
SELECT 'old' as change,* FROM @diffInput
)
UNION
(
SELECT 'new' as change, * FROM @diffInput
EXCEPT
SELECT
'new' as change,
name,
date,
FieldA,
FieldB,
...,
FieldZ
FROM
myTable mt
WHERE
version = @version
AND mt.name + '_' + CAST(mt.date AS VARCHAR) IN (SELECT name + '_' + CAST(date AS VARCHAR) FROM @diffInput)
)
) AS myDiff
SELECT
d3.name, d3.date, CrossApplied.field, CrossApplied.oldValue, CrossApplied.newValue
FROM
(
SELECT
d2.name, d2.date,
d1.FieldA AS oldFieldA, d2.FieldA AS newFieldA,
d1.FieldB AS oldFieldB, d2.FieldB AS newFieldB,
...
d1.FieldZ AS oldFieldZ, d2.FieldZ AS newFieldZ,
FROM #diff AS d1
RIGHT OUTER JOIN #diff AS d2
ON
d1.name = d2.name
AND d1.date = d2.date
AND d1.change = 'old'
WHERE d2.change = 'new'
) AS d3
CROSS APPLY (VALUES ('FieldA', oldFieldA, newFieldA),
('FieldB', oldFieldB, newFieldB),
...
('FieldZ', oldFieldZ, newFieldZ))
CrossApplied (field, oldValue, newValue)
WHERE
crossApplied.oldValue != crossApplied.newValue
OR (crossApplied.oldValue IS NULL AND crossApplied.newValue IS NOT NULL)
OR (crossApplied.oldValue IS NOT NULL AND crossApplied.newValue IS NULL)
Спасибо!