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.