SQL Server: sélectionnez uniquement les lignes avec MAX (DATE)


109

J'ai une table de données (la base de données est 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

Je voudrais faire une requête qui renvoie OrderNO, PartCode et Quantity, mais uniquement pour la dernière commande enregistrée.

À partir du tableau d'exemple, je voudrais récupérer les informations suivantes:

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

Notez qu'une seule ligne a été renvoyée pour la commande 9999.

Merci!


2
À partir de votre commentaire, accédez à la réponse ROW_NUMBER (). Cela peut sembler plus long, mais c'est, d'après mon expérience, beaucoup plus rapide avec des index appropriés.
— MatBailie

Merci Dems, j'apprécie vos efforts.
— GEMI

1
@GEMI juste par curiosité, ne MAX(DATE)retourne pas une ligne pour la commande 9999?
— Zameer Ansari

Oui, mais je voulais que chaque commande différente ne renvoie que la dernière ligne de commande.
— GEMI

Réponses:


184

Si rownumber() over(...)est disponible pour vous ....

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
Merci Mikael Eriksson, c'est une requête géniale!
— GEMI

56

Le meilleur moyen est Mikael Eriksson, s'il ROW_NUMBER()est à votre disposition.

Le mieux est de se joindre à une requête, selon la réponse de Cularis.

Alternativement, le moyen le plus simple et direct est une sous-requête corrélée dans la clause WHERE.

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

Ou...

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

C'est la plus rapide de toutes les requêtes fournies ci-dessus. Le coût de la requête est de 0,0070668.

La réponse préférée ci-dessus, par Mikael Eriksson, a un coût de requête de 0,0146625

Vous ne vous souciez peut-être pas des performances d'un si petit échantillon, mais dans les requêtes volumineuses, tout s'additionne.


2
Cela s'est avéré légèrement plus rapide que les autres solutions ici sur un ensemble de données de lignes d'environ 3,5 millions, mais SSMS a suggéré un index qui réduit de moitié le temps d'exécution. Merci!
— facile

Rapide et simple. Merci.
— Stephen Zeng

J'ai 100k lignes et pour moi la requête de Mikael Eriksson 3 fois plus vite. C'est peut-être parce que j'ai la fonction ROUND dans la partition par clause.
— Wachburn

Si vous avez un champ de date avec la même valeur (15/04/2017) pour 2 ID différents, il renverra 2 lignes ...
— Portekoi

Oui Portekoi, c'est vrai, mais sans autre moyen de différencier les deux rangées, comment pouvez-vous sélectionner l'une sur l'autre? Vous pourriez mettre un TOP sur le résultat, mais comment savez-vous que ce n'est pas l'autre ligne que vous voulez?
— ton

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)

La requête interne sélectionne tous OrderNoavec leur date maximale. Pour obtenir les autres colonnes de la table, vous pouvez les joindre sur OrderNoet le MaxDate.


1

Pour MySql, vous pouvez faire quelque chose comme ceci:

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

Vous ne pouvez pas sélectionner l'ID dans le tableau intérieur si vous groupez par numéro de commande
— Jacob

@Dems merci @ cularis oui, cela fait référence à MySql, la question ne spécifiait pas quel moteur de base de données
— bencobb

Si vous postez des échantillons de code, XML ou de données, veuillez mettre en évidence ces lignes dans l'éditeur de texte et cliquer sur le bouton "exemples de code" ( { }) dans la barre d'outils de l'éditeur pour bien le formater et la mettre en évidence!
— marc_s

C'est MSSQL, désolé pour ça.
— GEMI

1

Et vous pouvez également utiliser cette instruction select comme requête de jointure gauche ... Exemple:

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

J'espère que cela aidera quelqu'un qui cherche cela :)


1

rownumber () over (...) fonctionne mais je n'ai pas aimé cette solution pour 2 raisons. - Cette fonction n'est pas disponible lorsque vous utilisez une ancienne version de SQL comme SQL2000 - Dépendance à la fonction et n'est pas vraiment lisible.

Une autre solution est:

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

Si vous avez indexé ID et OrderNo, vous pouvez utiliser IN: (Je déteste le trading de la simplicité pour l'obscurité, juste pour économiser quelques cycles):

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

0

Essayez d'éviter d'utiliser 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
Pourquoi éviter IN? Avez-vous des arguments pour soutenir votre opinion?
— Preza8

cela prendra beaucoup de temps pour exécuter votre requête. Vous pouvez lire l'article suivant xaprb.com/blog/2006/06/28/why-large-in-clauses-are-problematic
— king neo

@anik Ceci est un article de 2006. Avez-vous des preuves récentes de ce que vous dites?
— Félix Gagnon-Grenier

0

Cela a parfaitement fonctionné pour moi.

    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

Cela fonctionne pour moi. utilisez MAX (CONVERT (date, ReportDate)) pour vous assurer que vous avez une valeur de date

select max( CONVERT(date, ReportDate)) FROM [TraxHistory]
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.