1. ホーム
  2. mysql

[解決済み] MySQLのORDER BY RAND()関数を最適化するにはどうしたらいいですか?

2023-03-26 20:33:16

質問

クエリを最適化したいので、以下のことを調べています。 mysql-slow.log .

私の遅いクエリのほとんどは ORDER BY RAND() . この問題を解決するための実際のソリューションが見つかりません。可能な解決策は次のとおりです。 MySQLPerformanceBlog にありますが、これで十分だとは思えません。最適化されていない (あるいは頻繁に更新される、ユーザーが管理する) テーブルでは、うまくいかないか、あるいは、自分の PHP -で生成されたランダムな行を選択する前に、2つ以上のクエリを実行する必要があります。

この問題に対する解決策はありますか?

ダミーの例です。

SELECT  accomodation.ac_id,
        accomodation.ac_status,
        accomodation.ac_name,
        accomodation.ac_status,
        accomodation.ac_images
FROM    accomodation, accomodation_category
WHERE   accomodation.ac_status != 'draft'
        AND accomodation.ac_category = accomodation_category.acat_id
        AND accomodation_category.acat_slug != 'vendeglatohely'
        AND ac_images != 'b:0;'
ORDER BY
        RAND()
LIMIT 1

どのように解決するのですか?

これを試してみてください。

SELECT  *
FROM    (
        SELECT  @cnt := COUNT(*) + 1,
                @lim := 10
        FROM    t_random
        ) vars
STRAIGHT_JOIN
        (
        SELECT  r.*,
                @lim := @lim - 1
        FROM    t_random r
        WHERE   (@cnt := @cnt - 1)
                AND RAND(20090301) < @lim / @cnt
        ) i

これは特に MyISAM で特に効率的です (つまり COUNT(*) は瞬間的なものなので)、しかし InnoDB10 よりも何倍も効率的です。 ORDER BY RAND() .

ここでの主な考え方は、ソートを行わず、2つの変数を保持して計算することです。 running probability を計算することです。

詳しくは私のブログのこの記事をご覧ください。

更新しました。

ランダムに1つのレコードを選択する必要がある場合、これを試してみてください。

SELECT  aco.*
FROM    (
        SELECT  minid + FLOOR((maxid - minid) * RAND()) AS randid
        FROM    (
                SELECT  MAX(ac_id) AS maxid, MIN(ac_id) AS minid
                FROM    accomodation
                ) q
        ) q2
JOIN    accomodation aco
ON      aco.ac_id =
        COALESCE
        (
        (
        SELECT  accomodation.ac_id
        FROM    accomodation
        WHERE   ac_id > randid
                AND ac_status != 'draft'
                AND ac_images != 'b:0;'
                AND NOT EXISTS
                (
                SELECT  NULL
                FROM    accomodation_category
                WHERE   acat_id = ac_category
                        AND acat_slug = 'vendeglatohely'
                )
        ORDER BY
                ac_id
        LIMIT   1
        ),
        (
        SELECT  accomodation.ac_id
        FROM    accomodation
        WHERE   ac_status != 'draft'
                AND ac_images != 'b:0;'
                AND NOT EXISTS
                (
                SELECT  NULL
                FROM    accomodation_category
                WHERE   acat_id = ac_category
                        AND acat_slug = 'vendeglatohely'
                )
        ORDER BY
                ac_id
        LIMIT   1
        )
        )

これは、あなたの ac_id が多かれ少なかれ均等に配置されていると仮定しています。