¿Cómo obtener múltiples cuentas con una consulta SQL?


Me pregunto cómo escribir esta consulta.

Sé que esta sintaxis real es falsa, pero te ayudará a entender lo que quiero. Lo necesito en este formato, porque es parte de una consulta mucho más grande.

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

Necesito todo esto devuelto en una consulta.

Además, debe estar en una fila, por lo que lo siguiente no funcionará:

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

9 answers

Puede usar una instrucción CASE con una función aggregate. Esto es básicamente lo mismo que una función PIVOT en algunos 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-08 21:35:06

Una forma que funciona con seguridad

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 ;

EDITAR:
Vea el desglose de rendimiento de @KevinBalmforth para saber por qué probablemente no desea usar este método y en su lugar debe optar por la respuesta de @bluefeet. Voy a dejar esto para que la gente pueda entender sus opciones.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
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 solo cuenta los valores non null y el DECODE devolverá un valor no nulo 1 solo si se cumple su condición.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-08 21:10:23

Para mysql esto se puede acortar a

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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-03 16:12:57

Basado en otras respuestas publicadas.

Ambos producirán los valores correctos:

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 ; 

Sin embargo, el rendimiento es bastante diferente, lo que obviamente será más relevante a medida que crezca la cantidad de datos.

Descubrí que, suponiendo que no se definieran índices en la tabla, la consulta que usa las sumas haría un solo análisis de la tabla, mientras que la consulta con los recuentos haría múltiples análisis de la tabla.

Como ejemplo, ejecute lo siguiente 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)

Resalte las 2 instrucciones SELECT y haga clic en el icono Mostrar Plan de ejecución Estimado. Verá que la primera instrucción hará un escaneo de tabla y la segunda hará 4. Obviamente, un escaneo de tabla es mejor que 4.

Agregar un índice agrupado también es interesante. Por ejemplo,

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

La primera SELECCIÓN anterior hará un único Análisis de Índice Agrupado. La segunda SELECCIÓN hará 4 Búsquedas de Índices Agrupados, pero aún son más caras que una sola Agrupación Escaneo de Índice. Intenté lo mismo en una mesa con 8 millones de filas y la segunda SELECCIÓN seguía siendo mucho más cara.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-12-03 16:29:50

Bueno, si usted debe tener todo en una consulta, usted podría hacer una unión:

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

O, si puede hacerlo después del procesamiento:

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

Obtendrás el recuento de cada nivel y tendrás que sumarlos todos para obtener el total.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-10-08 21:03:50

Hago algo como esto donde solo le doy a cada tabla un nombre de cadena para identificarla en la columna A, y un recuento para la columna. Entonces los uniré a todos para que se apilen. El resultado es bastante en mi opinión-no estoy seguro de lo eficiente que es en comparación con otras opciones, pero me consiguió lo que necesitaba.

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;

Resultado:

-------------------
| 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-20 19:07:36

Basado en la respuesta aceptada de Bluefeet con un matiz añadido usando 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

Usando OVER() sin nada en el () le dará el recuento total para todo el conjunto de datos.

 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-09 19:54:51

Creo que esto también puede funcionar para usted select count(*) as anc,(select count(*) from Patient where sex='F')as patientF,(select count(*) from Patient where sex='M') as patientM from anc

Y también puedes seleccionar y contar tablas relacionadas como esta 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/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-13 07:10:58