MySQLでOracleのrownumのような行番号を付加する方法として、Web上でよく見かけるのは以下のようなユーザー変数を使ったSQL文で、2つのSQL文を発行しなければならない。
set @i:=0; select @i:=@i+1 as rownum,user_id from user;
しかし、kotori::logで紹介されていた方法を用いれば、1回のSQL文で付加することができる。メモメモ。
select @i:=@i+1 as rownum,user_id from (select @i:=0) as dummy,user;
なるほど。サブクエリでユーザー変数を初期化しているのね。
で、ふとorder by節で並び替えた時の挙動が気になったので、早速試してみた。
全部で4つ試していて、始めの2つがテーブルのidカラムで並び替え(昇順、降順)、残りの2つ(3つ目と4つ目)が生成したrownumで並び替えたものである。
1つ目。
mysql> select @i := @i + 1 rownum, id -> from (select @i := 0) dummy, tablename -> order by id asc; +--------+----+ | rownum | id | +--------+----+ | 1 | 2 | | 2 | 3 | | 3 | 5 | | 4 | 7 | | 5 | 11 | +--------+----+ 5 rows in set (0.00 sec)
2つ目。
mysql> select @i := @i + 1 rownum, id -> from (select @i:= 0) dummy, tablename -> order by id desc; +--------+----+ | rownum | id | +--------+----+ | 1 | 11 | | 2 | 7 | | 3 | 5 | | 4 | 3 | | 5 | 2 | +--------+----+ 5 rows in set (0.00 sec)
3つ目
mysql> select @i := @i + 1 rownum, id -> from (select @i := 0) dummy, tablename -> order by rownum asc; +--------+----+ | rownum | id | +--------+----+ | 1 | 2 | | 2 | 3 | | 3 | 5 | | 4 | 7 | | 5 | 11 | +--------+----+ 5 rows in set (0.00 sec)
4つ目
mysql> select @i := @i + 1 rownum, id -> from (select @i := 0) dummy, tablename -> order by rownum desc; +--------+----+ | rownum | id | +--------+----+ | 5 | 11 | | 4 | 7 | | 3 | 5 | | 2 | 3 | | 1 | 2 | +--------+----+ 5 rows in set (0.00 sec)
う~ん。2つ目のSQL文の結果が予想していた結果と異なっている。予想していた結果は4つ目のSQL文と同様の結果だったのだけれど……。
Oracleで以下のようなSQL文を書いて確認してみたが、どちらも4つ目と同じ結果。
select rownum, id from tablename order by id desc; select rownum, id from tablename order by rownum desc;
となると2つ目のSQL文は、4つ目のSQLと比べて
@i := @i + 1 rownum
を評価するタイミングが違うということになる。
2つ目のSQL文では、idカラムで並び替えた後に評価されているように見える。
というわけで調べてみたところ、MySQLのサイトの8.4. ユーザによって定義された変数のページで以下のような説明が。
ユーザ変数の評価順序は定義されておらず、与えられたクエリ内の要素に基づいて変更されることがあります。
SELECT @a, @a := @a+1 ...
では、MySQLは@a
を先に評価し次に割り当てが実行されるように見えますが、クエリの変更(例えばGROUP BY
、HAVING
またはORDER BY
節による変更)は評価順序を変更する可能性があります。
このエントリで紹介したrownumもどきは、使用する時に注意が必要ですな。