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.