1

SQL クエリの結果からランダムな行を取得しようとしています。

私のクエリは次のとおりです

SET @rank = 0;
SELECT * FROM(
     SELECT (@rank:=@rank+1) AS num, ...
     FROM ...
     WHERE ....) as raw
WHERE raw.num = FLOOR(1 + (RAND() * @rank)) LIMIT 1

一般的な考え方は、内部クエリ結果からのテーブルの各行に一意の番号 (num) が与えられるということです。これが実際に当てはまり、すべての行に番号が付けられていることを手動で確認しました。

最後の行は私を苦しめています。現状では、WHERE num = FLOOR(1 + (RAND() * @rank)) LIMIT 1私が望むものを返しています-半分の時間しかありません。正しい範囲内のランダムな行を返しているようです (クエリをテストしている例では、0 ~ 1299 です)。ただし、3 回に 1 回のクエリではまったく何も返されません。

わかりましたので、おそらく倍精度の問題だと思ったので、>=次のように使用してみました: WHERE num >= FLOOR(1 + (RAND() * @rank)) LIMIT 1. この場合の結果は私を混乱させます。このコードでは常に結果が得られますが、返される行数は常に < 100 です。

したがって、 FLOOR(1 + (RAND() * @rank)) を呼び出すとしますx。を使用する=と、(場合によっては) 1000 より大きい数値に等しくなければならない>=ことが確認されます。x>=

どうしたの?または、他にどのように問題を解決できますか

4

2 に答える 2

5

問題はRAND()、クエリ内の関数が から返された行ごとに 1 回、複数回呼び出されていることだと思いますraw。それが起こっている場合、各行を異なるターゲットと比較しているため、述語を満たす行が見つからない可能性があります。(1列目は5列目?2列目は3列目?など)

RAND() への呼び出しと @rank の最初の割り当てを、次のようにクエリの先頭に移動します。

SELECT * FROM(
     SELECT (@rank:=@rank+1) AS num, ...
     FROM (SELECT @rand := RAND(), @rank := 0) r
     CROSS JOIN ...
     WHERE ....) as raw
WHERE raw.num = FLOOR(1 + @rand * @rank) LIMIT 1

-- または、別の SET ステートメントを使用するパターンに合わせて --

SET @rand = RAND();
SET @rank = 0;
SELECT * FROM(
     SELECT (@rank:=@rank+1) AS num, ...
     FROM ...
     WHERE ....) as raw
WHERE raw.num = FLOOR(1 + @rand * @rank) LIMIT 1

(単一のステートメントとして実行されるため、たまたま前者を好みます。SELECT ステートメントの外部で設定されているユーザー変数に依存しません。)

ただし、どちらも関数の呼び出しがRAND()正確に 1 回 (クエリの開始時に) 発生していることを確認する必要があります。

それ以外には、あなたが見ている行動についての良い説明がありません。

于 2012-07-09T20:46:38.003 に答える
0

なぜ使用しないのですか?:

ORDER BY RAND() LIMIT 1

そうでない場合は、以下をお読みください: http://akinas.com/pages/en/blog/mysql_random_row/

EDIT でクエリをテストしたところ、次の udd 出力が得られました。

mysql> SET @rank = 0;
Query OK, 0 rows affected (0.00 sec)

mysql> SELECT * FROM(
    ->     SELECT (@rank:=@rank+1) AS num, id
    ->     FROM (
    ->         SELECT 1 AS id UNION SELECT 2 UNION SELECT 3
    ->         ) as abc
    ->     ) as raw
    -> WHERE raw.num = @rownr := FLOOR(1 + (RAND() * @rank)) LIMIT 1;
Empty set (0.00 sec)

mysql> SELECT @rownr;
+--------+
| @rownr |
+--------+
|      1 |
+--------+
1 row in set (0.00 sec)
于 2012-07-09T19:35:23.117 に答える