web-dev-qa-db-fra.com

Comment obtenir plusieurs comptes avec une requête SQL?

Je me demande comment écrire cette requête.

Je sais que cette syntaxe est fausse, mais cela vous aidera à comprendre ce que je veux. J'en ai besoin dans ce format, car cela fait partie d'une requête beaucoup plus grande.

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

J'ai besoin de tout cela en une seule requête.

En outre, il doit être placé sur une seule ligne, pour que les éléments suivants ne fonctionnent pas:

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

Vous pouvez utiliser une instruction CASE avec une fonction d'agrégat. C'est fondamentalement la même chose qu'une fonction PIVOT dans un SGBDR:

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
575
Taryn

Une façon qui fonctionne à coup sûr

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:
Voir la ventilation des performances de @ KevinBalmforth pour expliquer pourquoi vous ne souhaitez probablement pas utiliser cette méthode et devriez plutôt opter pour la réponse de @ bluefeet. Je laisse ça pour que les gens comprennent leurs options.

75
NotMe
SELECT 
    distributor_id, 
    COUNT(*) AS TOTAL, 
    COUNT(IF(level='exec',1,null)),
    COUNT(IF(level='personal',1,null))
FROM sometable;

COUNT ne compte que non null valeurs et la DECODE renverra une valeur non nulle 1 uniquement si votre condition est remplie.

36
Majid Laissi

S'appuyant sur d'autres réponses postées.

Les deux produiront les bonnes valeurs:

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 ; 

Cependant, la performance est assez différente, ce qui sera évidemment plus pertinent à mesure que la quantité de données augmente.

J'ai constaté que, en supposant qu'aucun index n'était défini sur la table, la requête utilisant les SUM effectuait une analyse de table unique, alors que la requête avec les COUNT effectuait plusieurs analyses de table.

Par exemple, exécutez le script suivant:

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)

Mettez en surbrillance les 2 instructions SELECT et cliquez sur l’icône Afficher le plan d’exécution estimé. Vous verrez que la première instruction effectuera une analyse de table et la seconde en fera 4. De toute évidence, une analyse de table est meilleure que 4.

L'ajout d'un index clusterisé est également intéressant. Par exemple.

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

Le premier SELECT ci-dessus fera un seul balayage d'index clusterisé. Le second SELECT fera 4 recherches d'index en cluster, mais elles sont toujours plus chères qu'une analyse d'index en cluster unique. J'ai essayé la même chose sur une table avec 8 millions de lignes et le second SELECT était encore beaucoup plus cher.

22
Kevin Balmforth

Pour MySQL, cela peut être raccourci à

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

Eh bien, si vous devez tout avoir dans une requête, vous pouvez créer une union:

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

Ou, si vous pouvez le faire après le traitement:

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

Vous obtiendrez le nombre pour chaque niveau et devrez les résumer pour obtenir le total.

10
CrazyCasta

Je fais quelque chose comme ceci où je donne juste à chaque table un nom de chaîne pour l'identifier dans la colonne A, et un compte pour la colonne. Ensuite, je les unit tous pour qu’ils s’empilent. À mon avis, le résultat est joli. Je ne suis pas sûr de son efficacité par rapport aux autres options, mais cela m'a apporté ce dont j'avais besoin.

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;

Résultat:

-------------------
| String  | Count |
-------------------
| table1  | 123   |
| table2  | 234   |
| table3  | 345   |
| table4  | 456   |
| table5  | 567   |
-------------------
5
Frantumn

Basé sur la réponse acceptée de Bluefeet avec une nuance supplémentaire en utilisant 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

En utilisant OVER() sans rien dans le (), vous obtiendrez le nombre total de données pour l'ensemble du jeu de données.

2
mentorrory

Je pense que cela peut aussi marcher pour vous select count(*) as anc,(select count(*) from Patient where sex='F')as patientF,(select count(*) from Patient where sex='M') as patientM from anc

et vous pouvez aussi sélectionner et compter les tables liées comme ceci 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

1
Sinte