여러 열에서 마지막 날짜를 가져옵니다


18

이것은 쉬운 것 같아요. 다른 열에있는 최신 날짜를 얻으려면 어떻게합니까

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 

답변:


20

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 개 날짜 열 중 하나 이상에 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;

데모


그것은 작동하지 않습니다 date3는 3 열의 마지막 날짜를 얻지 못합니다
Ahmed Alkhteeb

1
@AhmedAlkhteeb 하나 이상의 날짜 열이있는 경우도 처리하도록 답변을 편집했습니다 NULL.
Tim Biegeleisen '11

3
그런 다음 여기에 주어진 많은 답변이 깨져 작동하지 않습니다. 솔직히, 심지어 4 개의 열에 대해이 비교를 수행해야하는 경우 데이터베이스 테이블 디자인을 다시 생각하고 각 날짜 값을 별도의 에 가져 오는 것이 좋습니다 . 각 날짜가 별도의 행에 있으면 요구 사항이 사소한 것입니다. 왜냐하면 우리는 MAXusing 만 사용할 수 있기 때문 GROUP BY입니다. 따라서 귀하의 질문에 대한 나의 대답은 "수정되지 않을 것"입니다. 데이터베이스 설계가 변경되어야 할 수도 있기 때문입니다.
Tim Biegeleisen '11

1
Tim은 바로 여기 있습니다. @AhmedAlkhteeb 날짜 열이 10 개인 경우 데이터가 비정규 화되었을 수 있습니다. 한 줄에있는 부부는 괜찮습니다. 즉, 다른 것들을 의미합니다 (시작과 끝, 생년월일과 사람이 시스템에 추가 된 날짜). 그러나 많은 날짜 (10 중)는 당신이 무언가가 바뀔 때마다 새로운 날짜를 열에 추가; 기록을 유지하기 위해 새 행을 삽입하지 않습니다. 예를 들어 배달 서비스 회사의 데이터베이스 인 경우 가능한 모든 여행 단계에 대한 날짜 열이 없습니다. 각각에 대해 새 행을 삽입합니다.
Larnu

1
이 경우 @AhmedAlkhteeb Larnu가 정확합니다. 작업 ( call_case)과 타임 스탬프 가있는 테이블이 있어야합니다 . 열이 50 개인 단일 테이블이 아님
Dannnno

13

현재 허용 대답은 최고의 답변입니다,하지만 난 이유를 설명의 충분한 일을 생각하지 않습니다. 다른 대답은 분명히 한 눈에 훨씬 더 깔끔해 보이지만 (그 못생긴 사례 진술을 작성하려는 경우) 규모에서 운영을 시작할 때 훨씬 더 나빠질 수 있습니다.

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 INTOSSMS로 결과 인쇄를 마칠 때까지 기다릴 필요가 없으므로) 다음과 같은 결과가 나타납니다.

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기반 솔루션은 병렬화되지 않고 매우 빠르게 실행되며 매우 간단합니다.

이 경우 무제한 리소스가없는 경우 (아직없는 경우) 가장 간단하고 빠른 방법을 선택해야합니다.


새 열을 계속 추가하고 사례 설명을 확장해야하는 경우 수행 할 작업에 대한 질문이있었습니다. 예, 이것은 다루기 힘들지만 다른 모든 솔루션도 마찬가지입니다. 이것이 실제로 그럴듯한 워크 플로라면 테이블을 다시 디자인해야합니다. 원하는 것은 아마도 다음과 같습니다.

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

이것은 훌륭한 형사 연구 +1이며, 나는 어떤 유권자들을 끌어 들이지 않는 것에 놀랐습니다.
Tim Biegeleisen '11

매우 도움이됩니다 답변 +1
Ahmed Alkhteeb

11

이 시도:

SELECT call_case,
  (SELECT
     MAX(call_case) 
   FROM ( VALUES 
            (MAX(date1)), 
            (MAX(date2))
            ,(max(date3)) 
        ) MyAlias(call_case)
  ) 
FROM #indebtedness
group by call_case

@AhmedAlkhteeb. . . 이것이 가장 좋은 대답입니다. NULLs를 처리 하고 성능이 좋으며 더 많은 열로 쉽게 일반화됩니다.
Gordon Linoff

VALUES () 및 GROUP BY의 MAX ()는 필요하지 않으며 쿼리 속도가 느려집니다. SELECT i.call_case, (SELECT MAX (d.date) FROM (VALUES ((i.date1)), ((i.date2)), ((i.date3))) AS d (date)) AS를 사용하는 것이 좋습니다. max_date FROM #Indebtedness AS i
Thomas Franz

8

SQL FIDDLE

사용하다 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

2
다운 투표에 대한 단서가 아닙니다. 제 의견으로는 MAX를 사용하는 예가 허용 된 솔루션보다 훨씬 우아합니다 (날짜가 많을 경우 매우 번거로울 것입니다).
BarneyL

1
더 많은 가치를 가진 방법을 사용하는 방법 VALUES은 큰 CASE표현 보다 훨씬 확장 가능하다는 데 동의합니다 . 유권자 는 SQL에 문제가 있다고 생각하는 것처럼 보이기 때문에 왜 다운 투표를했는지 알고 싶습니다. 따라서 그 문제를 알려 주면 모두 배울 수 있습니다.
Larnu

1

필자의 관점에서 Pivot은이 쿼리에 가장 적합하고 효율적인 옵션입니다. 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

0

다른 사람들이 지적했듯이 이것은 실제로 설계 수준에서 재평가되어야합니다. 아래는 결과에서 찾고자하는 것을 더 잘 달성하기 위해 두 개의 테이블을 사용하는 다른 디자인의 예입니다. 이것은 성장을 훨씬 유리하게 할 것입니다.

다음은 예입니다 (사용 된 다른 테이블 이름).

-- 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

이를 통해 더 많은 케이스 유형을 추가하고 더 많은 로그 항목을 추가 할 수 있으며 더 나은 디자인을 제공합니다.

이것은 학습 목적을위한 예일뿐입니다.


사용자의 상황에 따라 데이터베이스를 다시 디자인하는 것은 옵션이 아닐 수 있습니다. 데이터를 재구성 할 필요가없는 다른 옵션이 있습니다.
DWRoelands

@ DWRoelands 나는 그것이 옵션이 아닐 수도 있다는 것에 동의 할 것이고, 아마도 이것을 더 분명하게 만들어야했을 것입니다. 가능한 경우 재 설계 가 더 나은 솔루션이되고 예를 제공 한다는 다른 의견을 바탕으로 응답했습니다 . 그리고 데이터베이스를 다시 디자인 할 수없는 많은 이유가 있다는 것을 잘 알고 있습니다.
Enoch
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.