SQL Server: Yalnızca MAX (DATE) olan satırları SEÇİN


109

Bir veri tablom var (db MSSQL'dir):

    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

SiparişNO, Parça Kodu ve Miktar döndüren bir sorgu yapmak istiyorum, ancak yalnızca son kayıtlı sipariş için.

Örnek tablodan aşağıdaki bilgileri geri almak istiyorum:

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

9999 siparişi için yalnızca bir satırın iade edildiğine dikkat edin.

Teşekkürler!


2
Yorumunuzdan ROW_NUMBER () yanıtı verin. Daha uzun görünebilir, ancak benim deneyimime göre uygun dizinlerle en hızlısıdır.
MatBailie

Teşekkürler Dems, çabanı takdir ediyorum.
GEMI

1
@GEMI sadece meraktan, MAX(DATE)9999 siparişi için bir satır döndürmüyor mu ?
Zameer Ansari

Evet, ancak her farklı siparişin yalnızca son sipariş satırını döndürmesini istedim.
GEMI

Yanıtlar:


185

Eğer rownumber() over(...).... sizin için kullanılabilir

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
Teşekkürler Mikael Eriksson, bu harika bir sorgu!
GEMI

57

En iyi yol, eğer ROW_NUMBER()sizin için uygunsa Mikael Eriksson .

Bir sonraki en iyisi, Cularis'in cevabına göre bir sorguya katılmaktır.

Alternatif olarak, en basit ve doğrudan yol, WHERE cümlesindeki ilişkili bir alt sorgudur.

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

Veya...

WHERE
  ID = (SELECT TOP 1 ID FROM yourTable WHERE orderNo = [data].orderNo ORDER BY DateEntered DESC)

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

Bu, yukarıda verilen tüm sorguların en hızlısıdır. Sorgu maliyeti 0,0070668 olarak geldi.

Yukarıda tercih edilen yanıt, Mikael Eriksson tarafından 0,0146625 sorgu maliyetine sahiptir.

Bu kadar küçük bir örneklemin performansını önemsemeyebilirsiniz, ancak büyük sorgularda hepsi toplanır.


2
Bu bana ~ 3.5M satır veri kümesindeki diğer çözümlerden biraz daha hızlı çıktı, ancak SSMS, yürütme süresini yarıya indiren bir dizin önerdi. Teşekkürler!
easuter

Hızlı ve anlaşılır. Teşekkürler.
Stephen Zeng

100.000 satırım var ve benim için Mikael Eriksson'un sorgusu 3 kat daha hızlı. Belki de YUVARLAK işlevine sahip olduğum içindir.
Wachburn

2 farklı kimlik için aynı değere (04/15/2017) sahip bir tarih alanınız varsa, 2 satır döndürür ...
Portekoi

Evet Portekoi, bu doğru, ancak iki satırı ayırt etmenin başka bir yolu olmadan birini diğerine nasıl seçebilirsin? Sonuca bir TOP koyabilirsiniz, ancak bunun istediğiniz diğer satır olmadığını nasıl anlarsınız?
tonu

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

İç sorgu OrderNo, maksimum tarihiyle tümünü seçer . Tablonun diğer sütunlarını almak için bunlara OrderNove MaxDate.


1

MySql için aşağıdaki gibi bir şey yapabilirsiniz:

select OrderNO, PartCode, Quantity from table a
join (select ID, MAX(DateEntered) from table group by OrderNO) b on a.ID = b.ID

Sipariş No.'ya göre gruplandırırsanız iç tablodan kimlik seçemezsiniz
Jacob

@Dems teşekkürler @ cularis evet, bu MySql'e atıfta bulunuyor, soru hangi veritabanı motorunu belirtmedi
bencobb

Kod, XML veya veri örnekleri gönderiyorsanız, lütfen metin düzenleyicide bu satırları vurgulayın ve { }güzel bir şekilde biçimlendirmek ve sözdizimi vurgulamak için düzenleyici araç çubuğundaki "kod örnekleri" düğmesini ( ) tıklayın !
marc_s

Bu MSSQL, bunun için üzgünüm.
GEMI

1

Ve bu select deyimini sol birleştirme sorgusu olarak da kullanabilirsiniz ... Örnek:

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

Umarım bu, bunu arayan birine yardımcı olur :)


1

rownumber () over (...) çalışıyor ancak bu çözümü 2 nedenden dolayı beğenmedim. - Bu işlev, SQL2000 gibi eski SQL sürümünü kullandığınızda kullanılamaz - İşleve bağlılık ve gerçekten okunamaz.

Başka bir çözüm şudur:

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

ID ve OrderNo'yu indekslediyseniz IN'i kullanabilirsiniz: (Sadece bazı döngüleri kurtarmak için belirsizlik için ticaret basitliğinden nefret ediyorum):

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

0

KULLANIMDA KULLANMAYIN 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
Neden IN'den kaçının? Fikrinizi destekleyecek herhangi bir iddianız var mı?
Preza8

sorgunuzu yürütmek uzun zaman alacak. Aşağıdaki makaleyi okuyabilirsiniz xaprb.com/blog/2006/06/28/why-large-in-clauses-are-problematic
king neo

@anik Bu 2006'dan bir makale. Söylediklerinize dair yeni bir kanıtınız var mı?
Félix Gagnon-Grenier

0

Bu benim için gayet iyi çalıştı.

    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

Bu benim için çalışıyor. tarih değerine sahip olduğunuzdan emin olmak için MAX (CONVERT (tarih, RaporTarihi)) kullanın

select max( CONVERT(date, ReportDate)) FROM [TraxHistory]
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.