La respuesta actualmente aceptada es la mejor respuesta, pero no creo que explique por qué. Las otras respuestas ciertamente parecen mucho más limpias de un vistazo (quién quiere escribir esa fea declaración de caso), pero es probable que sean mucho peores cuando comience a operar a escala.
SELECT @@VERSION
Microsoft SQL Server 2016 (SP2) (KB4052908) - 13.0.5026.0 (X64)
Mar 18 2018 09:11:49
Copyright (c) Microsoft Corporation
Developer Edition (64-bit) on Windows 10 Enterprise 10.0 <X64> (Build 17763: )
Así es como configuro todo
DECLARE @Offset bigint = 0;
DECLARE @Max bigint = 10000000;
DROP TABLE IF EXISTS #Indebtedness;
CREATE TABLE #Indebtedness
(
call_case char(10) COLLATE DATABASE_DEFAULT NOT NULL,
date1 datetime NULL,
date2 datetime NULL,
date3 datetime NULL
);
WHILE @Offset < @Max
BEGIN
INSERT INTO #Indebtedness
( call_case, date1, date2, date3 )
SELECT @Offset + ROW_NUMBER() OVER ( ORDER BY ( SELECT NULL )),
DATEADD( DAY,
CASE WHEN RAND() > 0 THEN 1
ELSE -1 END * ROUND( RAND(), 0 ),
CURRENT_TIMESTAMP ),
DATEADD( DAY,
CASE WHEN RAND() > 0 THEN 1
ELSE -1 END * ROUND( RAND(), 0 ),
CURRENT_TIMESTAMP ),
DATEADD( DAY,
CASE WHEN RAND() > 0 THEN 1
ELSE -1 END * ROUND( RAND(), 0 ),
CURRENT_TIMESTAMP )
FROM master.dbo.spt_values a
CROSS APPLY master.dbo.spt_values b;
SET @Offset = @Offset + ROWCOUNT_BIG();
END;
En mi sistema, esto me da 12,872,738 filas en la tabla. Si intento cada una de las consultas anteriores (ajustada para SELECT INTO
no tener que esperar a que termine de imprimir los resultados en SSMS), obtengo los siguientes resultados:
Method | CPU time (ms) | Elapsed time (ms) | Relative Cost
-----------------------------------------------------------------------------------------
Tim Biegeleisen (CASE) | 13485 | 2167 | 2%
Red Devil (Subquery over MAX columns) | 55187 | 9891 | 14%
Vignesh Kumar (Subquery over columns) | 33750 | 5139 | 5%
Serkan Arslan (UNPIVOT) | 86205 | 15023 | 12%
Metal (STRING_SPLIT) | 459668 | 186742 | 68%
Si nos fijamos en los planes de consulta, resulta bastante obvio por qué: al agregar cualquier tipo de elemento no dinámico o agregado (o Dios no lo quiera STRING_SPLIT
), terminará con todo tipo de operadores adicionales que no necesita (y obliga al plan a ir en paralelo, quitando recursos que otras consultas podrían desear). Por contrato, la CASE
solución basada no es paralela, se ejecuta muy rápidamente y es increíblemente simple.
En este caso, a menos que tenga recursos ilimitados (no los tiene), debe elegir el enfoque más simple y rápido.
Hubo una pregunta sobre qué hacer si necesita seguir agregando nuevas columnas y expandir la declaración del caso. Sí, esto se vuelve difícil de manejar, pero también lo hace cualquier otra solución. Si este es realmente un flujo de trabajo plausible, entonces debe rediseñar su tabla. Lo que quieres probablemente se parece a esto:
CREATE TABLE #Indebtedness2
(
call_case char(10) COLLATE DATABASE_DEFAULT NOT NULL,
activity_type bigint NOT NULL, -- This indicates which date# column it was, if you care
timestamp datetime NOT NULL
);
SELECT Indebtedness.call_case,
Indebtedness.activity_type,
Indebtedness.timestamp
FROM ( SELECT call_case,
activity_type,
timestamp,
ROW_NUMBER() OVER ( PARTITION BY call_case
ORDER BY timestamp DESC ) RowNumber
FROM #Indebtedness2 ) Indebtedness
WHERE Indebtedness.RowNumber = 1;
Ciertamente, esto no está exento de posibles problemas de rendimiento y requerirá un ajuste cuidadoso del índice, pero es la mejor manera de manejar un número arbitrario de marcas de tiempo potenciales
En caso de que se eliminen las respuestas, aquí están las versiones que estaba comparando (en orden)
SELECT
call_case,
CASE WHEN date1 > date2 AND date1 > date3
THEN date1
WHEN date2 > date3
THEN date2
ELSE date3 END AS [Latest Date]
FROM #indebtedness;
SELECT call_case,
(SELECT Max(v)
FROM (VALUES (date1), (date2), (date3),...) AS value(v)) as [MostRecentDate]
FROM #indebtedness
SELECT call_case,
(SELECT
MAX(call_case)
FROM ( VALUES
(MAX(date1)),
(MAX(date2))
,(max(date3))
) MyAlias(call_case)
)
FROM #indebtedness
group by call_case
select call_case, MAX(date) [Latest Date] from #indebtedness
UNPIVOT(date FOR col IN ([date1], [date2], [date3])) UNPVT
GROUP BY call_case
select call_case , max(cast(x.Item as date)) as 'Latest Date' from #indebtedness t
cross apply dbo.SplitString(concat(date1, ',', date2, ',', date3), ',') x
group by call_case