MySQL查询动态范围?

时间:2009-11-21 22:28:52

标签: sql mysql

给出表格摘录:

id | name | age

我正在尝试构建一个将在特定年龄范围内返回10个人的查询。但是,如果该范围内没有足够的人,我想扩大范围,直到找到10个人。

例如,如果我在30-40范围内只找到5个人,我会在25-45范围内找到5个人。

此外,我希望查询能够使用RAND()或类似的顺序,以便每次都能得到不同的结果。

这超出了MySQL可以处理的范围吗?我是否必须将一些逻辑放在应用程序中?

2 个答案:

答案 0 :(得分:2)

为效果更新

我的原始解决方案有效,但需要进行表格扫描。我的解决方案是一个很好的解决方案,并且不需要表扫描,但是当唯一匹配远远超出异常值时,其硬编码范围将不起作用。此外,它还需要重复删除记录。但是,如果您有年龄指数,那么结合使用这两种解决方案可以让您获得两全其美的效果。 (如果您没有年龄索引,那么所有解决方案都需要进行表扫描)。

组合解决方案首先只选择可能符合条件的行(所需范围,加上该行范围内的10行和10行),然后使用原始逻辑对结果进行排名。警告:我没有足够的样本数据来验证MySQL的优化器确实足够智能以避免在这里进行表扫描--MySQL可能不够聪明,无法在没有扫描的情况下将这三个UNION编织在一起。

[刚刚更新,修复了2个令人尴尬的SQL错误:DESC应该没有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() ;

原始解决方案:

我会这样接近:

  1. 首先,计算每条记录距所需年龄范围中心的距离,然后按该距离对结果进行排序。对于范围内的所有结果,将距离视为0到1之间的随机数。这样可确保以随机顺序选择范围内的记录,如果需要,将按照最接近所需范围的顺序选择范围之外的记录。
  2. 将距离排序的结果集中的记录数修剪为10条记录
  3. 随机化结果记录的顺序
  4. 像这样:

    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:性能,这需要扫描表格,所以不会很快 - 我现在正在开发一个无扫描解决方案......

答案 1 :(得分:1)

我会做这样的事情:

select * from (
 SELECT * FROM (select * from ppl_table where age>30 and age<40 order by rand() limit 10) as Momo1
 union
 SELECT * FROM (select * from ppl_table where age>25 and age<40 order by rand() limit 20) as Momo2
) as FinalMomo
limit 10

基本上从第一组中选择10个用户,然后从第二组中选择更多用户。 如果第一组不加10,那么第二组将会有更多。

我们从第二组中选择20的原因是因为UNION将删除重复的值,并且您希望最终结果中至少有10个用户。

修改

我从内部as添加了SELECT别名,并在内部SELECT中单独添加,因为MySql不喜欢ORDER BY UNION }