【问题标题】:Have a select query with multiple where clauses return multiple result rows in one result table?具有多个 where 子句的选择查询在一个结果表中返回多个结果行?
【发布时间】:2013-10-30 10:12:08
【问题描述】:

我有一个查询,例如:

SELECT COUNT(id) AS user_list FROM tbl_users
WHERE (enrolled = 1 AND age >=15 AND age < 20)
OR (enrolled = 1 AND age >=20 AND age < 25)
OR (enrolled = 1 AND age >=25 AND age < 30)
OR (enrolled = 1 AND age >=30)

如何让它为一行和一个表中的每个 where 条件返回一个结果:

user_list
---------
   18    --(enrolled = 1 AND age >=15 AND age < 20)
   12    --(enrolled = 1 AND age >=20 AND age < 25)
   22    --(enrolled = 1 AND age >=25 AND age < 30) 
   56    --(enrolled = 1 AND age >=30)

【问题讨论】:

  • 这只是我的问题的一个简单示例,我可能会有数百/数千个 where 子句。

标签: php mysql sql performance query-optimization


【解决方案1】:

如前所述,您可以使用案例并在一个查询中执行所有操作,但是您不需要使用子查询,尽管我不是它的忠实粉丝,MySQL 允许您在分组,所以以下将起作用:

SELECT  CASE WHEN (enrolled = 1 AND age >=15 AND age < 20) THEN '15-19'
            WHEN (enrolled = 1 AND age >=20 AND age < 25) THEN '20-24'
            WHEN (enrolled = 1 AND age >=25 AND age < 30) THEN '25-29'
            WHEN (enrolled = 1 AND age >=30) THEN '30+'
            ELSE 'Other'
        END AS AgeRange 
        COUNT(id) AS user_list 
FROM    tbl_users
WHERE   Enrolled = 1
AND     Age >= 15
GROUP BY AgeRange;

注意,我仍然添加了 where 子句以避免扫描冗余数据。

Example on SQL Fiddle

但是,如果您缺少数据,例如将返回一个空表,而不是这个:

AGERANGE    USER_LIST
15-19       0
20-24       0
25-29       0
30+         0

正如您所料。为了解决这个问题,我将创建一个伪表以供选择,然后加入您的数据:

SELECT  t.Name AS Age_Range,
        COUNT(u.ID) AS User_List
FROM    (   SELECT  '15-19' AS Name, 15 AS LowerBound, 20 AS UpperBound, 1 AS Enrolled
            UNION ALL
            SELECT  '20-24' AS Name, 20 AS LowerBound, 25 AS UpperBound, 1 AS Enrolled
            UNION ALL
            SELECT  '25-29' AS Name, 25 AS LowerBound, 30 AS UpperBound, 1 AS Enrolled
            UNION ALL
            SELECT  '30+' AS Name, 30 AS LowerBound, 9999999 AS UpperBound, 1 AS Enrolled
        ) t
        LEFT JOIN tbl_Users u
            ON u.Enrolled = t.enrolled
            AND u.Age >= t.LowerBound
            AND u.Age < t.UpperBound
GROUP BY t.Name;

Example on SQL Fiddle

【讨论】:

    【解决方案2】:

    试试这个方法

    create table t (list1 int,list2 int,list3 int,list4 int)
    
    insert into t
    SELECT 
    SUM(case when age >=15 AND age < 20 then 1 else 0 end) AS user_list1,
    SUM(case when age >=20 AND age < 25 then 1 else 0 end) AS user_list2,
    SUM(case when age >=25 AND age < 30 then 1 else 0 end) AS user_list3,
    SUM(case when age >=15 AND age < 20 then 1 else 0 end) AS user_list4
    FROM tbl_users
    WHERE enrolled = 1 ;
    
    select list1 from t union all
    select list2 from t union all
    select list3 from t union all
    select list4 from t 
    

    【讨论】:

    • 我正在尝试使用它,但是我收到此错误:错误代码:1064 您的 SQL 语法有错误;检查与您的 MySQL 服务器版本相对应的手册以获取正确的语法,以便在第 1 行的 'declare @t table (list1 int,list2 int) insert into @t SELECT SUM(case when' 附近使用。应该已经创建了表吗?
    • 1 个查询已执行,0 个成功,1 个错误,0 个警告 查询:声明 @t 表 (list1 int,list2 int) 插入到 @t SELECT SUM(case when ((start_time >= '2013- 08-02 00:00:15' AND start_time ... 错误代码:1064 您的 SQL 语法有错误;请查看与您的 MySQL 服务器版本相对应的手册,以了解在 'declare @t table ( list1 int,list2 int) 插入@t SELECT SUM(case when' at line 1 执行时间:0 秒传输时间:0 秒总时间:0 秒
    【解决方案3】:

    你可以使用联合:

    SELECT COUNT(id) AS user_list
      FROM tbl_users
     WHERE (enrolled = 1 AND age >= 15 AND age < 20)
    union
    SELECT COUNT(id)
      FROM tbl_users
     where (enrolled = 1 AND age >= 20 AND age < 25)
    union
    SELECT COUNT(id)
      FROM tbl_users
     where (enrolled = 1 AND age >= 25 AND age < 30)
    union
    SELECT COUNT(id) FROM tbl_users where (enrolled = 1 AND age >= 30)
    

    【讨论】:

    • 我想避免使用 UNION,因为这会导致查询运行很多次,这会使其变慢
    • 并非总是如此。将多个 where 子句与大量 ORs 组合起来基本上会打乱执行计划,因此 4 个单独的查询可能每个都能够利用索引,组合子句会导致表扫描。这完全取决于数据的复杂性和宽度。不幸的是,只有您可以通过测试这两种方法并检查计划来回答这个问题。
    【解决方案4】:

    使用 4 个简单的查询而不是一个复杂的查询应该不会有性能问题

    【讨论】:

      【解决方案5】:

      以下 SQL 子句可以提供帮助:

      Group byCaseSubqueries...查找它们并与它们一起玩,自己弄清楚应该很有趣...

      您可以考虑在查询中使用另一列(例如,15 到 18 之间的大小写,然后是 1,大小写...然后是 2,...)作为 Range,标识一个范围并按此分组...

      这是一个示例,说明如何将您的范围标识为子查询中的列,并在按子查询中的范围分组的列之上进行查询:

      select count(*) as Count, r as Range 
          from 
              (select 
              (case 
              when enrolled = 1 AND age >=20 AND age < 25 then 'r1' 
              when enrolled = 1 AND age >=25 AND age < 30 then 'r2' 
              end) as r 
              from Text)          
          as groups 
      group by r
      

      这会导致以下结果:

      范围 |数

      r1 | 10

      r2 | 7

      其中 r1 可以是与“enrolled = 1 AND age >=20 AND age

      【讨论】:

      • 谢谢,你能不能给我一个简单的例子,因为我不太明白。
      【解决方案6】:

      这很简单

      select count(1),ceil(age/5)*5 from tbl_users where enrolled=1 group by ceil(age/5)

      【讨论】:

        猜你喜欢
        • 2011-08-31
        • 2023-04-09
        • 1970-01-01
        • 2013-12-30
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-10-28
        • 1970-01-01
        相关资源
        最近更新 更多