SQL Server: виберіть лише рядки з MAX (DATE)


109

У мене є таблиця даних (db - 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 та Кількість, але лише для останнього зареєстрованого замовлення.

З таблиці прикладу я хочу повернути таку інформацію:

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

Зауважте, що для замовлення 9999 повернуто лише один рядок.

Дякую!


2
З коментаря перейдіть з ROW_NUMBER () відповіддю. Це може виглядати довше, але, на мій досвід, набагато швидше з відповідними показниками.
MatBailie

Дякую Демс, я ціную ваші зусилля.
GEMI

1
@GEMI просто з цікавості, не MAX(DATE)повертає один рядок для замовлення 9999?
Замер

Так, але я хотів, щоб кожне інше замовлення повертало лише останній рядок порядку.
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
Дякую Мікаелю Ерікссону, це надзвичайний запит!
GEMI

56

Найкращий спосіб - Мікаел Ерікссон, якщо вам 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.

Уподобаний вище відповідь, Мікаел Ерікссон, має вартість запиту 0,0146625

Ви можете не турбуватися про ефективність для такого невеликого зразка, але у великих запитах все це складає.


2
Це виявилося для мене незначно швидше, ніж інші рішення тут на наборі даних ~ 3,5 М рядків, однак SSMS запропонував індекс, що скоротить час виконання наполовину. Дякую!
східний

Швидкий і прямий. Дякую.
Стівен Зенг

У мене 100k рядків, і для мене запит Mikael Eriksson в 3 рази швидше. Можливо, це тому, що у мене функція ROUND в розділі за пунктом.
Вахберн

Якщо у вас є поле дати з однаковим значенням (15.04.2017) для 2-х ідентифікаторів, воно поверне 2 ряди ...
Portekoi

Так, Портекой, це правда, але без іншого способу розмежувати два ряди, як можна вибрати один над іншим? Ви можете поставити ТОП за результатом, але як ви знаєте, що це не інший рядок, який ви хочете?
тон

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із їх максимальною датою. Щоб отримати інші стовпці таблиці, ви можете приєднати їх до OrderNoта MaxDate.


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

Ви не можете вибрати ідентифікатор у внутрішній таблиці, якщо ви згрупуєтесь за наказом №
Яків

@ Дем спасибі @ cularis так, це стосується MySql, у питанні не було вказано, який двигун бази даних
bencobb

Якщо ви розміщуєте код, XML або зразки даних, будь-ласка, виділіть ці рядки в текстовому редакторі та натисніть кнопку «зразки коду» ( { }) на панелі інструментів редактора, щоб гарненько відформатувати та виділити синтаксис!
marc_s

Це MSSQL, вибачте за це.
GEMI

1

І ви також можете використовувати цей оператор select як запит на приєднання зліва ... Приклад:

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

Якщо у вас є індексований ідентифікатор та OrderNo, ви можете використовувати IN: (Я ненавиджу простоту торгівлі для неясності, просто щоб зберегти кілька циклів):

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

0

Постарайтеся уникати використання ПРИЄДНАЙТЕСЬ

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
короля нео

@anik Це стаття з 2006 року. Чи є у вас недавні докази того, що ви говорите?
Фелікс Ганьон-Греньє,

0

Це працювало для мене прекрасно.

    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

Це працює для мене. використовуйте MAX (CONVERT (дата, ReportDate)), щоб переконатися, що у вас є значення дати

select max( CONVERT(date, ReportDate)) FROM [TraxHistory]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.