如何通过一个SQL查询获得多个计数?


315

我想知道如何编写此查询。

我知道这个实际的语法是虚假的,但是它将帮助您了解我想要的内容。我需要这种格式,因为它是更大查询的一部分。

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

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

此外,它必须排成一排,因此以下内容将不起作用:

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

1
您的此查询工作正常吗?SELECT distributor_id, COUNT(*) AS TOTAL, COUNT(*) WHERE level = 'exec', COUNT(*) WHERE level = 'personal'
Pratik

Answers:


689

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

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

55
太棒了,这太神奇了。好答案。只是给那些在这里跌跌撞撞的人的笔记。Count将对所有行进行计数,与case语句一起使用时,总和将与计数做相同的事情。
约翰·巴林杰

1
出色的解决方案!值得一提的是,如果在一个查询中将许多表组合在一起,则此方法的效果同样好,因为在这种情况下使用子查询会变得很混乱。
达伦·克拉布

7
感谢您提供的非常优雅的解决方案。顺便说一句,这也适用于TSQL。
安妮·拉冈

6
为什么这可能不是最佳答案:始终进行全表扫描。考虑一个count-subqueries的联接,或者一个select中的嵌套count。但是,由于没有索引,因此最好这样做,因为您只能保证一个表扫描与多个表扫描。请参阅@KevinBalmforth的答复
YoYo

1
@JohnBallinger,'计数将计数所有行'- COUNT将计数distributor_id明智。不是表格的所有行,对不对?
Istiaque Ahmed

87

一种肯定有效的方法

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♦的答案。我离开这是为了让人们可以理解他们的选择。


2
这帮助我解决了如何进行多个计数并将其在单个SELECT语句中输出的情况,每个计数都是一列。效果很好-谢谢!
马克

2
在我的一个项目中,我能够使用您在此处提供的内容。现在,所有内容都在单个查询中,而不是多个查询中。该页面的加载时间不到一秒钟,而多次查询的加载时间为5-8秒。爱它。谢谢,Notme。
韦恩·巴伦

1
如果每个子查询实际上命中一个索引,这可能会很好地工作。如果不是,sum(case...)则应考虑解决方案。
YoYo

1
请注意,作为我进行更正的一种替代方法,您也可以/更好地使用group bycount(*)@Mihai所示的简单替换整个嵌套查询的好处-进一步简化了MySQL语法。
YoYo

43
SELECT 
    distributor_id, 
    COUNT(*) AS TOTAL, 
    COUNT(IF(level='exec',1,null)),
    COUNT(IF(level='personal',1,null))
FROM sometable;

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


distributor_id将查询节目?总共显示1行。
Istiaque Ahmed

OP在我的答案中省略的列上有一个分组依据。
Majid Laissi

您救了我的命,所有其他请求者在MySQL中返回多行。非常感谢
Abner

1
@Abner很高兴这在8年后仍然有帮助:)
Majid Laissi

@MajidLaissi是的,将查询时间从一分钟更改为不到一秒钟。:)
艾伯纳

25

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

两者都会产生正确的值:

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的查询将执行单个表扫描,而使用COUNTs的查询将进行多个表扫描。

例如,运行以下脚本:

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仍然昂贵得多。


23

对于MySQL,可以缩短为:

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

1
此查询中是否真的需要“ group by distributor_id”?它也可以不使用它吗
user1451111

2
@ user1451111原始问题得到了,所以它的答案取决于问题本身
Al-Mothafar

11

好吧,如果必须在一个查询中全部包含所有内容,则可以进行并集:

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(*)函数多个实例的报告时非常有帮助。
James O

结果显示#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
Istiaque Ahmed

应用UNION的所有查询返回的列数应相等。@IstiaqueAhmed可能就是您的错误背后的原因。
user1451111

给以后偶然发现此答案的任何人的注释。当“级别”列中的某些值为NULL时,此处描述的“后处理”技术可能会导致问题。在这种情况下,所有子计数的总和将不等于总行数。
user1451111

6

我做这样的事情,我只给每个表一个字符串名称以在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   |
-------------------

1
a query that I created makes ...-那查询在哪里?
Istiaque Ahmed

2
如何在所有表中添加位置caluse

3

基于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()在()中不加任何使用将为您提供整个数据集的总数。


1

我认为这也可以为您服务 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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.