PG

思ったことをぽつぽつと

DISTINCTにはまった話

postgresqlでDISTINCTを使っていてはまってしまったので

ブログにまとめてみます。

 

SQLを使用していて重複項目を取り出したくないときは、

DISTINCTを使用します。

 

SELECT DISTINCT 列1, 列2
FROM 表;

 

重複を削除するとき事前にソートしたものから取り出したかったので

次のように書いてみました。

 

SELECT DISTINCT T.列1, T.列2
FROM (
SELECT 表1.列1,表1.列2,表2.列3, 表2.列4
FROM 表1, 表2
WHERE 表1.列1=表2.列3
ORDER BY 表2.列4
) T;

二番目のSELECTで取得した表から列1と列2の重複データを削除してソートした
通りの順番で取り出したかったのですが、結果は順番が狂って取り出されました。
調べてみたらDISTINCTは内部でソートしてから重複を削除するという
処理が働くために起こる現象のようです。

ORDER BYを評価した後にDISTINCTが評価されて意図しないソートが発生してしまうわけです。

 

対策としては、二番目のSELECT内でDISTINCTし、ORDER BYを一番目のSELECTに対して行う。

これでDISTINCTの評価をORDER BYより先にさせることができます。

ただし、ソートする条件が取得したいカラム以外に指定する必要がある場合はさらに副問い合わせを行う必要があります。

 

SELECT T1.列1, T1.列2
FROM 
(SELECT T2.列1, T2.列2, T2.列3, T2.列4
FROM(
SELECT DISTINCT 表1.列1,表1.列2,表2.列3, 表2.列4
FROM 表1, 表2
WHERE 表1.列1=表2.列3
) T2
ORDER BY T2.列4
)T1;

 

また、SQLから離れますが、SELECT結果取得後に一意項目を削除する処理をコード上で行う方法もあります。

ちなみに同じ重複を削除する処理として表結合時に重複を削除するUNIONもあります。

こちらも重複削除時は、ソートが走り、意図しない順番になる可能性があるので、

基本的にUNION ALLを使用した方が安全と言えます。

(UNION ALLのが早いしね)

 

例に挙げた通り、条件が複雑になるとSQLが複雑になり、コードからは追いづらくなります。

個人的には、データベース言語で処理するよりも、JavaC#などのコードで処理した方が簡単になるのでお勧めです。