これは簡単なはずです。別の列にある最新の日付を取得する方法
DROP TABLE #indebtedness
CREATE TABLE #indebtedness (call_case CHAR(10), date1 DATETIME, date2 DATETIME, date3 DATETIME)
INSERT #indebtedness VALUES ('Key1', '2019-10-30', '2019-11-30', '2019-10-25')
INSERT #indebtedness VALUES ('Key2', '2019-10-20', '2019-10-30', '2019-10-15')
INSERT #indebtedness VALUES ('Key3', '2019-11-11', '2019-10-29', '2019-10-30')
INSERT #indebtedness VALUES ('Key4', null , '2019-10-29', '2019-10-13')
select call_case, ?? AS 'Latest Date' from #indebtedness
結果を希望します:
call_case Latest Date
Key1 2019-11-30
Key2 2019-10-30
Key3 2019-11-11
Key4 2019-10-29
CASE
式を使用します。
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;
MySQL、SQL Server、SQLiteなどの一部のデータベースは、スカラー関数をサポートしています。 SQL Serverではサポートされていないため、回避策としてCASE
式を使用できます。
編集:
実際のテーブルでは、3つの日付列の1つ以上がNULL
値を持つ可能性があります。上記のクエリを次のように変更できます。
SELECT
call_case,
CASE WHEN (date1 > date2 OR date2 IS NULL) AND (date1 > date3 OR date3 IS NULL)
THEN date1
WHEN date2 > date3 OR date3 IS NULL
THEN date2
ELSE date3 END AS [Latest Date]
FROM #indebtedness;
現在受け入れられている回答 が最良の回答ですが、理由を説明するのに十分とは言えません。他の回答は一見すると見た目がすっきりしているように見えます(その醜いケースステートメントを書きたいと思っている人)が、大規模な運用を開始すると、はるかに悪くなる可能性があります。
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
これを試して:
SELECT call_case,
(SELECT
MAX(call_case)
FROM ( VALUES
(MAX(date1)),
(MAX(date2))
,(max(date3))
) MyAlias(call_case)
)
FROM #indebtedness
group by call_case
MAX()
を使用します
SELECT call_case,
(SELECT Max(v)
FROM (VALUES (date1), (date2), (date3),...) AS value(v)) as [MostRecentDate]
FROM #indebtedness
CASE
を使用
SELECT
CASE
WHEN Date1 >= Date2 AND Date1 >= Date3 THEN Date1
WHEN Date2 >= Date1 AND Date2 >= Date3 THEN Date2
WHEN Date3 >= Date1 AND Date3 >= Date2 THEN Date3
ELSE Date1
END AS MostRecentDate
FROM #indebtedness
私の考えでは、ピボットはこのクエリに最適で効率的なオプションです。 MS SQL SERVERにコピーして貼り付けます。以下のコードを確認してください:
CREATE TABLE #indebtedness (call_case CHAR(10), date1 DATETIME, date2 DATETIME, date3 DATETIME)
INSERT #indebtedness VALUES ('Key1', '2019-10-30', '2019-11-30', '2019-10-31')
INSERT #indebtedness VALUES ('Key2', '2019-10-20', '2019-10-30', '2019-11-21')
INSERT #indebtedness VALUES ('Key3', '2019-11-11', '2019-10-29', '2019-10-30')
INSERT #indebtedness VALUES ('Key4', Null, '2019-10-29', '2019-10-13')
--Solution-1:
SELECT
call_case,
MAX(RecnetDate) as MaxDateColumn
FROM #indebtedness
UNPIVOT
(RecnetDate FOR COL IN ([date1], [date2], [date3])) as TRANSPOSE
GROUP BY call_case
--Solution-2:
select
call_case, case
when date1>date2 and date1 > date3 then date1
when date2>date3 then date2
when date3>date1 then date1
else date3 end as date
from #indebtedness as a
Drop table #indebtedness
他の人が指摘したように、これは設計レベルで本当に再評価されるべきです。以下は、2つのテーブルを使用した異なるデザインの例であり、結果で探しているように見えるものをよりよく達成します。これにより、成長がはるかに有利になります。
次に例を示します(使用される異なるテーブル名)。
-- Drop pre-existing tables
DROP TABLE #call_log
DROP TABLE #case_type
-- Create table for Case Types
CREATE TABLE #case_type (id INT PRIMARY KEY CLUSTERED NOT NULL,
descript VARCHAR(50) NOT NULL)
INSERT #case_type VALUES (1,'No Answer')
INSERT #case_type VALUES (2,'Answer')
INSERT #case_type VALUES (3,'Not Exist')
INSERT #case_type VALUES (4,'whatsapp')
INSERT #case_type VALUES (5,'autodial')
INSERT #case_type VALUES (6,'SMS')
-- Create a Call Log table with a primary identity key and also an index on the call types
CREATE TABLE #call_log (call_num BIGINT PRIMARY KEY CLUSTERED IDENTITY NOT NULL,
call_type INT NOT NULL REFERENCES #case_type(id), call_date DATETIME)
CREATE NONCLUSTERED INDEX ix_call_log_entry_type ON #call_log(call_type)
INSERT #call_log(call_type, call_date) VALUES (1,'2019-11-30')
INSERT #call_log(call_type, call_date) VALUES (2,'2019-10-15')
INSERT #call_log(call_type, call_date) VALUES (3,null)
INSERT #call_log(call_type, call_date) VALUES (3,'2019-10-29')
INSERT #call_log(call_type, call_date) VALUES (1,'2019-10-25')
INSERT #call_log(call_type, call_date) VALUES (2,'2019-10-30')
INSERT #call_log(call_type, call_date) VALUES (3,'2019-10-13')
INSERT #call_log(call_type, call_date) VALUES (2,'2019-10-20')
INSERT #call_log(call_type, call_date) VALUES (1,'2019-10-30')
-- use an aggregate to show only the latest date for each case type
SELECT DISTINCT ct.descript, MAX(cl.call_date) AS "Date"
FROM #call_log cl JOIN #case_type ct ON cl.call_type = ct.id GROUP BY ct.descript
これにより、より多くのケースタイプを追加でき、より多くのログエントリを追加でき、より優れた設計が提供されます。
これは学習目的の単なる例です。