Tengo una tabla en el servidor SQL que se ve así:
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
Estoy trabajando en un procedimiento almacenado para diferenciar, que toma datos de entrada y un número de versión. Los datos de entrada tienen columnas de Nombre hasta campoZ. Se espera que la mayoría de las columnas de campo sean NULL, es decir, cada fila generalmente tiene datos solo para los primeros campos, el resto son NULL. El nombre, la fecha y la versión forman una restricción única en la tabla.
Necesito diferenciar los datos que se ingresan con respecto a esta tabla, para una versión dada. Cada fila debe diferenciarse: una fila se identifica por el nombre, la fecha y la versión, y cualquier cambio en cualquiera de los valores en las columnas del campo deberá mostrarse en la diferencia.
Actualización: no es necesario que todos los campos sean de tipo decimal. Algunos de ellos pueden ser nvarchars. Preferiría que se produzca el diff sin convertir el tipo, aunque la salida de diff podría convertir todo a nvarchar, ya que se debe usar solo para la visualización intencionada.
Supongamos que la entrada es la siguiente y la versión solicitada es 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
El diff debe estar en el siguiente 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
Mi solución hasta ahora es generar primero un diff, usando EXCEPT y UNION. Luego, convierta el diff al formato de salida deseado usando unir y aplicar cruz. Aunque esto parece estar funcionando, me pregunto si hay una forma más limpia y eficiente de hacerlo. El número de campos es cercano a 100, y cada lugar en el código que tiene un ... es en realidad una gran cantidad de líneas. Se espera que tanto la tabla de entrada como la tabla existente sean bastante grandes con el tiempo. Soy nuevo en SQL y todavía estoy tratando de aprender el ajuste del rendimiento.
Aquí está el SQL para ello:
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)
¡Gracias!