sql - الحصول على التواريخ الأخيرة من أعمدة متعددة




sql-server datetime (4)

هذا يبدو وكأنه ينبغي أن يكون سهلا. كيف يمكنني الحصول على أحدث التواريخ الموجودة في أعمدة مختلفة

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 كحل.

تصحيح:

يبدو أنه في الجدول الفعلي ، يمكن أن يحتوي عمود أو أكثر من أعمدة التاريخ الثلاثة على قيم 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 call_case,
  (SELECT
     MAX(call_case) 
   FROM ( VALUES 
            (MAX(date1)), 
            (MAX(date2))
            ,(max(date3)) 
        ) MyAlias(call_case)
  ) 
FROM #indebtedness
group by call_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%

إذا نظرت إلى خطط الاستعلام ، يصبح من الواضح تمامًا لماذا - عند إضافة أي نوع من المحاكاة أو التجميع (أو سمح السماء STRING_SPLIT ) ، ستنتهي بكافة أنواع المشغلات الإضافية التي لا تحتاج إليها ( 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

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






datetime