サブクエリのキャッシュ
SQL
1SELECT * From data where id = CEIL(RAND() * (select count(*) from data));
これはサブクエリを持つクエリです。サブクエリは相関項目を持たない。一般にこの動作は、サブクエリを実行して結果をキャッシュ、キャッシュを使って外部クエリが実行される。
ここから推測。キャッシュされるのは、(select count(*) from data))の結果だけで、式 CEIL(RAND() * <キャッシュされたサブクエリ>) は毎回実行されると考えると、辻褄が会うのではないでしょうか。次のようにクエリを変更します。
SQL
1SELECT * From data where id = (SELECT RID FROM (SELECT CEIL(RAND() * (select count(*) from data)) AS RID)R);
式全体をキャッシュしたので1件だけヒットすると推測します。内部結合を使う方法は他の方が回答されています。あるいは、withを使う以下の方法でもCEIL式までキャッシュできる。SQLをバラバラにしています。
SQL
1WITH RND(R) AS (
2SELECT RAND()
3), CNT(C) AS (
4select count(*) from data
5), CIL(RID) AS (
6SELECT CEIL(R*C) from RND cross join CNT
7)
8SELECT * From data where id = (SELECT RID FROM CIL);
クエリとサブクエリの関係
クエリとサブクエリの関係は思ったより複雑です。2つのクエリごとにサブクエリがキャッシュされるようです。
SQL
1WITH RND(R) AS (
2SELECT RAND()
3), CNT(C) AS (
4select count(*) from data
5), CIL(RID) AS (
6SELECT CEIL(R*C) from RND cross join CNT
7)
8SELECT * From data where id = (SELECT RID FROM CIL)
9UNION ALL
10SELECT * From data where id = (SELECT RID FROM CIL);
CEIL(RAND() * (select count(*) from data))はレコードごとに実行される
理由は、idと上の式の値が等しいかを判定するために、式を評価するからです。次のSQLを考えてください。これらの条件式はレコードごとに評価しています。
SQL
1select * from data where id % 2 = 0 limit 5;
2select * from data where id = power(ceil(rand()*10000),0);
最後に、質問の現象はどのテーブルでも再現できます。where条件に ceil(rand() * (select count(*) from <table>))と書けばよい。
結論
以下の2つのSQLは同じです。
SQL
1SELECT * From data where id = CEIL(RAND() * (select count(*) from data));
2SELECT id,datum,created_at,updated_at From (select *, CEIL(RAND() * (select count(*) from data)) from data) rnd where id=`CEIL(RAND() * (select count(*) from data))`;
補足
EXPLAINを使ってSQLを確認しました。以下のSQLはSUBQUERY
でなくUNCACHEABLE SUBQUERY
が表示されました。しかし対象が導出表だったので同じ結果が得られると判断しました。
SQL
1MariaDB [...]> explain SELECT * From data where id = (SELECT R FROM (SELECT CEIL(RAND() * (select count(*) from data)) AS R) RND);
2+------+----------------------+------------+--------+---------------+---------+---------+-------+--------+----------------+
3| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
4+------+----------------------+------------+--------+---------------+---------+---------+-------+--------+----------------+
5| 1 | PRIMARY | data | const | PRIMARY | PRIMARY | 8 | const | 1 | Using where |
6| 2 | UNCACHEABLE SUBQUERY | <derived3> | system | NULL | NULL | NULL | NULL | 1 | |
7| 3 | DERIVED | NULL | NULL | NULL | NULL | NULL | NULL | NULL | No tables used |
8| 4 | SUBQUERY | data | index | NULL | PRIMARY | 8 | NULL | 100239 | Using index |
9+------+----------------------+------------+--------+---------------+---------+---------+-------+--------+----------------+
104 rows in set (0.043 sec)
これ以外のSQLのEXPLAINの結果は想定どおりでした。