如何获得一个SQL查询多个计数?
我想知道如何写这个查询。
我知道这个实际的语法是假的,但它会帮助你理解我想要的。 我需要这种格式,因为它是一个更大的查询的一部分。
SELECT distributor_id, COUNT(*) AS TOTAL, COUNT(*) WHERE level = 'exec', COUNT(*) WHERE level = 'personal'
我需要这一切返回一个查询。
此外,它需要在一行,所以下面不会工作:
'SELECT distributor_id, COUNT(*) GROUP BY distributor_id'
您可以使用具有聚合函数的CASE
语句。 这与一些RDBMS中的PIVOT
函数基本相同:
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 (SELECT DISTINCT distributor_id FROM myTable) a ;
编辑:
请参阅@ KevinBalmforth的性能分解为什么你可能不想使用这种方法,而应该select@ bluefeet的答案。 我要离开这个,所以人们可以理解他们的select。
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
。
对于MySQL这可以缩短到
select distributor_id, count(*) total, sum(level = 'exec') ExecCount, sum(level = 'personal') PersonalCount from yourtable group by distributor_id
build立在其他张贴的答案。
这两个都会产生正确的值:
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语句,然后单击Display Estimated Execution Plan图标。 你会看到,第一个语句将执行一个表扫描,第二个将执行4.显然,一个表扫描比4好。
添加聚集索引也很有趣。 例如
Create clustered index t1f1 on t1(f1); Update Statistics t1;
上面的第一个SELECT将执行单个聚集索引扫描。 第二个SELECT将执行4个群集索引search,但是它们比单个群集索引扫描还要贵。 我在800万行的桌子上试过同样的东西,第二个SELECT仍然要贵很多。
那么,如果你必须在一个查询中完成所有工作,你可以做一个联合:
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;
你会得到每个级别的计数,需要总结他们所有的总和。
我做了这样的事情,我只是给每个表一个string名称在列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 | -------------------
根据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()
中没有任何内容的情况下使用OVER()
会给你整个数据集的总数。