Eu tenho uma tabela no SQL Server que se parece com isso:
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
Estou trabalhando em um procedimento armazenado para diff, que leva dados de entrada e um número de versão. Os dados de entrada possuem colunas do Nome até o campoZ. Espera-se que a maioria das colunas de campo seja NULL, ou seja, cada linha geralmente possui dados apenas para os primeiros campos, o restante é NULL. O nome, a data e a versão formam uma restrição exclusiva na tabela.
Eu preciso diferenciar os dados que são inseridos em relação a esta tabela, para uma determinada versão. Cada linha precisa ser diferenciada - uma linha é identificada pelo nome, data e versão, e qualquer alteração em qualquer um dos valores nas colunas de campo precisará ser exibida na comparação.
Atualização: todos os campos não precisam ser do tipo decimal. Alguns deles podem ser nvarchars. Eu preferiria que o diff acontecesse sem converter o tipo, embora a saída do diff possa converter tudo em nvarchar, pois deve ser usada apenas para exibição proposta.
Suponha que a entrada seja a seguinte e a versão solicitada seja 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
O diff precisa estar no seguinte 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
Minha solução até agora é primeiro gerar um diff, usando EXCEPT e UNION. Em seguida, converta o diff para o formato de saída desejado usando um JOIN e CROSS APPLY. Embora isso pareça estar funcionando, estou me perguntando se existe uma maneira mais limpa e eficiente de fazer isso. O número de campos é próximo de 100 e cada local no código que possui um ... é na verdade um grande número de linhas. Espera-se que a tabela de entrada e a tabela existente sejam bastante grandes ao longo do tempo. Eu sou novo no SQL e ainda estou tentando aprender o ajuste de desempenho.
Aqui está o SQL para isso:
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)
Obrigado!