SQL Server: PILIH hanya baris dengan MAX (DATE)

109

Saya memiliki tabel data (db adalah MSSQL):

    ID  OrderNO PartCode  Quantity DateEntered
    417 2144     44917    100      18-08-11
    418 7235     11762    5        18-08-11
    419 9999     60657    100      18-08-11
    420 9999     60657    90       19-08-11

Saya ingin membuat kueri yang mengembalikan OrderNO, PartCode, dan Kuantitas, tetapi hanya untuk pesanan terdaftar terakhir.

Dari tabel contoh saya ingin mendapatkan kembali info berikut:

     OrderNO PartCode  Quantity     
     2144     44917    100      
     7235     11762    5        
     9999     60657    90      

Perhatikan bahwa hanya satu baris yang dikembalikan untuk pesanan 9999.

Terima kasih!

GEMI
sumber
2
Dari komentar Anda, lanjutkan dengan jawaban ROW_NUMBER (). Ini mungkin terlihat lebih lama, tetapi menurut pengalaman saya, ini adalah yang tercepat dengan indeks yang sesuai.
MatBailie
Terima kasih Dems, saya menghargai usaha Anda.
GEMI
1
@GEMI hanya ingin tahu, Tidak MAX(DATE)mengembalikan satu baris untuk pesanan 9999?
Zameer
Ya, tetapi saya ingin setiap pesanan yang berbeda hanya mengembalikan baris pesanan terakhir.
GEMI

Jawaban:

184

Jika rownumber() over(...)tersedia untuk Anda ....

select OrderNO,
       PartCode,
       Quantity
from (select OrderNO,
             PartCode,
             Quantity,
             row_number() over(partition by OrderNO order by DateEntered desc) as rn
      from YourTable) as T
where rn = 1      
Mikael Eriksson
sumber
2
Terima kasih Mikael Eriksson, ini adalah pertanyaan yang luar biasa!
GEMI
56

Cara terbaik adalah Mikael Eriksson ROW_NUMBER() tersedia untuk Anda.

Terbaik berikutnya adalah bergabung dalam kueri, sesuai jawaban Cularis.

Alternatifnya, cara yang paling sederhana dan langsung adalah sub-kueri berkorelasi di klausa WHERE.

SELECT
  *
FROM
  yourTable AS [data]
WHERE
  DateEntered = (SELECT MAX(DateEntered) FROM yourTable WHERE orderNo = [data].orderNo)

Atau...

WHERE
  ID = (SELECT TOP 1 ID FROM yourTable WHERE orderNo = [data].orderNo ORDER BY DateEntered DESC)
MatBailie
sumber
29
select OrderNo,PartCode,Quantity
from dbo.Test t1
WHERE EXISTS(SELECT 1
         FROM dbo.Test t2
         WHERE t2.OrderNo = t1.OrderNo
           AND t2.PartCode = t1.PartCode
         GROUP BY t2.OrderNo,
                  t2.PartCode
         HAVING t1.DateEntered = MAX(t2.DateEntered))

Ini adalah kueri tercepat dari semua kueri yang diberikan di atas. Biaya permintaan masuk pada 0,0070668.

Jawaban pilihan di atas, oleh Mikael Eriksson, memiliki biaya kueri 0,0146625

Anda mungkin tidak peduli dengan kinerja untuk sampel sekecil itu, tetapi dalam kueri besar, semuanya bertambah.

nada
sumber
2
Ini ternyata bagi saya sedikit lebih cepat daripada solusi lain di sini pada set data baris ~ 3,5 juta, namun SSMS menyarankan indeks yang memotong waktu eksekusi menjadi setengahnya. Terima kasih!
easuter
Cepat dan mudah. Terima kasih.
Stephen Zeng
Saya memiliki 100 ribu baris dan bagi saya permintaan Mikael Eriksson 3 kali lebih cepat. Mungkin karena saya memiliki fungsi ROUND di partisi demi klausa.
Wachburn
Jika Anda memiliki bidang tanggal dengan nilai yang sama (15/04/2017) untuk 2 ID yang berbeda, ini akan mengembalikan 2 baris ...
Portekoi
Ya Portekoi, itu benar, tetapi tanpa cara lain untuk membedakan kedua baris tersebut, bagaimana Anda dapat memilih salah satu dari yang lain? Anda bisa menempatkan TOP pada hasil, tetapi bagaimana Anda tahu bahwa itu bukan baris lain yang Anda inginkan?
nada
10
SELECT t1.OrderNo, t1.PartCode, t1.Quantity
FROM table AS t1
INNER JOIN (SELECT OrderNo, MAX(DateEntered) AS MaxDate
            FROM table
            GROUP BY OrderNo) AS t2
ON (t1.OrderNo = t2.OrderNo AND t1.DateEntered = t2.MaxDate)

Kueri dalam memilih semua OrderNodengan tanggal maksimumnya. Untuk mendapatkan kolom lain pada tabel, Anda dapat menggabungkannya di OrderNodan MaxDate.

Yakub
sumber
1

Untuk MySql Anda dapat melakukan sesuatu seperti berikut:

select OrderNO, PartCode, Quantity from table a
join (select ID, MAX(DateEntered) from table group by OrderNO) b on a.ID = b.ID
bencobb
sumber
Anda tidak dapat memilih ID di tabel bagian dalam jika Anda mengelompokkan berdasarkan No. Pesanan
Jacob
@Dems terima kasih @ cularis ya, ini mengacu pada MySql, pertanyaannya tidak menentukan mesin database apa
bencobb
Jika Anda memposting kode, XML, atau sampel data, harap sorot baris tersebut di editor teks dan klik tombol "sampel kode" ( { }) pada bilah alat editor untuk memformat dan menyorotnya dengan baik!
marc_s
Ini MSSQL, maaf untuk itu.
GEMI
1

Dan Anda juga dapat menggunakan pernyataan pilih itu sebagai kueri penggabung kiri ... Contoh:

... left join (select OrderNO,
   PartCode,
   Quantity from (select OrderNO,
         PartCode,
         Quantity,
         row_number() over(partition by OrderNO order by DateEntered desc) as rn
  from YourTable) as T where rn = 1 ) RESULT on ....

Semoga ini membantu seseorang yang mencari ini :)

idzi
sumber
1

rownumber () over (...) berfungsi tetapi saya tidak menyukai solusi ini karena 2 alasan. - Fungsi ini tidak tersedia ketika Anda menggunakan versi SQL yang lebih lama seperti SQL2000 - Ketergantungan pada fungsi dan tidak benar-benar dapat dibaca.

Solusi lainnya adalah:

SELECT tmpall.[OrderNO] ,
       tmpall.[PartCode] ,
       tmpall.[Quantity] ,
FROM   (SELECT [OrderNO],
               [PartCode],
               [Quantity],
               [DateEntered]
        FROM   you_table) AS tmpall
       INNER JOIN (SELECT [OrderNO],
                          Max([DateEntered]) AS _max_date
                   FROM   your_table
                   GROUP  BY OrderNO ) AS tmplast
               ON tmpall.[OrderNO] = tmplast.[OrderNO]
                  AND tmpall.[DateEntered] = tmplast._max_date
Navid Golforoushan
sumber
1

Jika Anda telah mengindeks ID dan OrderNo, Anda dapat menggunakan IN: (Saya benci kesederhanaan perdagangan karena ketidakjelasan, hanya untuk menghemat beberapa siklus):

select * from myTab where ID in(select max(ID) from myTab group by OrderNo);
MortenB
sumber
0

Cobalah untuk menghindari DALAM penggunaan JOIN

SELECT SQL_CALC_FOUND_ROWS *  FROM (SELECT  msisdn, callid, Change_color, play_file_name, date_played FROM insert_log
   WHERE play_file_name NOT IN('Prompt1','Conclusion_Prompt_1','silent')
 ORDER BY callid ASC) t1 JOIN (SELECT MAX(date_played) AS date_played FROM insert_log GROUP BY callid) t2 ON t1.date_played=t2.date_played
raja neo
sumber
1
Mengapa menghindari IN? Apakah Anda memiliki argumen untuk mendukung pendapat Anda?
Preza8
ini akan memakan waktu lama untuk mengeksekusi kueri Anda. Anda dapat membaca artikel berikut xaprb.com/blog/2006/06/28/why-large-in-clauses-are-problematic
king neo
@anik Ini adalah artikel dari tahun 2006. Apakah Anda memiliki bukti terbaru tentang apa yang Anda katakan?
Félix Gagnon-Grenier
0

Ini bekerja untuk saya dengan sangat baik.

    select name, orderno from (
         select name, orderno, row_number() over(partition by 
           orderno order by created_date desc) as rn from orders
    ) O where rn =1;
Shubhankar Sarkar
sumber
-1

Ini berhasil untuk saya. gunakan MAX (CONVERT (date, ReportDate)) untuk memastikan Anda memiliki nilai tanggal

select max( CONVERT(date, ReportDate)) FROM [TraxHistory]
pengguna2662006
sumber