log

日々の記録と、書くことを楽しむためのブログです

ROW_NUMBER() OVER()で重複データの排除

結構おもしろい方法だったので、備忘のため記しておきます。
使用しているDBはOracleです。

前提

たとえば、ITEM_LISTというテーブルからデータを持ってきたいとします。
こんなデータです。

ITEM_CD ITEM_NAME ITEM_CATEGORY CREATE_DATE
01 たぬき どうぶつ 12/12
02 卵焼き 食べ物 10/01
01 たぬき どうぶつ 08/13
03 モルモット どうぶつ 08/13

ITEM_CDとITEM_CATEGORYで、「どうぶつ」カテゴリの「たぬき」が重複しています。
重複データは必要ない。さらにITEM_LISTテーブルにはユニークキーたるIDカラムがないので、IDを振りたい。
そんな用事があるときにROW_NUMBER()とOVER()を使います。

サンプル

SELECT
  ROWNUM ID, -- 行番号はIDと名付ける
  TBL.* -- TBLクエリの全てを取る
FROM
(
    SELECT
        LIST.*, -- LISTテーブルの全データ取得
        ROW_NUMBER() OVER(
            PARTITION BY
                ITEM_CD, --アイテムコード……
                ITEM_CATEGORY -- と、アイテムカテゴリーの重複を許さない
            ORDER BY
                ITEM_CD -- アイテムコード昇順で並べる
        ) RW_NUM
    FROM
        ITEM_LIST LIST
) TBL
WHERE
    TBL.RW_NUM = 1 -- TBLクエリのうち、ROW_NUMBERが1のものだけ抽出。すなわち、重複のうち1番目のものだけ抽出

上のサンプルのOVER()の中では以下のことを指定しています。
ITEM_LISTテーブルを「ITEM_CDとITEM_CATEGORY」の塊で括り、その中でITEM_CDが若い順に並べる。

このルールで作った順位データはRW_NUMと名付けます。
そして、その順位とアイテムデータが組み合わさったサブクエリはTBLと呼びます。

WHERE句にて、重複データを除くためTBLのRW_NUMが1以外のデータはすべて捨てます。
なぜなら2番目以後はすべて重複データだからです。
ORDER BYに入れたいカラムが特にないときはパーティションと同じカラムでも大丈夫です。

最後に、メインとなるクエリでは、TBLクエリのデータを丸ごと取得し、その一行一行にROWNUMを利用してIDを振ります。

結果

先のSQLで取得した結果、このようになるはずです。

ID ITEM_CD ITEM_NAME ITEM_CATEGORY CREATE_DATE
1 01 たぬき どうぶつ 12/12
2 02 卵焼き 食べ物 10/01
3 03 モルモット どうぶつ 08/13

08/13に登録された「たぬき」は重複とみなされて消えています。