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.