В настоящее время принятый ответ - лучший ответ, но я не думаю, что он достаточно хорошо объясняет почему. Другие ответы, безусловно, выглядят намного чище (кто хочет написать это безобразное утверждение), но, вероятно, будут намного хуже, когда вы начнете работать в масштабе.
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: )
Вот как я все настроил
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;
В моей системе это дает мне 12 872 738 строк в таблице. Если я попытаюсь SELECT INTO
выполнить каждый из указанных выше запросов (настроенный таким образом, чтобы мне не нужно было ждать, пока он завершит печать результатов в SSMS), я получу следующие результаты:
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%
Если вы посмотрите на планы запросов, станет совершенно очевидно, почему - добавив любой тип разворота или агрегата (или, боже упаси STRING_SPLIT
), вы получите все виды дополнительных операторов, которые вам не нужны (и это заставляет план идти параллельно, забирая ресурсы, которые могут понадобиться другим запросам). По контракту CASE
основанное решение не идет параллельно, работает очень быстро и невероятно просто.
В этом случае, если у вас нет неограниченных ресурсов (у вас их нет), вы должны выбрать самый простой и быстрый подход.
Возник вопрос, что делать, если вам нужно продолжать добавлять новые столбцы и расширять оператор case. Да, это становится громоздким, как и любое другое решение. Если это действительно правдоподобный рабочий процесс, то вам следует переделать таблицу. То, что вы хотите, вероятно, выглядит примерно так:
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;
Это, безусловно, не освобождает от потенциальных проблем с производительностью и потребует тщательной настройки индекса, но является лучшим способом обработки произвольного числа потенциальных временных меток.
В случае, если какие-либо ответы будут удалены, вот версии, которые я сравнивал (по порядку)
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