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!


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      

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)

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.


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.


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

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


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

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

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

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;

-1

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

select max( CONVERT(date, ReportDate)) FROM [TraxHistory]
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.