Jak uzyskać wiele zliczeń za pomocą jednego zapytania SQL?

Zastanawiam się jak napisać to zapytanie.

Wiem, że ta składnia jest fałszywa, ale pomoże Ci zrozumieć, czego chcę. Potrzebuję go w tym formacie, ponieważ jest częścią znacznie większego zapytania.
SELECT distributor_id, 
COUNT(*) AS TOTAL, 
COUNT(*) WHERE level = 'exec', 
COUNT(*) WHERE level = 'personal'

Chcę to wszystko zwrócić w jednym zapytaniu.

Również, musi być w jednym rzędzie, więc następujące nie będą działać:

'SELECT distributor_id, COUNT(*)
GROUP BY distributor_id'
Author: Taryn, 2012-10-09

9 answers

Możesz użyć instrukcji CASE z funkcją zagregowaną. Jest to w zasadzie to samo co Funkcja PIVOT w niektórych RDBMS:

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
 468
Author: Taryn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-08 21:35:06

One way which work for sure

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:
Zobacz załamanie wydajności @KevinBalmforth, dlaczego prawdopodobnie nie chcesz używać tej metody i zamiast tego powinieneś wybrać odpowiedź @ bluefeet. Zostawiam to, żeby ludzie mogli zrozumieć swoje opcje.

 60
Author: NotMe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-05 14:37:57
SELECT 
    distributor_id, 
    COUNT(*) AS TOTAL, 
    COUNT(IF(level='exec',1,null)),
    COUNT(IF(level='personal',1,null))
FROM sometable;

COUNT liczy się tylko non null wartości, a DECODE zwróci wartość inną niż null 1 tylko wtedy, gdy twój warunek jest spełniony.

 32
Author: Majid Laissi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-08 21:10:23

Dla mysql można to skrócić do

select distributor_id,
    count(*) total,
    sum(level = 'exec') ExecCount,
    sum(level = 'personal') PersonalCount
from yourtable
group by distributor_id
 16
Author: Mihai,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-03 16:12:57

Bazując na innych opublikowanych odpowiedziach.

Obie z nich będą produkować odpowiednie wartości:

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 ; 

Jednak wydajność jest zupełnie inna, co oczywiście będzie bardziej istotne w miarę wzrostu ilości danych.

Odkryłem, że zakładając, że nie zdefiniowano indeksów w tabeli, zapytanie używając Sum wykonałoby skanowanie pojedynczej tabeli, podczas gdy zapytanie z licznikami wykonałoby skanowanie wielu tabel.

Jako przykład uruchom następujące skrypt:

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)

Zaznacz 2 polecenia SELECT i kliknij ikonę Wyświetl szacowany plan realizacji. Zobaczysz, że pierwsza instrukcja wykona jedno skanowanie tabeli, a druga zrobi 4. Oczywiście jedno skanowanie tabeli jest lepsze niż 4.

Dodanie indeksu klastrowego jest również interesujące. Np.

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

Pierwszy wybór powyżej wykona pojedynczy skan indeksu klastrowego. Drugi SELECT zrobi 4 Clustered Index szuka, ale są one nadal droższe niż pojedyncza klastrowa Skanowanie Indeksu. Próbowałem tego samego na stole z 8 milionami wierszy, a drugi SELECT był nadal dużo droższy.

 15
Author: Kevin Balmforth,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-12-03 16:29:50

Cóż, jeśli musisz mieć to wszystko w jednym zapytaniu, możesz zrobić związek:

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';

Lub, jeśli możesz to zrobić po przetworzeniu:

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

Otrzymasz liczbę dla każdego poziomu i trzeba zsumować je wszystkie, aby uzyskać sumę.

 7
Author: CrazyCasta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-08 21:03:50

Robię coś takiego, gdzie po prostu nadaję każdej tabeli nazwę łańcucha znaków, aby ją zidentyfikować w kolumnie A i policzyć kolumnę. Potem łączę je wszystkie, żeby się układały. Wynik jest moim zdaniem ładny - Nie wiem, jak wydajny jest w porównaniu z innymi opcjami, ale dostałem to, czego potrzebowałem.

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;

Wynik:

-------------------
| String  | Count |
-------------------
| table1  | 123   |
| table2  | 234   |
| table3  | 345   |
| table4  | 456   |
| table5  | 567   |
-------------------
 3
Author: Frantumn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-09-20 19:07:36

Na podstawie zaakceptowanej odpowiedzi Bluefeet z dodanym niuansem za pomocą 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

Użycie OVER() z niczym w () Da całkowitą liczbę dla całego zbioru danych.

 0
Author: mentorrory,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-09 19:54:51

Myślę, że to może również działać dla ciebie select count(*) as anc,(select count(*) from Patient where sex='F')as patientF,(select count(*) from Patient where sex='M') as patientM from anc

A także możesz wybrać i policzyć powiązane tabele, takie jak Ta 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

 0
Author: Sinte,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-04-13 07:10:58