SQL Server: เลือกเฉพาะแถวที่มี MAX (DATE)


109

ฉันมีตารางข้อมูล (ฐานข้อมูลคือ 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

ฉันต้องการสร้างแบบสอบถามที่ส่งคืน OrderNO, PartCode และ Quantity แต่สำหรับคำสั่งซื้อที่ลงทะเบียนล่าสุดเท่านั้น

จากตารางตัวอย่างฉันต้องการกลับข้อมูลต่อไปนี้:

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

สังเกตว่ามีการส่งคืนคำสั่ง 9999 เพียงบรรทัดเดียว

ขอบคุณ!


2
จากความคิดเห็นของคุณไปที่คำตอบ ROW_NUMBER () มันอาจจะดูยาวกว่า แต่จากประสบการณ์ของฉันเร็วที่สุดมากด้วยดัชนีที่เหมาะสม
MatBailie

ขอบคุณ Dems ขอขอบคุณในความพยายามของคุณ
GEMI

1
@GEMI เพียงแค่อยากรู้อยากเห็นไม่MAX(DATE)ส่งคืนหนึ่งบรรทัดสำหรับคำสั่ง 9999?
Zameer Ansari

ใช่ แต่ฉันต้องการให้คำสั่งซื้อที่แตกต่างกันแต่ละรายการส่งคืนเฉพาะบรรทัดคำสั่งสุดท้าย
GEMI

คำตอบ:


184

ถ้าrownumber() over(...)มีให้นะ ....

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
ขอบคุณ Mikael Eriksson นี่เป็นคำถามที่ยอดเยี่ยม!
GEMI

56

วิธีที่ดีที่สุดคือ Mikael Eriksson ถ้า ROW_NUMBER()มีให้คุณ

สิ่งที่ดีที่สุดต่อไปคือการเข้าร่วมแบบสอบถามตามคำตอบของ Cularis

อีกวิธีหนึ่งวิธีที่ง่ายและตรงไปตรงมาที่สุดคือแบบสอบถามย่อยที่สัมพันธ์กันในส่วนคำสั่ง WHERE

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

หรือ...

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

นี่เป็นคำค้นหาที่เร็วที่สุดในบรรดาข้อความค้นหาที่ระบุข้างต้น ค่าใช้จ่ายในการสืบค้นอยู่ที่ 0.0070668

คำตอบที่ต้องการข้างต้นโดย Mikael Eriksson มีค่าใช้จ่ายในการสืบค้น 0.0146625

คุณอาจไม่สนใจเกี่ยวกับประสิทธิภาพของตัวอย่างขนาดเล็กเช่นนี้ แต่ในการค้นหาขนาดใหญ่ทุกอย่างจะรวมเข้าด้วยกัน


2
สิ่งนี้ทำให้ฉันเร็วกว่าโซลูชันอื่นเล็กน้อยที่นี่ในชุดข้อมูลแถว ~ 3.5M อย่างไรก็ตาม SSMS แนะนำดัชนีที่ลดเวลาในการดำเนินการลงครึ่งหนึ่ง ขอบคุณ!
easuter

รวดเร็วและตรงไปตรงมา ขอบคุณ.
Stephen Zeng

ฉันมี 100,000 แถวและสำหรับฉันการสืบค้นของ Mikael Eriksson เร็วขึ้น 3 เท่า อาจเป็นเพราะฉันมีฟังก์ชัน ROUND ในพาร์ติชันโดยอนุประโยค
Wachburn

หากคุณมีฟิลด์วันที่ที่มีค่าเท่ากัน (04/15/2017) สำหรับ 2 ID ที่แตกต่างกันระบบจะส่งคืน 2 แถว ...
Portekoi

ใช่ Portekoi นั่นเป็นเรื่องจริง แต่หากไม่มีวิธีอื่นใดในการแยกความแตกต่างของสองแถวคุณจะเลือกแถวใดแถวหนึ่งทับกันได้อย่างไร? คุณสามารถใส่ TOP ไว้ในผลลัพธ์ได้ แต่คุณจะรู้ได้อย่างไรว่ามันไม่ใช่แถวอื่นที่คุณต้องการ?
โทน

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)

คำค้นหาภายในจะเลือกทั้งหมดที่OrderNoมีวันที่สูงสุด ที่จะได้รับคอลัมน์อื่น ๆ ของตารางคุณสามารถเข้าร่วมพวกเขาบนและOrderNoMaxDate


1

สำหรับ MySql คุณสามารถทำสิ่งต่อไปนี้:

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

คุณไม่สามารถเลือก ID ในตารางด้านในได้หากคุณจัดกลุ่มตาม Order No.
Jacob

@Dems ขอบคุณ @ cularis ใช่นี่หมายถึง MySql คำถามไม่ได้ระบุเครื่องมือฐานข้อมูลอะไร
bencobb

หากคุณโพสต์โค้ด XML หรือตัวอย่างข้อมูลโปรดเน้นบรรทัดเหล่านั้นในโปรแกรมแก้ไขข้อความและคลิกที่ปุ่ม "ตัวอย่างโค้ด" ( { }) บนแถบเครื่องมือแก้ไขเพื่อจัดรูปแบบและไวยากรณ์ให้สวยงาม
marc_s

นี่คือ MSSQL ขออภัยด้วย
GEMI

1

และคุณยังสามารถใช้คำสั่งที่เลือกเป็นคิวรีเข้าร่วมทางซ้าย ... ตัวอย่าง:

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

หวังว่านี่จะช่วยคนที่ค้นหาสิ่งนี้ :)


1

rownumber () over (... ) ใช้งานได้ แต่ฉันไม่ชอบโซลูชันนี้ด้วยเหตุผล 2 ประการ - ฟังก์ชันนี้ใช้ไม่ได้เมื่อคุณใช้ SQL เวอร์ชันเก่าเช่น SQL2000 - การพึ่งพาฟังก์ชันและไม่สามารถอ่านได้จริง

อีกวิธีหนึ่งคือ:

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 และ OrderNo คุณสามารถใช้ IN: (ฉันเกลียดความเรียบง่ายในการซื้อขายเพื่อความสับสนเพียงเพื่อประหยัดบางรอบ):

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

0

พยายามหลีกเลี่ยงการใช้ 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
ทำไมต้องหลีกเลี่ยง IN? คุณมีข้อโต้แย้งที่จะสนับสนุนความคิดเห็นของคุณหรือไม่?
Preza8

จะใช้เวลานานในการดำเนินการค้นหาของคุณ คุณสามารถอ่านบทความต่อไปนี้xaprb.com/blog/2006/06/28/why-large-in-clauses-are-problematic
king neo

@anik นี่คือบทความจากปี 2006 คุณมีหลักฐานล่าสุดเกี่ยวกับสิ่งที่คุณกำลังพูดหรือไม่?
Félix Gagnon-Grenier


-1

สิ่งนี้ใช้ได้กับฉัน ใช้MAX (CONVERT (วันที่, วันที่รายงาน))เพื่อให้แน่ใจว่าคุณมีค่าวันที่

select max( CONVERT(date, ReportDate)) FROM [TraxHistory]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.