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 была возвращена только одна строка.

Спасибо!


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)

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

Вы не можете выбрать идентификатор во внутренней таблице, если группируете по номеру заказа
Джейкоб,

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

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]
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.