J'ai une table dans SQL Server qui ressemble à ceci:
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
Je travaille sur une procédure stockée pour différencier, qui prend les données d'entrée et un numéro de version. Les données d'entrée ont des colonnes du champ Nom uptilZ. La plupart des colonnes de champ sont censées être NULL, c'est-à-dire que chaque ligne contient généralement des données uniquement pour les premiers champs, les autres sont NULL. Le nom, la date et la version forment une contrainte unique sur la table.
J'ai besoin de différencier les données entrées par rapport à ce tableau, pour une version donnée. Chaque ligne doit être différenciée - une ligne est identifiée par le nom, la date et la version, et tout changement dans l'une des valeurs dans les colonnes de champ devra apparaître dans le diff.
Mise à jour: tous les champs n'ont pas besoin d'être de type décimal. Certains d'entre eux peuvent être des nvarchars. Je préférerais que le diff se produise sans convertir le type, bien que la sortie diff puisse tout convertir en nvarchar car elle ne doit être utilisée qu'à des fins d'affichage.
Supposons que l'entrée soit la suivante et que la version demandée soit 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
Le diff doit être au format suivant:
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
Ma solution jusqu'à présent est de générer d'abord un diff, en utilisant EXCEPT et UNION. Convertissez ensuite le diff au format de sortie souhaité à l'aide de JOIN et CROSS APPLY. Bien que cela semble fonctionner, je me demande s'il existe un moyen plus propre et plus efficace de le faire. Le nombre de champs est proche de 100, et chaque place dans le code qui a un ... est en fait un grand nombre de lignes. La table d'entrée et la table existante devraient être assez importantes au fil du temps. Je suis nouveau dans SQL et j'essaie toujours d'apprendre le réglage des performances.
Voici le SQL pour cela:
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)
Je vous remercie!