La réponse actuellement acceptée est la meilleure réponse, mais je ne pense pas qu'elle explique assez bien pourquoi. Les autres réponses semblent certainement beaucoup plus propres en un coup d'œil (qui veut écrire cette déclaration de cas laide), mais sont susceptibles d'être bien pires lorsque vous commencez à opérer à grande échelle.
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: )
Voici comment j'ai tout mis en place
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;
Sur mon système, cela me donne 12 872 738 lignes dans le tableau. Si j'essaye chacune des requêtes ci-dessus (peaufinée pour SELECT INTO
ne pas avoir à attendre qu'elle termine l'impression des résultats dans SSMS), j'obtiens les résultats suivants:
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 vous regardez les plans de requête, il devient assez évident pourquoi - en ajoutant tout type de pivot ou d'agrégat (ou le ciel ne plaise STRING_SPLIT
), vous vous retrouverez avec toutes sortes d'opérateurs supplémentaires dont vous n'avez pas besoin (et cela force le plan à aller en parallèle, en supprimant les ressources que d'autres requêtes pourraient souhaiter). Par contrat, la CASE
solution basée ne va pas en parallèle, fonctionne très rapidement et est incroyablement simple.
Dans ce cas, à moins que vous n'ayez des ressources illimitées (vous n'en avez pas), vous devez choisir l'approche la plus simple et la plus rapide.
Il y avait une question de quoi faire si vous devez continuer à ajouter de nouvelles colonnes et à développer l'instruction case. Oui, cela devient compliqué, mais il en va de même pour toutes les autres solutions. S'il s'agit en fait d'un flux de travail plausible, vous devez reconcevoir votre table. Ce que vous voulez ressemble probablement à ceci:
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;
Ce n'est certainement pas exempt de problèmes de performances potentiels, et nécessitera un réglage minutieux de l'index, mais c'est le meilleur moyen de gérer un nombre arbitraire d'horodatages potentiels
Dans le cas où des réponses seraient supprimées, voici les versions que je comparais (dans l'ordre)
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