重複行があり得ないクエリに付加した distinct にもコストがかかる


C.J.Dateに倣って、重複行があろうとなかろうと、selectには常に distinct を付けるというやり方でイケルのかどうか、試してみた。
動的にSQL文を組み立てる場合、必要な時だけ distinct を付加するのが面倒くさいので、付けっぱなしにできるとありがたいので。
対象は PostgreSQL7.3.6 と MySQL4.0.20。 ちょっと古いけど。

結論

要らないdistinctは付けないほうがよいという、やっぱりな結果になりました。

PostgreSQL

PostgreSQL7.3.6は、distinctを付けると、重複排除の必要がない場合でも、正直に重複排除処理を行う。
だから、distinct は、必要な時以外は付けないほうがよい。


テストDBのテーブル customer には、主キー customer_id があり、顧客データが3万件弱入っている。
インデックスは主キーのものだけ。
以下のクエリは、distinctがないと、一瞬で返ってくる。

select c.customer_id, c.first_name
from customer c
order by customer_id
limit 100

QUERY PLAN
Limit (cost=0.00..12.26 rows=100 width=36)
-> Index Scan using customer_pkey on customer c (cost=0.00..119.75 rows=977 width=36)

distinctをつけると、PostgreSQLは正直にソート&重複排除する。
結果表が主キー列を含んでいるので、重複行が含まれることはあり得ないけど。
そして、1000倍オーダーの時間をかけて応答する。

select distinct c.customer_id, c.first_name
from customer c
order by customer_id
limit 100

QUERY PLAN
Limit (cost=84.29..91.56 rows=97 width=36)
-> Unique (cost=84.29..91.62 rows=98 width=36)
-> Sort (cost=84.29..86.73 rows=977 width=36)
Sort Key: customer_id, first_name
-> Seq Scan on customer c (cost=0.00..35.77 rows=977 width=36)

MySQL

MySQLの場合も、あってもなくてもよい distinct が最適化で無視されることはない模様。
テストDBのテーブル orders には主キー order_id があり、受注データが26万件入っている。
インデックスは主キーのものだけ。
distinctがあってもなくても、どちらも一瞬で返ってくる。
クエリキャッシュをoffにしても、体感速度は変わらない。

select id, from_name
from orders
order by id
limit 1000;

EXPLAIN実行結果>
table type possible_keys key key_len ref rows Extra
orders index PRIMARY 4 262556

ただし、distinctを付けるとプランに "Using temporary" が現れる。
一時テーブルを作ってソートしている模様?

select distinct id, from_name
from orders
order by id
limit 1000;

EXPLAIN実行結果>
table type possible_keys key key_len ref rows Extra
orders index PRIMARY 4 262556 Using temporary

気持ち悪いので、要らないdistinctは指定しないことにしよう。