MySQL邻域中的随机行

时间:2013-05-21 15:36:12

标签: php mysql sql sorting random

我有这个表( PERSONS ),有25M行:

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秒)。但这些行实际上只是随机的(自points > 0以来至少有1个点)。我想显示一些有点接近的行,不一定是每次,但是假设我使用限制50执行此查询,而不是选择PHP中的随机行和3个最接近的行(基于点) 在它的旁边。我认为你需要对结果进行排序,选择一个随机行并显示它之后/之前的行。但我不知道如何才能做到这一点,因为我对PHP很新。

任何建议,欢迎所有反馈:)

3 个答案:

答案 0 :(得分: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

答案 1 :(得分:0)

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

答案 2 :(得分:0)

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

  • 获得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