Dapatkan tanggal terakhir dari banyak kolom

18

Ini terasa seperti itu harus mudah. Bagaimana cara mendapatkan tanggal terbaru yang ada di kolom yang berbeda

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 

Saya ingin hasilnya:

call_case   Latest Date
Key1        2019-11-30 
Key2        2019-10-30 
Key3        2019-11-11 
Key4        2019-10-29 
Ahmed Alkhteeb
sumber

Jawaban:

20

Gunakan CASEekspresi:

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;

Demo

Perhatikan bahwa beberapa database, seperti MySQL, SQL Server, dan SQLite, mendukung fungsi skalar terbesar. SQL Server tidak, jadi kita bisa menggunakanCASE ekspresi sebagai solusinya.

Edit:

Tampaknya dalam tabel Anda yang sebenarnya, satu atau lebih dari tiga kolom tanggal bisa memiliki NULLnilai. Kami dapat menyesuaikan kueri di atas sebagai berikut:

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;

Demo

Tim Biegeleisen
sumber
itu tidak berfungsi, mendapatkan date3 hanya tidak mendapatkan tanggal terakhir dalam 3 kolom
Ahmed Alkhteeb
1
@AhmedAlkhteeb Saya mengedit jawaban saya untuk juga menangani kasus di mana satu atau lebih kolom tanggal mungkin NULL.
Tim Biegeleisen
3
Maka banyak jawaban yang diberikan di sini akan rusak, dan tidak akan berhasil. Jujur, jika Anda perlu melakukan perbandingan ini di bahkan empat kolom, Anda mungkin ingin memikirkan kembali desain tabel database Anda, dan alih-alih mendapatkan setiap nilai tanggal ke baris yang terpisah . Persyaratan Anda akan sepele jika Anda memiliki masing-masing tanggal pada baris yang terpisah, karena dengan begitu kami bisa MAXmenggunakan saja GROUP BY. Jadi jawaban saya untuk pertanyaan Anda adalah "tidak akan diperbaiki," karena saya pikir mungkin desain database Anda perlu diubah.
Tim Biegeleisen
1
Tim ada di sini, @AhmedAlkhteeb jika Anda memiliki 10 kolom tanggal, Anda mungkin memiliki data yang tidak normal. Pasangan dalam satu baris baik-baik saja, itu berarti hal yang berbeda (Katakanlah Awal dan Akhir, dan Tanggal Lahir dan tanggal bahwa seseorang ditambahkan ke sistem), tetapi banyak tanggal (10 dari mereka) menyarankan Anda menambahkan tanggal baru ke dalam kolom setiap kali ada perubahan; tidak menyisipkan baris baru untuk mempertahankan riwayat. Misalnya, jika itu adalah database perusahaan jasa pengiriman, misalnya, ia tidak akan memiliki kolom tanggal untuk setiap langkah yang mungkin dari perjalanan; Anda akan memasukkan baris baru untuk masing-masing.
Larnu
1
@AhmedAlkhteeb dalam hal itu Larnu benar - Anda harus memiliki tabel dengan action ( call_case) dan cap waktu. Bukan satu meja dengan 50 kolom
Dannnno
13

Jawaban yang saat ini diterima adalah jawaban terbaik, tetapi saya pikir itu tidak cukup baik untuk menjelaskan alasannya. Sekilas jawaban lain tampak jauh lebih bersih (yang ingin menulis pernyataan kasus yang jelek), tetapi kemungkinan akan jauh lebih buruk ketika Anda mulai beroperasi pada skala.

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

Inilah cara saya mengatur semuanya

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;

Di sistem saya ini membuat saya 12.872.738 baris dalam tabel. Jika saya mencoba setiap pertanyaan di atas (tweaked toSELECT INTO jadi saya tidak perlu menunggu sampai selesai mencetak hasil dalam SSMS), saya mendapatkan hasil berikut:

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%

Jika Anda melihat rencana kueri, menjadi sangat jelas mengapa - menambahkan segala jenis unpivot atau agregat (atau surga dilarang STRING_SPLIT ) Anda akan berakhir dengan semua jenis operator tambahan yang tidak Anda butuhkan (dan itu memaksa rencana untuk pergi paralel, mengambil sumber daya pertanyaan lain mungkin ingin). Dengan kontrak, CASEsolusi berbasis tidak berjalan paralel, berjalan sangat cepat, dan sangat sederhana.

Dalam hal ini, kecuali Anda memiliki sumber daya tidak terbatas (Anda tidak memiliki), Anda harus memilih pendekatan yang paling sederhana dan tercepat.


Ada pertanyaan tentang apa yang harus dilakukan jika Anda perlu terus menambahkan kolom baru dan memperluas pernyataan kasus. Ya, ini menjadi sulit, tetapi begitu juga setiap solusi lainnya. Jika ini sebenarnya alur kerja yang masuk akal, maka Anda harus mendesain ulang tabel Anda. Apa yang Anda inginkan mungkin terlihat seperti ini:

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;

Ini tentu saja tidak bebas dari masalah kinerja potensial, dan akan membutuhkan penyetelan indeks yang cermat, tetapi merupakan cara terbaik untuk menangani sejumlah cap waktu potensial yang sewenang-wenang


Jika ada jawaban yang dihapus, berikut adalah versi yang saya bandingkan (agar)

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
Dannnno
sumber
Ini adalah pekerjaan detektif yang hebat +1, dan saya terkejut itu telah menghindari menarik upvotes.
Tim Biegeleisen
jawaban yang sangat membantu +1
Ahmed Alkhteeb
11

Coba ini:

SELECT call_case,
  (SELECT
     MAX(call_case) 
   FROM ( VALUES 
            (MAX(date1)), 
            (MAX(date2))
            ,(max(date3)) 
        ) MyAlias(call_case)
  ) 
FROM #indebtedness
group by call_case
setan Merah
sumber
@AhmedAlkhteeb. . . Ini jawaban terbaik. Ini menangani NULLs, harus memiliki kinerja yang baik, dan mudah digeneralisasikan ke lebih banyak kolom.
Gordon Linoff
MAX () dalam VALUES () dan GROUP BY tidak diperlukan dan membuat kueri menjadi lebih lambat; lebih baik gunakan SELECT i.call_case, (SELECT MAX (d.date) FROM (VALUES ((i.date1)), ((i.date2)), ((i.date3))) SEBAGAI d (tanggal)) SEBAGAI max_date DARI #Indebtedness AS i
Thomas Franz
8

SQL FIDDLE

Menggunakan MAX()

SELECT call_case,
  (SELECT Max(v) 
   FROM (VALUES (date1), (date2), (date3),...) AS value(v)) as [MostRecentDate]
FROM #indebtedness

Menggunakan 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
Vignesh Kumar A
sumber
2
Bukan petunjuk tentang suara turun, menurut pendapat saya contoh Anda menggunakan MAX jauh lebih elegan daripada solusi yang diterima (yang akan menjadi sangat rumit jika ada lebih banyak kolom tanggal).
BarneyL
1
Saya setuju, dengan lebih banyak nilai metode yang digunakan VALUESjauh lebih scalable daripada CASEekspresi yang besar . Saya juga ingin belajar mengapa itu dibatalkan, karena pemilih tampaknya percaya ada masalah dengan SQL, dan karena itu jika mereka memberi tahu kami masalah itu, kita semua bisa belajar darinya.
Larnu
1

Dalam pandangan saya, Pivot adalah opsi terbaik dan efisien untuk kueri ini. Salin dan Tempel di MS SQL SERVER. Silakan periksa kode yang ditulis di bawah ini:

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
Satheesh
sumber
0

Ini benar-benar harus dievaluasi kembali pada tingkat desain seperti yang telah ditunjukkan orang lain. Di bawah ini adalah contoh dari desain yang berbeda menggunakan dua tabel untuk lebih menyelesaikan apa yang tampaknya Anda cari dalam hasil Anda. Ini akan membuat pertumbuhan jauh lebih menguntungkan.

Berikut ini sebuah contoh (nama tabel yang berbeda digunakan):

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

Ini memungkinkan lebih banyak jenis case untuk ditambahkan, lebih banyak entri log yang akan ditambahkan dan memberikan desain yang lebih baik.

Ini hanya contoh untuk tujuan pembelajaran.

Henokh
sumber
Mendesain ulang basis data mungkin bukan opsi, tergantung pada situasi pengguna. Ada opsi lain yang tersedia yang tidak memerlukan restrukturisasi data.
DWRoelands
@DWRoelands Saya setuju bahwa itu mungkin bukan pilihan, dan mungkin saya harus membuatnya lebih jelas. Saya hanya menanggapi berdasarkan komentar lain bahwa mendesain ulang, jika mungkin , akan menjadi solusi yang lebih baik dan memberikan contoh. Dan saya sangat menyadari bahwa ada banyak alasan database tidak akan dapat dirancang ulang.
Henokh