Här är två tabeller.
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
PERSONER
PERSON_ID + NAME ================= 111222 ABC 222111 XYZ
Här är min orakelfråga.
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;
vilket ger dessa resultat
LAST_UPDATE SCHOOL_CODE PERSON_ID ===========+===========+========= 24-JAN-13 ABE 111222 09-FEB-12 ABE 222111
Jag vill välja den första för skolan som har senaste datum.
Tack.
Svar
Din nuvarande fråga ger inte önskat resultat eftersom du använder en GROUP BY
sats i kolumnen PERSON_ID
som har ett unikt värde för båda posterna. Som ett resultat kommer du att returnera båda raderna.
Det finns några sätt att lösa detta. Du kan använda en underfråga för att tillämpa den samlade funktionen för att returnera max(LAST_UPDATE_DATE_TIME)
för varje 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;
Eller så kan du använda en fönsterfunktion för att returnera dataraderna för varje skola med den senaste 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;
Denna fråga implementerar row_number()
som tilldelar varje rad ett unikt nummer i partitionen av SCHOOL_CODE
och placeras i en fallande ordning baserat på LAST_UPDATE_DATE_TIME
.
Som en sidoteckning är JOIN med aggregerad funktion inte exakt densamma som row_number()
-versionen. Om du har två rader med samma händelsetid kommer JOIN att returnera båda raderna, medan row_number()
bara returnerar en. Om du vill returnera båda med en fönsterfunktion kan du överväga att använda rank()
fönsterfunktionen istället eftersom den returnerar band:
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;
Se Demo
Kommentarer
- Tack, jag tycker den inre kopplingen till en undersökningstabell (exempel 1 ovan) för att vara den mest intuitiva .. och kräver inte ' mig att lära mig vad partition av är allt handla om. Här är en titt på en liknande syntax som exempel 1: välj oT.dateField, oT.siteID, oT.field1, oT.field2, oT.field3, från originalTable som oT inner join (välj max (dateField) som newestDate, siteID från originalTable group by siteID) som newTable på oT.siteID = newTable.site_ID och oT.dateField = newTable.newestDate order av oT.siteID asc För mig förklarar det bättre vad ' händer i underfrågan.
Svar
I ”Jag är förvånad över att ingen har utnyttjat fönsterfunktioner bortom radnummer ()
Här är lite data att spela med:
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");
OVER () sats skapar ett fönster för vilket du definierar dina aggregerade grupper. I det här fallet partitionerar jag bara på SHOOL_CODE, så vi kommer att se FIRST_VALUE, som kommer från LAST_UPDATE_DATE_TIME, grupperad efter SCHOOL_CODE, och i ordningen LAST_UPDATE_DATE_TIME efter fallande ordning. Detta värde kommer att tillämpas på hela kolumnen för varje SCHOOL_CODE.
Det är viktigt att vara noga med din partitionering och beställning i över () klausulen.
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
Returnerar:
24-JAN-13 ABE 111222
Detta bör eliminera ditt behov av GROUP BY och underfrågor för det mesta. Du vill dock se till att ta med DISTINCT dock.
Kommentarer
- Det här är trevligt, men finns det ett sätt att undvika att upprepa överklausulen för alla kolumner?
Svar
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)
Kommentarer
- I stället för att posta bara kod bör du försöka förklara hur detta svarar på frågan; och eventuellt vad OP gjorde felaktigt.