A resposta atualmente aceita é a melhor resposta, mas não acho que seja suficiente explicar o porquê. As outras respostas certamente parecem muito mais limpas à primeira vista (quem quer escrever essa declaração feia de caso), mas provavelmente serão muito piores quando você começar a operar em 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: )
Aqui está como eu configuro tudo
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;
No meu sistema, isso gera 12.872.738 linhas na tabela. Se eu tentar cada uma das consultas acima (ajustada para SELECT INTO
que eu não precise esperar que ela termine de imprimir os resultados no SSMS), recebo os seguintes 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%
Se você olhar para os planos de consulta, torna-se bastante óbvio o motivo: ao adicionar qualquer tipo de dinâmica não agregada ou agregada (ou proibida STRING_SPLIT
), você terminará com todos os tipos de operadores adicionais dos quais não precisa (e força o plano a paralelamente, retirando recursos que outras consultas podem querer). Por contrato, a CASE
solução baseada não é paralela, é executada muito rapidamente e é incrivelmente simples.
Nesse caso, a menos que você tenha recursos ilimitados (não possui), escolha a abordagem mais simples e rápida.
Havia uma pergunta sobre o que fazer se você precisar continuar adicionando novas colunas e expandindo a instrução de caso. Sim, isso fica pesado, mas o mesmo acontece com qualquer outra solução. Se esse é realmente um fluxo de trabalho plausível, você deve redesenhar sua tabela. O que você deseja provavelmente se parece com isso:
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;
Isso certamente não está livre de possíveis problemas de desempenho e exigirá um ajuste cuidadoso do índice, mas é a melhor maneira de lidar com um número arbitrário de possíveis carimbos de data / hora
Caso alguma resposta seja excluída, aqui estão as versões que eu estava comparando (em ordem)
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