我有这个表( 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很新。
任何建议,欢迎所有反馈:)
答案 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:
简而言之: 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