Ich habe eine Tabelle in SQL Server, die so aussieht:
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
Ich arbeite an einer gespeicherten Prozedur zum Diff, die Eingabedaten und eine Versionsnummer verwendet. Die Eingabedaten enthalten Spalten aus Name bis FeldZ. Es wird erwartet, dass die meisten Feldspalten NULL sind, dh jede Zeile enthält normalerweise nur Daten für die ersten Felder, der Rest ist NULL. Der Name, das Datum und die Version bilden eine eindeutige Einschränkung für die Tabelle.
Ich muss die Daten, die in Bezug auf diese Tabelle eingegeben werden, für eine bestimmte Version unterscheiden. Jede Zeile muss unterschiedlich sein - eine Zeile wird durch den Namen, das Datum und die Version identifiziert, und jede Änderung der Werte in den Feldspalten muss im Unterschied angezeigt werden.
Update: Alle Felder müssen nicht dezimal sein. Einige von ihnen können nvarchars sein. Ich würde es vorziehen, wenn das Diff ohne Konvertierung des Typs erfolgt, obwohl die Diff-Ausgabe alles in nvarchar konvertieren könnte, da es nur für die Anzeige verwendet werden soll.
Angenommen, die Eingabe ist die folgende und die angeforderte Version ist 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
Der Diff muss das folgende Format haben:
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
Meine bisherige Lösung besteht darin, zunächst mit EXCEPT und UNION ein Diff zu generieren. Konvertieren Sie dann das Diff mit JOIN und CROSS APPLY in das gewünschte Ausgabeformat. Obwohl dies zu funktionieren scheint, frage ich mich, ob es einen saubereren und effizienteren Weg gibt, dies zu tun. Die Anzahl der Felder liegt nahe bei 100, und jede Stelle im Code, die ein ... enthält, besteht tatsächlich aus einer großen Anzahl von Zeilen. Es wird erwartet, dass sowohl die Eingabetabelle als auch die vorhandene Tabelle im Laufe der Zeit ziemlich groß sind. Ich bin neu in SQL und versuche immer noch, die Leistungsoptimierung zu lernen.
Hier ist die SQL dafür:
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)
Vielen Dank!