1

テーブルからレコードを取得するには、次の mysql クエリを使用します。

SELECT 
    a.id as aid, a.data1 as adata1, a.data2 as adata2
    b.id as bid, b.data1 as bdata1, b.data2 as bdata2
FROM table AS a
JOIN table AS b ON ( a.id <> b.id ) 
WHERE (a.data1=1 AND b.data1=1) AND ABS( a.rating - b.rating ) <100
ORDER BY RAND() 
LIMIT 1

このクエリは、必要なレコードを正確に取得しますが、残念ながら、RAND()このクエリは非常に遅いためです。

RAND()関数の使用を避ける方法をいくつか見つけましRAND()しかし、私の問題は、このクエリの関数を置き換える方法がまだ見つからないことです。一部の単純なクエリでは、 を置き換えることは問題RAND()ありませんが、上記の例でそれを行う方法はわかりません...WHERE句の条件が多いためです。

4

3 に答える 3

1

あなたの質問はあまり具体的ではありません。。。テーブルの大きさはどれくらいですか?正確には、「かなり遅い」とは何ですか?テーブル内のレコードのすべてのペアを検索しようとしています。ここで、data1 = 1であり、評価の差は100未満です。次のバージョンでは、すべての条件を「ON」句に移動したため、より明確にまとめられています。 :

SELECT a.id as aid, a.data1 as adata1, a.data2 as adata2
       b.id as bid, b.data1 as bdata1, b.data2 as bdata2
FROM table AS a join
     table AS b
     ON a.id <> b.id and
        a.data1 = b.data1 and
        a.data1 = 1 and b.data1 = 1 and
        ABS( a.rating - b.rating ) < 100
ORDER BY RAND() 
LIMIT 1

また、追加の条件を追加しましたa.data1 = b.data1。これは、SQLエンジンがこれを等結合として認識するのに役立ち、結合のパフォーマンスに役立つはずだからです。

data1が選択的である(つまり、data1を持つレコードが比較的少ない)と仮定すると、(data1、id)または(data1、rating)のインデックスを使用してこれを高速化できるはずです。

すべてのレコードに少なくとも1つの一致があることがわかっている場合(つまり、すべてのレコードに同様の評価の別のレコードがある場合)、次のバリエーションの方がうまくいくはずです。

SELECT a.id as aid, a.data1 as adata1, a.data2 as adata2
       b.id as bid, b.data1 as bdata1, b.data2 as bdata2
FROM (select *
      from table AS a
      where a.data1 = 1
      order by rand()
      limit 1
     ) a join
     table AS b
     ON a.id <> b.id and
        a.data1 = b.data1 and
        a.data1 = 1 and b.data1 = 1 and
        ABS( a.rating - b.rating ) < 100
ORDER BY RAND() 
LIMIT 1

これは最初にランダムなレコードを選択し、次に自己参加を行います。

これにより、次のように、この問題に対して別のアプローチを取ることができるという考えが得られます。まず、見ているデータの評価を計算します。次に、差が100未満のランダムな評価のペアを選択し、それらに一致するランダムなレコードを見つけます。data1と評価のインデックスを使用すると、このアプローチが最速になる可能性があります。

于 2012-09-22T15:22:42.663 に答える
1

MySQL を使用しているため、最初にテーブルからカウントを取得し、次にそのカウントに基づいてランダムなオフセットを選択する次の SQL クエリを試すことができます。次に、計算されたオフセットを使用できるようにステートメントを準備し、ステートメントを実行します。

SELECT @count := COUNT(*) FROM table AS a JOIN table AS b ON ( a.id <> b.id ) WHERE (a.data1=1 AND b.data1=1) AND ABS( a.rating - b.rating ) <100;
SET @offset = CONVERT(FLOOR(RAND() * @count), SIGNED);
PREPARE mystatement FROM "SELECT 
                          a.id as aid, a.data1 as adata1, a.data2 as adata2
                          b.id as bid, b.data1 as bdata1, b.data2 as bdata2
                          FROM table AS a
                          JOIN table AS b ON ( a.id <> b.id ) 
                          WHERE (a.data1=1 AND b.data1=1) AND ABS( a.rating - b.rating ) <100 LIMIT ?, 1";
EXECUTE mystatement USING @offset;
DEALLOCATE PREPARE mystatement;

大規模なデータセットでは、よりも高速に実行する必要がありますORDER BY RAND()。試してみて、私に知らせてください... ;-)

編集

クエリは phpmyadmin で使用すると機能しないため、MySQL コンソールを使用してクエリを実行するか、2 つのオプションがある php スクリプトを作成します。最初のオプションは mysql に作業を任せることです。

mysql_query('SELECT @count := COUNT(*) FROM table AS a JOIN table AS b ON ( a.id <> b.id ) WHERE (a.data1=1 AND b.data1=1) AND ABS( a.rating - b.rating ) <100');
mysql_query('SET @offset = CONVERT(FLOOR(RAND() * @count), SIGNED)');
mysql_query('PREPARE mystatement FROM "SELECT 
                          a.id as aid, a.data1 as adata1, a.data2 as adata2
                          b.id as bid, b.data1 as bdata1, b.data2 as bdata2
                          FROM table AS a
                          JOIN table AS b ON ( a.id <> b.id ) 
                          WHERE (a.data1=1 AND b.data1=1) AND ABS( a.rating - b.rating ) <100 LIMIT ?, 1"');
$res = mysql_query('EXECUTE mystatement USING @offset');
$row = mysql_fetch_assoc($res);
print_r($row);

さらに高速になる可能性がある 2 番目のオプションは、MySQL で作業の一部を実行し、プログラミング言語 (この場合は PHP) で他の部分を実行することで構成されます。

$res = mysql_query("SELECT COUNT(*) FROM table AS a JOIN table AS b ON ( a.id <> b.id ) WHERE (a.data1=1 AND b.data1=1) AND ABS( a.rating - b.rating ) <100')");
$row = mysql_fetch_array($res);
$offset = rand(0, $row[0]-1);

$res = mysql_query("SELECT 
                              a.id as aid, a.data1 as adata1, a.data2 as adata2
                              b.id as bid, b.data1 as bdata1, b.data2 as bdata2
                              FROM table AS a
                              JOIN table AS b ON ( a.id <> b.id ) 
                              WHERE (a.data1=1 AND b.data1=1) AND ABS( a.rating - b.rating ) <100 LIMIT $offset, 1");
$row = mysql_fetch_assoc($res);

私が見つけた ORDER BY RAND() を高速化する別の方法は、次のようなクエリで構成されています。

SELECT 
    a.id as aid, a.data1 as adata1, a.data2 as adata2
    b.id as bid, b.data1 as bdata1, b.data2 as bdata2
FROM table AS a
JOIN table AS b ON ( a.id <> b.id ) 
WHERE (RAND() < (SELECT ((1/COUNT(*))*10) FROM table AS a JOIN table AS b ON ( a.id <> b.id ) ) )
 AND (a.data1=1 AND b.data1=1) AND ABS( a.rating - b.rating ) <100
ORDER BY RAND() 
LIMIT 1

あなたが得た結果について私に更新することを忘れないでください;-) .

于 2012-09-22T15:17:19.150 に答える
0

問題のある空間での分布がやや均一でなくても問題がない場合は、次のことを試してみてください。

SELECT a.id as aid, a.data1 as adata1, a.data2 as adata2
       b.id as bid, b.data1 as bdata1, b.data2 as bdata2
  FROM ( SELECT *
           FROM table
          WHERE data1 = 1
          ORDER
             BY RAND()
          LIMIT 1
       ) a
  JOIN table b
    ON b.data1 = 1
   AND b.rating BETWEEN a.rating - 100 AND a.rating + 100
 ORDER
    BY RAND()
 LIMIT 1
;

上記は、ランダムに1つaのレコードを選択し、次にランダムにレコードを選択しますb。したがって、注文して参加するレコードがはるかに少なくなります。aこれは、の可能な対応する選択肢の数に比例するのではなく、のすべての選択肢の可能性が等しくなることを意味するため、均一性が低くなりますbが、おそらくそれはあなたの目的には十分ですか?

于 2012-09-22T15:21:06.387 に答える