从多个列获取最后日期


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 

Answers:


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表达式作为解决方法。

编辑:

看来在您的实际表中,三个日期列中的一个或多个可能具有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
蒂姆·比格莱森

3
然后,这里给出的许多答案都会中断,并且将无法正常工作。老实说,如果您甚至需要对四列进行此比较,则可能需要重新考虑数据库表的设计,而是将每个日期值放到单独的行中。如果您将每个日期都放在单独的行中,那么您的要求将是微不足道的,因为这样我们就可以MAX使用using了GROUP BY。因此,我对您的问题的回答是“不会解决”,因为我认为您的数据库设计可能需要更改。
蒂姆·比格勒伊森

1
蒂姆就在这里,@ AhmedAlkhteeb,如果您有10列日期列,则可能是非规范化数据。一行中的一对夫妇就可以了,这意味着不同的事情(例如,“开始”和“结束”,“出生日期”以及将一个人添加到系统中的日期),但是很多日期(其中有10个)表明您在每当发生变化时,将新日期添加到列中;不插入新行来维护历史记录。例如,如果是送货服务公司的数据库,则旅程的每个可能步骤都没有日期列;您将为每一行插入一个新行。
拉努(Larnu)

1
在这种情况下,@ AhmedAlkhteeb Larnu是正确的-您应该有一个带有动作(call_case)和时间戳的表。没有一个包含50列的表格
Dannnno

13

目前接受的答案是最好的答案,但我不认为它解释为什么的工作不够好。其他答案一目了然(看上去很丑陋)(谁想要写这个丑陋的case语句),但是当您开始大规模操作时,答案可能会更糟。

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%

如果您查看查询计划,原因将变得非常明显-添加任何种类的unpivot或合计(或禁止上天堂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

这是出色的侦探工作+1,我很惊讶它没有吸引任何投票。
Tim Biegeleisen

+1是非常有帮助的
艾哈迈德·阿尔赫特伯

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()中的MAX()和GROUP BY,这会使查询变慢;最好只使用SELECT i.call_case,(SELECT MAX(d.date)FROM(VALUES((i.date1)),((i.date2)),((i.date3)))AS d(date))AS max_date来自#债务AS i
托马斯·弗朗兹

8

SQL字段

采用 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 '19

@DWRoelands我同意这可能不是一个选择,也许我应该更清楚一点。我只是根据其他评论做出回应,如果可能的话,重新设计将是更好的解决方案并提供示例。而且我很清楚,有很多原因无法重新设计数据库。
Enoch
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.