Sélectionnez la date maximale ou la dernière date

Voici deux tableaux.

SCHOOL_STAFF

SCHOOL_CODE + STAFF_TYPE_NAME + LAST_UPDATE_DATE_TIME + PERSON_ID ================================================================= ABE Principal 24-JAN-13 111222 ABE Principal 09-FEB-12 222111 

PERSONNES

PERSON_ID + NAME ================= 111222 ABC 222111 XYZ 

Voici ma requête oracle.

SELECT MAX(LAST_UPDATE_DATE_TIME) AS LAST_UPDATE, SCHOOL_CODE, PERSON_ID FROM SCHOOL_STAFF WHERE STAFF_TYPE_NAME="Principal" GROUP BY SCHOOL_CODE, PERSON_ID ORDER BY SCHOOL_CODE; 

qui donne ces résultats

LAST_UPDATE SCHOOL_CODE PERSON_ID ===========+===========+========= 24-JAN-13 ABE 111222 09-FEB-12 ABE 222111 

Je veux sélectionner le premier pour lécole qui a la dernière date.

Merci.

Réponse

Votre requête actuelle ne donne pas le résultat souhaité car vous utilisez un GROUP BY sur la colonne PERSON_ID qui a une valeur unique pour les deux entrées. En conséquence, vous retournerez les deux lignes.

Il existe plusieurs façons de résoudre ce problème. Vous pouvez utiliser une sous-requête pour appliquer la fonction dagrégation afin de renvoyer le max(LAST_UPDATE_DATE_TIME) pour chaque SCHOOL_CODE:

select s1.LAST_UPDATE_DATE_TIME, s1.SCHOOL_CODE, s1.PERSON_ID from SCHOOL_STAFF s1 inner join ( select max(LAST_UPDATE_DATE_TIME) LAST_UPDATE_DATE_TIME, SCHOOL_CODE from SCHOOL_STAFF group by SCHOOL_CODE ) s2 on s1.SCHOOL_CODE = s2.SCHOOL_CODE and s1.LAST_UPDATE_DATE_TIME = s2.LAST_UPDATE_DATE_TIME; 

Voir Violon SQL avec démo

Ou vous pouvez utiliser un fonction de fenêtrage pour renvoyer les lignes de données pour chaque école avec le plus récent LAST_UPDATE_DATE_TIME:

select SCHOOL_CODE, PERSON_ID, LAST_UPDATE_DATE_TIME from ( select SCHOOL_CODE, PERSON_ID, LAST_UPDATE_DATE_TIME, row_number() over(partition by SCHOOL_CODE order by LAST_UPDATE_DATE_TIME desc) seq from SCHOOL_STAFF where STAFF_TYPE_NAME="Principal" ) d where seq = 1; 

Voir Violon SQL avec démo

Cette requête implémente row_number() qui attribue un numéro unique à chaque ligne de la partition de SCHOOL_CODE et placé dans un ordre décroissant basé sur le LAST_UPDATE_DATE_TIME.

En remarque, la fonction JOIN avec agrégation nest pas exactement la même que la version row_number(). Si vous avez deux lignes avec la même heure dévénement, JOIN renverra les deux lignes, tandis que row_number() nen renverra quune. Si vous souhaitez renvoyer les deux avec une fonction de fenêtrage, envisagez dutiliser la fonction de fenêtrage rank() car elle renverra des liens:

select SCHOOL_CODE, PERSON_ID, LAST_UPDATE_DATE_TIME from ( select SCHOOL_CODE, PERSON_ID, LAST_UPDATE_DATE_TIME, rank() over(partition by SCHOOL_CODE order by LAST_UPDATE_DATE_TIME desc) seq from SCHOOL_STAFF where STAFF_TYPE_NAME="Principal" ) d where seq = 1; 

Voir Démo

Commentaires

  • Merci, je trouve la jointure interne à une table de sous-requête (exemple 1 ci-dessus) pour être la plus intuitive .. et ne ' ne moblige pas à apprendre ce que partition par est tout à propos de. Voici un aperçu dune syntaxe similaire à lexemple 1: sélectionnez oT.dateField, oT.siteID, oT.field1, oT.field2, oT.field3, de originalTable comme oT jointure interne (sélectionnez max (dateField) comme date la plus récente, siteID du groupe originalTable par siteID) comme newTable sur oT.siteID = newTable.site_ID et oT.dateField = newTable.newestDate order by oT.siteID asc Pour moi, cela explique mieux ce qui ' se passe dans la sous-requête.

Réponse

I « Je suis surpris que personne nait profité des fonctions de fenêtre au-delà de row_number ()

Voici quelques données avec lesquelles jouer:

CREATE TABLE SCHOOL_STAFF ( LAST_UPDATE_DATE_TIME VARCHAR(20), SCHOOL_CODE VARCHAR(20), PERSON_ID VARCHAR(20), STAFF_TYPE_NAME VARCHAR(20) ); INSERT INTO SCHOOL_STAFF VALUES ("24-JAN-13", "ABE", "111222", "Principal"); INSERT INTO SCHOOL_STAFF VALUES ("09-FEB-12", "ABE", "222111", "Principal"); 

Le La clause OVER () crée une fenêtre pour laquelle vous définissez vos groupes dagrégation. Dans ce cas, je partitionne uniquement sur le SHOOL_CODE, nous verrons donc le FIRST_VALUE, qui proviendra de LAST_UPDATE_DATE_TIME, regroupé par SCHOOL_CODE, et dans lordre de LAST_UPDATE_DATE_TIME par ordre décroissant. Cette valeur sera appliquée à la colonne entière pour chaque SCHOOL_CODE.

Il est important de porter une attention particulière à votre partitionnement et à votre ordre dans la clause over ().

SELECT DISTINCT FIRST_VALUE(LAST_UPDATE_DATE_TIME) OVER (PARTITION BY SCHOOL_CODE ORDER BY LAST_UPDATE_DATE_TIME DESC) AS LAST_UPDATE ,FIRST_VALUE(SCHOOL_CODE) OVER (PARTITION BY SCHOOL_CODE ORDER BY LAST_UPDATE_DATE_TIME DESC) AS SCHOOL_CODE ,FIRST_VALUE(PERSON_ID) OVER (PARTITION BY SCHOOL_CODE ORDER BY LAST_UPDATE_DATE_TIME DESC) AS PERSON_ID FROM SCHOOL_STAFF WHERE STAFF_TYPE_NAME = "Principal" ORDER BY SCHOOL_CODE 

Renvoie:

24-JAN-13 ABE 111222 

Cela devrait éliminer votre besoin de GROUP BY et de sous-requêtes pour la plupart. Vous voudrez cependant vous assurer dinclure DISTINCT.

Commentaires

  • Cest bien, mais y a-t-il un moyen déviter de répéter la clause over pour toutes les colonnes?

Réponse

select LAST_UPDATE_DATE_TIME as LAST_UPDATE, SCHOOL_CODE, PERSON_ID from SCHOOL_STAFF WHERE STAFF_TYPE_NAME="Principal" AND LAST_UPDATE_DATE_TIME = (SELECT MAX(LAST_UPDATE_DATE_TIME) FROM SCHOOL_STAFF s2 WHERE PERSON_ID = s2.PERSON_ID) 

Commentaires

  • Au lieu de poster juste du code, vous devriez essayer dexpliquer comment cela répond à la question; et potentiellement ce que le PO faisait incorrectement.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *