给定表格片段:
id | name | age
我正在尝试构建一个查询,该查询将返回特定年龄范围内的 10 个人。但是,如果那个范围内没有足够的人,我想扩大范围,直到我能找到 10 个人。
例如,如果我只找到 30-40 岁之间的 5 个人,我会找到 25-45 岁之间的另外 5 个人。
此外,我希望查询能够使用 RAND() 或类似命令,以便每次都能获得不同的结果。
这是否超出了 MySQL 的处理能力?我是否必须改为将其中的一些逻辑放入应用程序中?
最佳答案
性能更新:
我最初的解决方案有效,但需要进行表扫描。 Am 的解决方案是一个很好的解决方案,不需要表扫描,但是当唯一的匹配项是非常异常值时,它的硬编码范围将不起作用。此外,它还需要删除重复记录。但是,将这两种解决方案结合起来可以让您两全其美,前提是您有一个关于年龄的索引。 (如果您没有年龄索引,那么所有解决方案都需要进行表扫描)。
组合解决方案首先仅选择可能符合条件的行(所需范围,加上超出该范围的 10 行和低于该范围的 10 行),然后使用我的原始逻辑对结果进行排名。警告:我没有足够的样本数据来验证 MySQL 的优化器确实足够聪明,可以避免此处的表扫描——MySQL 可能不够聪明,无法在不进行扫描的情况下将这三个 UNION 编织在一起。
[刚刚再次更新以修复 2 个令人尴尬的 SQL 拼写错误:DESC 不应该出现的地方!]
SELECT * FROM
(
SELECT id, name, age,
CASE WHEN age BETWEEN 25 and 35 THEN RAND() ELSE ABS (age-30) END as distance
FROM
(
SELECT * FROM (SELECT * FROM Person WHERE age > 35 ORDER BY age DESC LIMIT 10) u1
UNION
SELECT * FROM (SELECT * FROM Person WHERE age < 25 ORDER BY age LIMIT 10) u2
UNION
SELECT * FROM (SELECT * FROM Person WHERE age BETWEEN 25 and 35) u3
) p2
ORDER BY distance
LIMIT 10
) p ORDER BY RAND() ;
原始解决方案:
我会这样处理:
- 首先,计算每条记录与所需年龄范围中心的距离,然后按该距离对结果进行排序。对于范围内的所有结果,将距离视为介于 0 和 1 之间的随机数。这确保范围内的记录将以随机顺序被选择,而范围外的记录将在需要时以最接近所需范围的顺序被选择。
- 将按距离排序的结果集中的记录数减少为 10 条记录
- 随机生成记录的顺序
像这样:
CREATE TABLE Person (id int AUTO_INCREMENT PRIMARY KEY, name varchar(50) NOT NULL, age int NOT NULL);
INSERT INTO Person (name, age) VALUES ("Joe Smith", 26);
INSERT INTO Person (name, age) VALUES ("Frank Johnson", 32);
INSERT INTO Person (name, age) VALUES ("Sue Jones", 24);
INSERT INTO Person (name, age) VALUES ("Ella Frederick", 44);
SELECT * FROM
(
SELECT id, name, age,
CASE WHEN age BETWEEN 25 and 35 THEN RAND() ELSE ABS (age-30) END as distance
FROM Person
ORDER BY distance DESC
LIMIT 10
) p ORDER BY RAND() ;
请注意,我假设如果范围内没有足够的记录,则您要追加的记录是最接近该范围的记录。如果此假设不正确,请在问题中添加更多详细信息。
re: 性能,这需要扫描整个表,所以不会很快——我现在正在研究一个无扫描的解决方案......
关于sql - MySQL 查询动态范围?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1777007/