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 и Quantity, но только для последнего зарегистрированного заказа.

Из таблицы примеров я бы хотел получить следующую информацию:

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

Обратите внимание, что для заказа 9999 была возвращена только одна строка.

Спасибо!

GEMI
источник
2
Из вашего комментария перейдите к ответу ROW_NUMBER (). Он может выглядеть длиннее, но, по моему опыту, самый быстрый с соответствующими индексами.
MatBailie
Спасибо, Демс, я ценю ваши усилия.
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
Спасибо, Микаэль Эрикссон, это отличный запрос!
GEMI
56

Лучший способ - это Микаэль Эрикссон, если ROW_NUMBER()он вам доступен.

Следующий лучший вариант - присоединиться к запросу, согласно ответу Кулариса.

В качестве альтернативы, наиболее простой и понятный способ - это коррелированный подзапрос в предложении 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)
MatBailie
источник
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 предложил индекс, который вдвое сократил время выполнения. Спасибо!
пасха,
Быстро и просто. Спасибо.
Стивен Цзэн
У меня 100 тыс. Строк, и для меня запрос Микаэля Эрикссона в 3 раза быстрее. Может быть, это потому, что у меня есть функция ROUND в разделе по пункту.
Wachburn
Если у вас есть поле даты с одинаковым значением (15.04.2017) для двух разных идентификаторов, оно вернет 2 строки ...
Portekoi
Да, Portekoi, это правда, но без какого-либо другого способа различить две строки, как вы можете выбрать одну из них? Вы можете поместить результат в ТОП, но как узнать, что это не другая строка, которую вы хотите?
тон
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)

Внутренний запрос выбирает все 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
Bencobb
источник
Вы не можете выбрать идентификатор во внутренней таблице, если группируете по номеру заказа
Джейкоб,
@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 (...) работает, но мне не понравилось это решение по двум причинам. - Эта функция недоступна, если вы используете старую версию 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

Если у вас есть проиндексированный идентификатор и номер заказа, вы можете использовать IN: (я ненавижу простоту торговли из-за неясности, просто для экономии некоторых циклов):

select * from myTab where ID in(select max(ID) from myTab group by OrderNo);
MortenB
источник
0

Старайтесь избегать использования IN 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 08
на выполнение вашего запроса уйдет много времени. Вы можете прочитать следующую статью xaprb.com/blog/2006/06/28/why-large-in-clauses-are-problematic
king neo
@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 (date, ReportDate)), чтобы убедиться, что у вас есть значение даты

select max( CONVERT(date, ReportDate)) FROM [TraxHistory]
user2662006
источник