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

从中删除子查询将大大提高性能和缓存,因此您可以例如获取您的 ID 列表,将其放入文件中,然后从中随机获取(例如通过从文件中读取随机行)。这将大大改善它,因为您可以看到您是否会在此查询上运行 EXPLAIN 并通过更改查询以仅加载 4 个(仍然是随机的)id 的数据来比较它。

于 2013-05-21T15:48:17.107 回答
0

我建议在 PHP 中执行两个单独的 sql 查询,而不是加入/子查询它们。在许多情况下,优化器无法简化您的查询,必须分别执行每一项。所以,在你的情况下。如果您有 1000 人,优化器将在最坏的情况下执行以下操作:

  • 获取 1000 人行
  • 为获得 1000 人行的每个人做子选择
  • 加入 1000 个人,加入行导致 1.000.000 行
  • 过滤所有这些

简而言之:1001 个查询,1.000.000 行

我的建议?

执行两个查询并且两者都没有连接或子选择(尤其是在大多数情况下组合会导致性能急剧下降)

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

现在将找到的点用于您的第二个查询

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 回答