ユニークな番号を採番したいケースがありますよね。SQLでやれば複数のサーバ間でユニークにできる。UUIDは重ならないとかいう話もあるが、実際システムでユニークであることを保証しようと思えば乱数等に依存するわけにはいかない。 というわけで、ここではSQLを使って採番することを考える。
CREATE TABLE tbl (num integer NOT NULL UNIQUE PRIMARY KEY)
で、どんなSQLを発行すれば次に取りたい値を求められるのか?
SELECT (num+1) FROM tbl
WHERE (num+1) NOT IN (SELECT num FROM tbl)
これは十分直感的な書き方。num+1の値であって既存のnumの中にはない、というもの。どっかでググって見つけてきたクエリだ。ただこれはMySQLの場合dependent subqueryになる。
mysql> EXPLAIN SELECT (num+1) FROM tbl WHERE (num+1) NOT IN (SELECT num FROM tbl);
+----+--------------------+-------+-----------------+---------------+---------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-------+-----------------+---------------+---------+---------+------+------+--------------------------+
| 1 | PRIMARY | tbl | index | NULL | PRIMARY | 4 | NULL | 1 | Using where; Using index |
| 2 | DEPENDENT SUBQUERY | tbl | unique_subquery | PRIMARY,num | PRIMARY | 4 | func | 1 | Using index; Using where |
+----+--------------------+-------+-----------------+---------------+---------+---------+------+------+--------------------------+
2 rows in set (0.00 sec)
この「dependent subquery」というやつはMySQLにおいては忌み嫌われている有名な「遅くなる」クエリで、よく「外側から評価される」と称される。外側が評価されて、結果それぞれに対して内側のサブクエリが毎回実行されるという意味で直感的な動きとは異なってしまい、効率良さそうに書いたつもりが実は良くない、という問題をはらんでいるらしい。遅くなってしまうクエリにしても、内外を逆転して書ける種類のものであれば、良くなるんだろう。 ↑で上げたクエリの場合は外側と内側が同じ分量なのでまあ、そんなに悪化するとは思えない。実際いろいろ試してみたが、そんなに遅くはならない。 JOINを使ってみる場合はこんなクエリになる。