Open5
【達人に学ぶSQL徹底指南書メモ】3. 自己結合の使い方

3.1 重複順列
Products
name | price |
---|---|
りんご | 100 |
みかん | 50 |
バナナ | 80 |
SELECT
P1.name AS name_1,
P2.name AS name_2
FROM
Products P1
CROSS JOIN Products P2;
結果
name_1 | name_2 |
---|---|
りんご | りんご |
りんご | みかん |
りんご | バナナ |
みかん | りんご |
みかん | みかん |
みかん | バナナ |
バナナ | りんご |
バナナ | みかん |
バナナ | バナナ |
以下のようにも書けるが、古い書き方なので非推奨。
SELECT
P1.name AS name_1,
P2.name AS name_2
FROM
Products P1,
Products P2;

3.2 順列
SELECT
P1.name AS name_1,
P2.name AS name_2
FROM
Products P1
INNER JOIN Products P2
ON P1.name <> P2.name;
結果
name_1 | name_2 |
---|---|
りんご | みかん |
りんご | バナナ |
みかん | りんご |
みかん | バナナ |
バナナ | りんご |
バナナ | みかん |
古い書き方では以下のようにも書ける。
WHERE
句を書き忘れるとクロス結合になってしまうため非推奨。
INNER JOIN
を使えば、ON
句を書き忘れるとエラーになる。
SELECT
P1.name AS name_1,
P2.name AS name_2
FROM
Products P1,
Products P2
WHERE
P1.name <> P2.name;

3.3 組み合わせ
SELECT
P1.name AS name_1,
P2.name AS name_2
FROM
Products P1
INNER JOIN Products P2
ON P1.name > P2.name;
結果
name_1 | name_2 |
---|---|
りんご | みかん |
バナナ | みかん |
バナナ | りんご |
3 列に拡張すると
SELECT
P1.name AS name_1,
P2.name AS name_2,
P3.name AS name_3
FROM
Products P1
INNER JOIN Products P2
ON P1.name > P2.name
INNER JOIN Products P3
ON P2.name > P3.name;
name_1 | name_2 | name_3 |
---|---|---|
りんご | みかん | バナナ |

3.4 重複組合せ
SELECT
P1.name AS name_1,
P2.name AS name_2
FROM
Products P1
INNER JOIN Products P2
ON P1.name >= P2.name;
結果
name_1 | name_2 |
---|---|
バナナ | みかん |
バナナ | りんご |
バナナ | バナナ |
りんご | みかん |
りんご | りんご |
みかん | みかん |

3.5 重複行を削除する
Products
name | price |
---|---|
りんご | 50 |
みかん | 100 |
みかん | 100 |
みかん | 100 |
バナナ | 80 |
結果
name | price |
---|---|
バナナ | 80 |
みかん | 100 |
りんご | 50 |
3.5.1 実装依存のレコード ID を使う
Oracle の rowid
を使った例
極値関数を利用した場合
DELETE
FROM
Products P1
WHERE
rowid < (
SELECT MAX(P2.rowid)
FROM Products P2
WHERE P1.name = P2.name
AND P1.price = P2.price
);
非等値結合を利用した場合
DELETE
FROM
Products P1
WHERE
EXISTS(
SELECT *
FROM Products P2
WHERE P1.name = P2.name
AND P1.price = P2.price
AND P1.rowid < P2.rowid
);
3.5.2 実装依存の機能を使わない
(name, price) をキーとするパーティションを作って連番を振る。
CREATE TABLE Products_NoRedundant AS
SELECT
ROW_NUMBER()
OVER(
PARTITION BY
name,
price
ORDER BY
name
) AS row_num,
name,
price
FROM
Products;
Products_NoRedundant
row_num | name | price |
---|---|---|
1 | バナナ | 80 |
1 | みかん | 100 |
2 | みかん | 100 |
3 | みかん | 100 |
1 | りんご | 50 |
連番が 1 以外のレコードを削除する。
DELETE
FROM
Products_NoRedundant
WHERE
row_num > 1;