Oto dwie tabele.
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
OSOBY
PERSON_ID + NAME ================= 111222 ABC 222111 XYZ
Oto moje zapytanie wyroczni.
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;
co daje takie wyniki
LAST_UPDATE SCHOOL_CODE PERSON_ID ===========+===========+========= 24-JAN-13 ABE 111222 09-FEB-12 ABE 222111
Chcę wybrać pierwszą szkołę, która ma ostatnią datę.
Dziękuję.
Odpowiedź
Twoje obecne zapytanie nie daje oczekiwanych rezultatów, ponieważ używasz GROUP BY
w kolumnie PERSON_ID
, która ma unikalne wartości dla obu pozycji. W rezultacie zwrócisz oba wiersze.
Jest kilka sposobów rozwiązania tego problemu. Możesz użyć podzapytania, aby zastosować funkcję agregującą i zwrócić max(LAST_UPDATE_DATE_TIME)
dla każdego 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;
Zobacz SQL Fiddle with Demo
Możesz też użyć funkcja okienkowa , aby zwrócić wiersze danych dla każdej szkoły z najnowszymi 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;
Zobacz SQL Fiddle with Demo
To zapytanie implementuje row_number()
, który przypisuje niepowtarzalny numer do każdego wiersza w partycji SCHOOL_CODE
i jest umieszczony w porządku malejącym na podstawie LAST_UPDATE_DATE_TIME
.
Na marginesie, JOIN z funkcją agregującą nie jest dokładnie tym samym, co wersja row_number()
. Jeśli masz dwa wiersze z tą samą godziną zdarzenia, JOIN zwróci oba wiersze, podczas gdy row_number()
zwróci tylko jeden. Jeśli chcesz zwrócić oba za pomocą funkcji okienkującej, rozważ użycie funkcji okienkującej rank()
, ponieważ zwróci ona powiązania:
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;
Zobacz Demo
Komentarze
- Dziękuję, uważam sprzężenie wewnętrzne do tabeli podzapytań (przykład 1 powyżej) jest najbardziej intuicyjne … i nie wymaga ', abym się nauczył, czym jest partycjonowanie według o. Oto spojrzenie na składnię podobną do przykładu 1: wybierz oT.dateField, oT.siteID, oT.field1, oT.field2, oT.field3, z originalTable jako oT wewnętrzne sprzężenie (wybierz max (dateField) jako newestDate, siteID z grupy originalTable według siteID) jako newTable w oT.siteID = newTable.site_ID i oT.dateField = newTable.newestDate order by oT.siteID asc To lepiej wyjaśnia, co ' dzieje się w podzapytaniu.
Odpowiedź
I „Jestem zaskoczony, że nikt nie wykorzystał funkcji okna poza row_number ()
Oto kilka danych do zabawy:
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");
Klauzula OVER () tworzy okno, dla którego zdefiniujesz swoje grupy zagregowane. W tym przypadku partycjonuję tylko na SHOOL_CODE, więc zobaczymy FIRST_VALUE, który będzie pochodził z LAST_UPDATE_DATE_TIME, pogrupowany według SCHOOL_CODE i w kolejności LAST_UPDATE_DATE_TIME w porządku malejącym. Ta wartość zostanie zastosowana do całej kolumny dla każdego SCHOOL_CODE.
Ważne jest, aby zwrócić szczególną uwagę na partycjonowanie i porządkowanie w klauzuli 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
Zwroty:
24-JAN-13 ABE 111222
To powinno wyeliminować potrzebę GROUP BY i podzapytań przez większą część. Będziesz chciał jednak uwzględnić DISTINCT.
Komentarze
- To miłe, ale czy istnieje sposób na uniknięcie powtarzania klauzuli over dla wszystkie kolumny?
Odpowiedź
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)
Komentarze
- Zamiast publikować tylko kod, powinieneś spróbować wyjaśnić, jak to odpowiada na pytanie; i potencjalnie to, co OP robił nieprawidłowo.