Как получить несколько подсчетов с одним SQL-запросом?
мне интересно, как написать этот запрос.
Я знаю, что этот фактический синтаксис является фиктивным, но это поможет вам понять, что я хочу. Мне это нужно в таком формате, потому что это часть большого запроса.
SELECT distributor_id,
COUNT(*) AS TOTAL,
COUNT(*) WHERE level = 'exec',
COUNT(*) WHERE level = 'personal'
мне нужно, чтобы все это вернулось в одном запросе.
кроме того, он должен быть в одной строке, поэтому следующее не будет работать:
'SELECT distributor_id, COUNT(*)
GROUP BY distributor_id'
9 ответов:
можно использовать
CASE
оператор с агрегатной функцией. Это в основном то же самое как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 ;
EDIT:
См. @ kevinbalmforth's break down of performance для того, почему вы, вероятно, не хотите использовать этот метод и вместо этого должны выбрать ответ @bluefeet. Я оставляю это, чтобы люди могли понять свои варианты.
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
основываясь на других опубликованных ответах.
оба из них будут производить правильные значения:
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 ;
однако производительность совсем другая, что, очевидно, будет более актуальным по мере роста количества данных.
я обнаружил, что, предполагая, что индексы не были определены в таблице, запрос с использованием сумм будет выполнять одно сканирование таблицы, в то время как запрос с подсчетами будет выполнять несколько сканирований таблицы.
в качестве примера, выполните следующие сценарий:
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 будет делать 4 кластеризованных индексных поиска, но они все еще дороже чем один кластеризованный индекс сканирования. Я попробовал то же самое на таблице с 8 миллионами строк, и второй выбор был все еще намного дороже.
Ну, если вы должны иметь все это в одном запросе, вы можете сделать объединение:
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;
вы получите количество для каждого уровня и нужно суммировать их все, чтобы получить общее.
Я делаю что-то вроде этого, где я просто даю каждой таблице имя строки, чтобы идентифицировать его в столбце 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()
ничего в () даст вам общее количество для всего набора данных.
Я думаю, что это также работает для вас
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