ここに2つのテーブルがあります。
学校職員
SCHOOL_CODE + STAFF_TYPE_NAME + LAST_UPDATE_DATE_TIME + PERSON_ID
=================================================================
ABE Principal 24-JAN-13 111222
ABE Principal 09-FEB-12 222111
人
PERSON_ID + NAME
=================
111222 ABC
222111 XYZ
これが私の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;
これはこの結果を与えます
LAST_UPDATE SCHOOL_CODE PERSON_ID
===========+===========+=========
24-JAN-13 ABE 111222
09-FEB-12 ABE 222111
最新の日付の学校の最初のものを選択したいです。
ありがとう。
両方のエントリに一意の値を持つGROUP BY
列でPERSON_ID
句を使用しているため、現在のクエリでは望ましい結果が得られません。その結果、両方の行が返されます。
これを解決する方法はいくつかあります。サブクエリを使用して集計関数を適用し、SCHOOL_CODE
ごとにmax(LAST_UPDATE_DATE_TIME)
を返すことができます。
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;
SQL Fiddle with Demo を参照)
または、 ウィンドウ関数 を使用して、各学校のデータの行を最新の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;
SQL Fiddle with Demo を参照)
このクエリは row_number()
を実装し、SCHOOL_CODE
のパーティションの各行に一意の番号を割り当て、LAST_UPDATE_DATE_TIME
に基づいて降順で配置されます。
補足として、集約関数を使用したJOINは、row_number()
バージョンとまったく同じではありません。同じイベント時間の2つの行がある場合、JOINは両方の行を返しますが、row_number()
は1つだけを返します。ウィンドウ関数で両方を返す場合は、代わりにrank()
ウィンドウ関数を使用することを検討してください。
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;
デモ を参照してください
Row_number()を超えるウィンドウ関数を誰も利用していないことに驚きます
以下は、操作するデータの一部です。
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()句は、集計グループを定義するウィンドウを作成します。この場合、私はSHOOL_CODEでのみパーティション化しているため、LAST_UPDATE_DATE_TIMEからのFIRST_VALUEが、SCHOOL_CODEでグループ化され、LAST_UPDATE_DATE_TIMEの降順で表示されます。この値は、各SCHOOL_CODEの列全体に適用されます。
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
戻り値:
24-JAN-13 ABE 111222
これにより、ほとんどの場合、GROUP BYとサブクエリの必要がなくなります。ただし、DISTINCTを必ず含める必要があります。
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)