1

25M 行のこのテーブル ( PERSONS ) があります。

ID int(10) PK
points int(6) INDEX
some other columns

ユーザーに、互いにポイントがやや近い4つのランダムな行を表示したいと思います。ランダムな行を生成するためにいくつかの検索と調整を行った後、このクエリを見つけました。これは非常に高速です。

SELECT person_id, points
FROM persons AS r1 JOIN
       (SELECT (RAND() *
                     (SELECT MAX(person_id)
                        FROM persons)) AS id)
        AS r2
 WHERE r1.person_id>= r2.id and points > 0
 ORDER BY r1.person_id ASC
 LIMIT 4

だから私はPHPでこれを照会します。これにより、素晴らしく高速な結果が得られます (ウォームアップ時は 0.05 秒未満)。しかし、これらの行は実際にはランダムです (少なくとも 1 点以降points > 0)。少し近い行をいくつか表示したいと思いますが、毎回である必要はありませんが、このクエリを制限 50 で実行し、PHP でランダムな行を選択し、最も近い 3 つの行 (ポイントに基づく) を選択するとします。 ) その次。結果をソートし、ランダムな行を選択して、その前後の行を表示する必要があると思います。しかし、私はPHPにまったく慣れていないため、これをどのように作成できるかわかりません。

誰でも提案、すべてのフィードバックを歓迎します:)

4

3 に答える 3

3

列にインデックスを作成しpoints(まだ存在しない場合)、その上でランダム化ロジックを実行します。

ALTER TABLE persons ADD INDEX (points);

SELECT   person_id, points
FROM     persons JOIN (
           SELECT RAND() * MAX(points) AS pivot
           FROM   persons
           WHERE  points > 0
         ) t ON t.pivot <= points
ORDER BY points
LIMIT    4

このアプローチでは、points値の範囲にわたって均一な確率分布を使用してピボットが選択されることに注意してください。非常に不均一な場合points、他の値よりも頻繁にいくつかの値をピボットすることになります (その結果、一見「非ランダム」な結果になります)。

person_idこれを解決するには、より均一に分散された列 (おそらく?)によってランダム レコードを選択し、pointsそのランダム レコードの値をピボットとして使用できます。つまり、上記のステートメントのサブクエリを次のように置き換えます。

           SELECT   points AS pivot
           FROM     persons JOIN (

                      SELECT FLOOR(
                               MIN(person_id)
                             + RAND() * (MAX(person_id)-MIN(person_id))
                             ) AS random
                      FROM   persons
                      WHERE  points > 0

                    ) r ON r.random <= person_id
           WHERE    points > 0
           ORDER BY person_id
           LIMIT    1
于 2013-05-21T15:44:11.997 に答える
0

PHPで2つの別々のSQLクエリを実行し、それらを結合/サブクエリしないことをお勧めします。多くの場合、オプティマイザーはクエリを単純化できず、それぞれを個別に実行する必要があります。だから、あなたの場合。1000 人のユーザーがいる場合、オプティマイザーは最悪の場合、次のような問題を実行します。

  • 1000 人の行を取得する
  • 1000人の行を取得する人ごとにサブ選択を行います
  • 結合された行で 1000 人を結合すると、1.000.000 行になります
  • それらすべてをフィルタリングする

要するに: 1.000.000 行の 1001 クエリ

私のアドバイス?

2 つのクエリを実行し、両方とも結合またはサブ選択を行わない (特に組み合わせると、ほとんどの場合、パフォーマンスが劇的に低下します)

SELECT person_id, points 
FROM persons 
ORDER BY RAND() LIMIT 1

見つかったポイントを 2 番目のクエリに使用します

SELECT person_id, points, ABS(points - <POINTS FROM ABOVE>) AS distance 
FROM persons 
ORDER BY distance ASC LIMIT 4
于 2013-05-21T15:57:40.963 に答える