SQL Server: Chỉ CHỌN các hàng có MAX (DATE)


109

Tôi có một bảng dữ liệu (db là 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

Tôi muốn thực hiện một truy vấn trả về OrderNO, PartCode và Quantity, nhưng chỉ cho đơn hàng đã đăng ký cuối cùng.

Từ bảng ví dụ, tôi muốn lấy lại thông tin sau:

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

Lưu ý rằng chỉ có một dòng được trả lại cho đơn hàng 9999.

Cảm ơn!


2
Từ nhận xét của bạn, hãy xem câu trả lời ROW_NUMBER (). Nó có thể trông dài hơn, nhưng theo kinh nghiệm của tôi, nó là nhanh nhất với các chỉ mục thích hợp.
MatBailie

Cảm ơn Dems, tôi đánh giá cao nỗ lực của bạn.
GEMI

1
@GEMI chỉ vì tò mò, Không MAX(DATE)trả lại một dòng cho đơn đặt hàng 9999?
Zameer

Có, nhưng tôi muốn mỗi đơn hàng khác nhau chỉ trả về dòng đơn hàng cuối cùng.
GEMI

Câu trả lời:


184

Nếu rownumber() over(...)có sẵn cho bạn ....

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
Cảm ơn Mikael Eriksson, đây là một câu hỏi tuyệt vời!
GEMI

56

Cách tốt nhất là Mikael Eriksson, nếu ROW_NUMBER()có sẵn cho bạn.

Cách tốt nhất tiếp theo là tham gia vào một truy vấn, theo câu trả lời của Cularis.

Ngoài ra, cách đơn giản và dễ hiểu nhất là truy vấn phụ tương quan trong mệnh đề WHERE.

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

Hoặc là...

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

Đây là truy vấn nhanh nhất trong số các truy vấn được cung cấp ở trên. Chi phí truy vấn là 0,0070668.

Câu trả lời ưa thích ở trên, của Mikael Eriksson, có chi phí truy vấn là 0,0146625

Bạn có thể không quan tâm đến hiệu suất cho một mẫu nhỏ như vậy, nhưng trong các truy vấn lớn, tất cả đều cộng lại.


2
Điều này hóa ra với tôi nhanh hơn một chút so với các giải pháp khác ở đây trên tập dữ liệu ~ 3,5 triệu hàng, tuy nhiên SSMS đã đề xuất một chỉ mục giúp giảm thời gian thực thi xuống một nửa. Cảm ơn!
lễ phục sinh

Nhanh chóng và đơn giản. Cảm ơn.
Stephen Zeng,

Tôi có 100k hàng và đối với tôi truy vấn của Mikael Eriksson nhanh hơn 3 lần. Có thể là do tôi có hàm ROUND trong phân vùng theo mệnh đề.
Wachburn

Nếu bạn có một trường ngày với cùng một giá trị (2017/04/15) trong 2 differents ID, nó sẽ trở lại 2 hàng ...
Portekoi

Đúng vậy Portekoi, điều đó đúng, nhưng nếu không có bất kỳ cách nào khác để phân biệt hai hàng, làm thế nào bạn có thể chọn hàng này trên hàng kia? Bạn có thể đặt TOP trên kết quả, nhưng làm thế nào để bạn biết rằng nó không phải là hàng khác mà bạn muốn?
tone

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)

Truy vấn bên trong chọn tất cả OrderNovới ngày tối đa của chúng. Để lấy các cột khác của bảng, bạn có thể nối chúng trên OrderNoMaxDate.


1

Đối với MySql, bạn có thể làm như sau:

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

Bạn không thể chọn ID trong bảng bên trong nếu bạn nhóm theo Số thứ tự
Jacob

@Dems nhờ @ cularis vâng, đây là đề cập đến MySql, câu hỏi không nói rõ những gì cơ sở dữ liệu
bencobb

Nếu bạn đăng các mẫu mã, XML hoặc dữ liệu, vui lòng đánh dấu các dòng đó trong trình soạn thảo văn bản và nhấp vào nút "mẫu mã" ( { }) trên thanh công cụ của trình soạn thảo để định dạng độc đáo và đánh dấu cú pháp!
marc_s

Đây là MSSQL, xin lỗi vì điều đó.
GEMI

1

Và bạn cũng có thể sử dụng câu lệnh select đó làm truy vấn nối trái ... Ví dụ:

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

Hy vọng điều này sẽ giúp ai đó tìm kiếm điều này :)


1

rownumber () over (...) đang hoạt động nhưng tôi không thích giải pháp này vì 2 lý do. - Chức năng này không khả dụng khi bạn sử dụng phiên bản SQL cũ hơn như SQL2000 - Phụ thuộc vào chức năng và không thực sự đọc được.

Một giải pháp khác là:

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

Nếu bạn đã lập chỉ mục ID và OrderNo, Bạn có thể sử dụng IN: (Tôi ghét giao dịch đơn giản vì không rõ ràng, chỉ để tiết kiệm một số chu kỳ):

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

0

Cố gắng tránh IN sử dụng THAM GIA

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
Tại sao tránh IN? Bạn có lập luận nào để ủng hộ ý kiến ​​của mình không?
Preza

sẽ mất nhiều thời gian để thực hiện truy vấn của bạn. Bạn có thể đọc bài viết sau xaprb.com/blog/2006/06/28/why-large-in-clauses-are-problematic
king neo

@anik Đây là một bài báo từ năm 2006. Bạn có bất kỳ bằng chứng nào gần đây về những gì bạn đang nói không?
Félix Gagnon-Grenier

0

Điều này làm việc cho tôi hoàn toàn tố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

Điều này làm việc cho tôi. sử dụng MAX (CONVERT (ngày, Ngày báo cáo)) để đảm bảo bạn có giá trị ngày tháng

select max( CONVERT(date, ReportDate)) FROM [TraxHistory]
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.