Как получить несколько счетчиков с одним SQL-запросом?

Мне интересно, как написать этот запрос.

Я знаю, что настоящий синтаксис является фиктивным, но он поможет вам понять, чего я хочу. Мне нужно это в этом формате, потому что это часть гораздо большего запроса.

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

Мне нужно, чтобы все было возвращено в одном запросе.

Кроме того, он должен быть в одной строке, поэтому следующее не будет работать:

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

Ответ 1

Вы можете использовать оператор CASE с агрегатной функцией. По сути, это то же самое, что и функция 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

Ответ 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 ;

EDIT:
 См. @KevinBalmforth о снижении производительности за то, почему вы, вероятно, не хотите использовать этот метод, и вместо этого должны выбрать ответ @bluefeet. Я оставляю это, чтобы люди могли понять их варианты.

Ответ 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, только если ваше условие выполнено.

Ответ 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 будет выполнять несколько сканирований таблиц.

В качестве примера запустите следующий script:

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 Clustered Index Seeks, но они все еще дороже, чем одно кластерное сканирование индексов. Я пробовал то же самое на столе с 8 миллионами строк, а второй SELECT по-прежнему намного дороже.

Ответ 5

Для mysql это можно сократить до

select distributor_id,
    count(*) total,
    sum(level = 'exec') ExecCount,
    sum(level = 'personal') PersonalCount
from yourtable
group by distributor_id

Ответ 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;

Вы получите счет для каждого уровня и должны суммировать их все, чтобы получить общее количество.

Ответ 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   |
-------------------

Ответ 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