【问题标题】:How to get multiple counts with one SQL query?如何通过一个 SQL 查询获得多个计数?
【发布时间】:2012-09-29 03:45:27
【问题描述】:

我想知道如何编写这个查询。

我知道这个实际的语法是假的,但它会帮助你理解我想要什么。 我需要这种格式,因为它是更大查询的一部分。

SELECT distributor_id, 
COUNT(*) AS TOTAL, 
COUNT(*) WHERE level = 'exec', 
COUNT(*) WHERE level = 'personal'

我需要在一个查询中返回所有这些。

另外,它需要在一行中,所以下面的行不通:

'SELECT distributor_id, COUNT(*)
GROUP BY distributor_id'

【问题讨论】:

  • 你的这个查询工作正常吗?? SELECT distributor_id, COUNT(*) AS TOTAL, COUNT(*) WHERE level = 'exec', COUNT(*) WHERE level = 'personal'

标签: mysql sql join count group-by


【解决方案1】:

您可以将CASE 语句与聚合函数一起使用。这与某些 RDBMS 中的 PIVOT 函数基本相同:

SELECT distributor_id,
    count(*) AS total,
    sum(case when level = 'exec' then 1 else 0 end) AS ExecCount,
    sum(case when level = 'personal' then 1 else 0 end) AS PersonalCount
FROM yourtable
GROUP BY distributor_id

【讨论】:

  • 太棒了,这太棒了。很好的答案。只是给在这里偶然发现的人的一个说明。 Count 将对所有行进行计数,当与 case 语句一起使用时,sum 的作用与 count 相同。
  • 绝妙的解决方案!可能值得注意的是,如果您在一个查询中将大量表组合在一起,则此方法同样有效,因为在这种情况下使用子查询可能会变得非常混乱。
  • 感谢这个非常优雅的解决方案。顺便说一句,这也适用于 TSQL。
  • 为什么这可能不是最佳答案:始终进行全表扫描。考虑计数子查询的连接,或选择中的嵌套计数。但是,在没有索引的情况下,这可能是最好的,因为您只保证了一次表扫描而不是多次表扫描。查看@KevinBalmforth 的回答
  • @JohnBallinger,'计数将计算所有行' - COUNT 将计数 distributor_id 明智。不是表格的所有行,对吧?
【解决方案2】:

一种肯定有效的方法

SELECT a.distributor_id,
    (SELECT COUNT(*) FROM myTable WHERE level='personal' and distributor_id = a.distributor_id) as PersonalCount,
    (SELECT COUNT(*) FROM myTable WHERE level='exec' and distributor_id = a.distributor_id) as ExecCount,
    (SELECT COUNT(*) FROM myTable WHERE distributor_id = a.distributor_id) as TotalCount
FROM (SELECT DISTINCT distributor_id FROM myTable) a ;

编辑:
请参阅@KevinBalmforth 的性能细分,了解为什么您可能不想使用此方法而应选择@Taryn♦ 的答案。我要离开这个,以便人们了解他们的选择。

【讨论】:

  • 这帮助我解决了如何进行多个计数并在单个 SELECT 语句中输出它们,每个计数都是一列。效果很好——谢谢!
  • 我可以在我的一个项目中使用您在此处提供的内容。现在一切都在一个查询中,而不是多个查询。页面加载时间不到一秒,而多个查询则需要 5-8 秒。爱它。谢谢,Notme。
  • 如果每个子查询实际上都命中一个索引,这可能会很好。如果不是,则应考虑sum(case...) 解决方案。
  • 请注意,作为 distinct 的替代方案,正如我已经做出的更正,您还可以/更好地使用group by,其好处是用简单的count(*) 替换整个嵌套查询作为@Mihai显示 - 进一步简化 MySQL 语法。
【解决方案3】:
SELECT 
    distributor_id, 
    COUNT(*) AS TOTAL, 
    COUNT(IF(level='exec',1,null)),
    COUNT(IF(level='personal',1,null))
FROM sometable;

COUNT 仅计算 non null 值,DECODE 仅在满足您的条件时才会返回非空值 1

【讨论】:

  • 查询将显示哪个distributor_id?它总共显示 1 行。
  • OP 在我的答案中省略的列上有一个分组依据。
  • 你救了我的命,所有其他 aswers 在 MySQL 中返回多行。非常感谢
  • @Abner 很高兴这在 8 年后仍然有帮助:)
  • @MajidLaissi 是的,它确实将我的查询时间从一分钟更改为不到一秒。 :)
【解决方案4】:

以其他已发布的答案为基础。

这两个都会产生正确的值:

select distributor_id,
    count(*) total,
    sum(case when level = 'exec' then 1 else 0 end) ExecCount,
    sum(case when level = 'personal' then 1 else 0 end) PersonalCount
from yourtable
group by distributor_id

SELECT a.distributor_id,
          (SELECT COUNT(*) FROM myTable WHERE level='personal' and distributor_id = a.distributor_id) as PersonalCount,
          (SELECT COUNT(*) FROM myTable WHERE level='exec' and distributor_id = a.distributor_id) as ExecCount,
          (SELECT COUNT(*) FROM myTable WHERE distributor_id = a.distributor_id) as TotalCount
       FROM myTable a ; 

但是,性能却大不相同,随着数据量的增长,这显然会更加相关。

我发现,假设没有在表上定义索引,使用 SUM 的查询会进行一次表扫描,而使用 COUNT 的查询会进行多次表扫描。

例如,运行以下脚本:

IF OBJECT_ID (N't1', N'U') IS NOT NULL 
drop table t1

create table t1 (f1 int)


    insert into t1 values (1) 
    insert into t1 values (1) 
    insert into t1 values (2)
    insert into t1 values (2)
    insert into t1 values (2)
    insert into t1 values (3)
    insert into t1 values (3)
    insert into t1 values (3)
    insert into t1 values (3)
    insert into t1 values (4)
    insert into t1 values (4)
    insert into t1 values (4)
    insert into t1 values (4)
    insert into t1 values (4)


SELECT SUM(CASE WHEN f1 = 1 THEN 1 else 0 end),
SUM(CASE WHEN f1 = 2 THEN 1 else 0 end),
SUM(CASE WHEN f1 = 3 THEN 1 else 0 end),
SUM(CASE WHEN f1 = 4 THEN 1 else 0 end)
from t1

SELECT 
(select COUNT(*) from t1 where f1 = 1),
(select COUNT(*) from t1 where f1 = 2),
(select COUNT(*) from t1 where f1 = 3),
(select COUNT(*) from t1 where f1 = 4)

突出显示 2 个 SELECT 语句并单击“显示估计执行计划”图标。你会看到第一条语句会进行一次表扫描,第二条语句会进行 4 次。显然,一次表扫描优于 4 次。

添加聚集索引也很有趣。例如

Create clustered index t1f1 on t1(f1);
Update Statistics t1;

上面的第一个 SELECT 将执行一次聚集索引扫描。第二个 SELECT 将执行 4 次聚集索引搜索,但它们仍然比单个聚集索引扫描更昂贵。我在一个有 800 万行的表上尝试了同样的事情,而第二个 SELECT 仍然要贵得多。

【讨论】:

    【解决方案5】:

    对于 MySQL,这可以缩短为:

    SELECT distributor_id,
        COUNT(*) total,
        SUM(level = 'exec') ExecCount,
        SUM(level = 'personal') PersonalCount
    FROM yourtable
    GROUP BY distributor_id
    

    【讨论】:

    • 在这个查询中“按distributor_id分组”真的有必要吗?没有它也可以工作
    • @user1451111 原始问题得到了它,所以答案取决于问题本身
    【解决方案6】:

    好吧,如果你必须在一个查询中包含所有内容,你可以做一个联合:

    SELECT distributor_id, COUNT() FROM ... UNION
    SELECT COUNT() AS EXEC_COUNT FROM ... WHERE level = 'exec' UNION
    SELECT COUNT(*) AS PERSONAL_COUNT FROM ... WHERE level = 'personal';
    

    或者,如果你可以做后处理:

    SELECT distributor_id, COUNT(*) FROM ... GROUP BY level;
    

    您将获得每个级别的计数,并且需要将它们全部相加以获得总数。

    【讨论】:

    • 发现UNION 在生成包含COUNT(*) 函数的多个实例的报告时非常有用。
    • 结果显示#1064 - You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ') FROM distributors UNION SELECT COUNT() AS EXEC_COUNT FROM distributors WHERE ' at line 1
    • 应用了 UNION 的所有查询返回的列数应该相等。 @IstiaqueAhmed 可能这就是你的错误背后的原因。
    • 给将来偶然发现此答案的任何人的说明。当 'level' 列中的某些值为 NULL 时,此处描述的 'After Processing' 技术可能会导致问题。在这种情况下,所有子计数的总和将不等于总行数。
    【解决方案7】:

    我做了这样的事情,我只是给每个表一个字符串名称以在 A 列中识别它,并为列提供一个计数。然后我将它们全部合并,以便它们堆叠。在我看来,结果非常好 - 不确定它与其他选项相比效率如何,但它让我得到了我需要的东西。

    select 'table1', count (*) from table1
    union select 'table2', count (*) from table2
    union select 'table3', count (*) from table3
    union select 'table4', count (*) from table4
    union select 'table5', count (*) from table5
    union select 'table6', count (*) from table6
    union select 'table7', count (*) from table7;
    

    结果:

    -------------------
    | String  | Count |
    -------------------
    | table1  | 123   |
    | table2  | 234   |
    | table3  | 345   |
    | table4  | 456   |
    | table5  | 567   |
    -------------------
    

    【讨论】:

    • a query that I created makes ...- 查询在哪里?
    • 如何在所有表格中添加 where caluse
    【解决方案8】:

    基于 Bluefeet 接受的回复,并使用OVER() 添加了细微差别:

    SELECT distributor_id,
        COUNT(*) total,
        SUM(case when level = 'exec' then 1 else 0 end) OVER() ExecCount,
        SUM(case when level = 'personal' then 1 else 0 end) OVER () PersonalCount
    FROM yourtable
    GROUP BY distributor_id
    

    使用 OVER() 且 () 中没有任何内容,将为您提供整个数据集的总数。

    【讨论】:

      【解决方案9】:

      我认为这也适用于你select count(*) as anc,(select count(*) from Patient where sex='F')as patientF,(select count(*) from Patient where sex='M') as patientM from anc

      你也可以像select count(*) as anc,(select count(*) from Patient where Patient.Id=anc.PatientId)as patientF,(select count(*) from Patient where sex='M') as patientM from anc这样选择和统计相关表格

      【讨论】:

        猜你喜欢
        • 2014-09-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-01-25
        • 1970-01-01
        • 1970-01-01
        • 2011-09-04
        相关资源
        最近更新 更多