Ho una tabella in SQL Server che assomiglia a questo:
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
Sto lavorando su una procedura memorizzata per diff, che richiede dati di input e un numero di versione. I dati di input hanno colonne da Nome fino a fieldZ. La maggior parte delle colonne dei campi dovrebbe essere NULL, ovvero, ogni riga di solito contiene dati solo per i primi pochi campi, il resto è NULL. Il nome, la data e la versione formano un vincolo univoco sulla tabella.
Ho bisogno di diff i dati che vengono immessi rispetto a questa tabella, per una data versione. Ogni riga deve essere diffusa: una riga è identificata dal nome, dalla data e dalla versione e qualsiasi modifica in uno qualsiasi dei valori nelle colonne del campo dovrà essere mostrata nel diff.
Aggiornamento: non è necessario che tutti i campi siano di tipo decimale. Alcuni di loro potrebbero essere nvarchars. Preferirei che il diff accadesse senza convertire il tipo, anche se l'output diff potrebbe convertire tutto in nvarchar dal momento che deve essere utilizzato solo per scopi di visualizzazione.
Supponiamo che l'input sia il seguente e che la versione richiesta sia 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
Il diff deve essere nel seguente formato:
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
La mia soluzione finora è di generare prima un diff, usando EXCEPT e UNION. Quindi converti il diff nel formato di output desiderato usando JOIN e CROSS APPLY. Anche se questo sembra funzionare, mi chiedo se esiste un modo più pulito ed efficiente per farlo. Il numero di campi è vicino a 100, e ogni posto nel codice che ha un ... è in realtà un gran numero di righe. Sia la tabella di input che la tabella esistente dovrebbero essere abbastanza grandi nel tempo. Sono nuovo di SQL e sto ancora cercando di imparare l'ottimizzazione delle prestazioni.
Ecco l'SQL per esso:
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)
Grazie!