Saya punya tabel di SQL server yang terlihat seperti ini:
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
Saya sedang mengerjakan prosedur tersimpan untuk diff, yang membutuhkan input data dan nomor versi. Data input memiliki kolom dari kolom Name uptilZ. Sebagian besar kolom bidang diharapkan menjadi NULL, yaitu, setiap baris biasanya memiliki data hanya untuk beberapa bidang pertama, sisanya adalah NULL. Nama, tanggal dan versi membentuk kendala unik di atas meja.
Saya perlu diff data yang dimasukkan sehubungan dengan tabel ini, untuk versi yang diberikan. Setiap baris harus di-diff - baris diidentifikasi oleh nama, tanggal dan versi, dan setiap perubahan dalam nilai-nilai apa pun di kolom bidang harus ditampilkan di dalam diff.
Pembaruan: semua bidang tidak harus bertipe desimal. Beberapa dari mereka mungkin nvarchars. Saya lebih suka diff terjadi tanpa mengubah tipe, walaupun output diff dapat mengubah segalanya menjadi nvarchar karena hanya digunakan untuk tampilan yang dimaksudkan.
Misalkan inputnya adalah sebagai berikut, dan versi yang diminta adalah 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
Perbedaannya harus dalam format berikut:
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
Solusi saya sejauh ini adalah pertama-tama menghasilkan diff, menggunakan KECUALI dan UNION. Kemudian konversikan diff ke format output yang diinginkan menggunakan JOIN dan CROSS APPLY. Meskipun ini tampaknya berhasil, saya bertanya-tanya apakah ada cara yang lebih bersih dan lebih efisien untuk melakukan ini. Jumlah bidang mendekati 100, dan setiap tempat dalam kode yang memiliki ... sebenarnya adalah sejumlah besar garis. Baik tabel input maupun tabel yang ada diharapkan cukup besar dari waktu ke waktu. Saya baru mengenal SQL dan masih mencoba mempelajari penyetelan kinerja.
Ini SQL untuknya:
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)
Terima kasih!