2009-02-24 9 views
123

Je construis une requête avec une clause GROUP BY qui a besoin de la capacité de compter les enregistrements en fonction seulement sur une certaine condition (par exemple ne comptabilisent que les enregistrements dans lesquels une certaine valeur de la colonne est égale à 1).Sql Server équivalent d'une fonction d'agrégation COUNTIF

SELECT UID, 
     COUNT(UID) AS TotalRecords, 
     SUM(ContractDollars) AS ContractDollars, 
     (COUNTIF(MyColumn, 1)/COUNT(UID) * 100) -- Get the average of all records that are 1 
FROM dbo.AD_CurrentView 
GROUP BY UID 
HAVING SUM(ContractDollars) >= 500000 

La ligne COUNTIF() échoue évidemment car il n'y a pas de fonction SQL natif appelé COUNTIF, mais l'idée est ici de déterminer le pourcentage de toutes les lignes qui ont la valeur « 1 » pour MaColonne. Avez-vous des idées sur la façon de l'implémenter correctement dans un environnement MS SQL 2005?

Répondre

241

Vous pouvez utiliser un SUM (non COUNT!) Combinée à une déclaration CASE, comme ceci:

SELECT SUM(CASE WHEN myColumn=1 THEN 1 ELSE 0 END) 
FROM AD_CurrentView 

Note: dans mon propre test NULL s ne sont pas un problème, même si cela peut être dépendant de l'environnement. Vous pouvez manipuler des nulls tels que:

SELECT SUM(CASE WHEN ISNULL(myColumn,0)=1 THEN 1 ELSE 0 END) 
FROM AD_CurrentView 
+0

(Je sais que l'OP a posé des questions sur MS SQL, mais juste un petit commentaire pour les utilisateurs de SQLite) SQLite n'a pas de 'ISNULL', vous pouvez faire' CASE WHEN myColumn IS NULL' ou utiliser 'ifnull' (https://stackoverflow.com/a/799406/1861346) – Matt

43

Je fais habituellement ce que Josh a recommandé, mais j'ai réfléchi et testé une alternative légèrement hokey que j'avais envie de partager.

Vous pouvez tirer profit du fait que COUNT (NomColonne) ne compte pas NULLs et utiliser quelque chose comme ceci:

SELECT COUNT(NULLIF(0, myColumn)) 
FROM AD_CurrentView 

NULLIF - retourne NULL si les deux valeurs passé sont les mêmes. Avantage: exprime votre intention de COUNT lignes au lieu d'avoir la notation SUM(). Inconvénient: Pas aussi clair comment cela fonctionne ("magie" est généralement mauvaise).

+2

Cette solution peut donner des réponses différentes que la somme quand un groupe ne contient que des nulls, il en résulte 1 au lieu de 0. – KimvdLinde

+0

Ancien poste, mais merci cela a aidé. J'ai étendu la magie et contourné le problème des "seuls Nuls" en ajoutant 'ISNULL' comme suit:' SELECT COUNT (NULLIF (0, ISNULL (myColumn, 0))) '. Attendez, cela semble juste moche ... – pcdev

16

J'utiliserais cette syntaxe. Il réalise la même chose que les suggestions de Josh et Chris, mais avec l'avantage, il est conforme à ANSI et n'est pas lié à un fournisseur de base de données particulier.

select count(case when myColumn = 1 then 1 else null end) 
from AD_CurrentView 
+2

La réponse de Chris est conforme à SQL Stndard (indice: 'NULLIF' est inclus Standard SQL-92). La réponse de Josh peut être facilement transformée en SQL standard en remplaçant 'isnull' par' COALESCE'. – onedaywhen

+0

En fait, j'aime mieux cette réponse, car elle a l'idée de "compter les lignes" que Chris affichait, mais elle est plus extensible, puisque vous pouvez utiliser n'importe quel opérateur de comparaison; pas seulement '='. Je l'utilise pour "compter le nombre de réponses> = 2". –

0

Non spécifique à un produit, mais le standard SQL fournit

SELECT COUNT() FILTER WHERE <condition-1>, COUNT() FILTER WHERE <condition-2>, ... FROM ...

à cet effet. Ou quelque chose qui lui ressemble beaucoup, je ne sais pas du haut de mon chapeau.

Et les vendeurs de cours préfèrent rester avec leurs solutions propriétaires.

+1

Je n'avais jamais entendu parler de ça auparavant, alors j'ai regardé ça. Selon http://modern-sql.com/feature/filter, le seul SGBD majeur qui offre réellement la clause 'FILTER' est PostgreSQL, mais il est émulé par' CASE' dans chacun d'entre eux. –

0

Pourquoi ne pas aimer ça?

SELECT count(1) 
FROM AD_CurrentView 
WHERE myColumn=1 
+0

Parce qu'il a besoin de beaucoup plus que du nombre. Il essaie d'obtenir le compte d'une partie d'un groupe, puis un agrégat de tout le groupe, ce que vous ne pouvez pas faire avec un WHERE. –

1

Ajout à la réponse de Josh,

SELECT COUNT(CASE WHEN myColumn=1 THEN AD_CurrentView.PrimaryKeyColumn ELSE NULL END) 
FROM AD_CurrentView 

ont bien fonctionné pour moi (dans SQL Server 2012) sans changer le « comte » à une « somme » et la même logique est portable à d'autres ' agrégats conditionnels ». Par exemple., Sommant basée sur une condition:

SELECT SUM(CASE WHEN myColumn=1 THEN AD_CurrentView.NumberColumn ELSE 0 END) 
FROM AD_CurrentView 
0

Que diriez-vous

SELECT id, COUNT(IF status=42 THEN 1 ENDIF) AS cnt 
FROM table 
GROUP BY table 

Plus court que CASE :)

fonctionne parce que COUNT() ne compte pas les valeurs NULL et IF/CASE return null lorsque la condition n'est pas rencontré et il n'y a pas ELSE.

Je pense que c'est mieux que d'utiliser SUM().

0

J'ai dû utiliser COUNTIF() dans mon cas dans le cadre de mes colonnes SELECT ET pour imiter un% du nombre de fois que chaque élément est apparu dans mes résultats.

J'utilisé cette ...

SELECT COL1, COL2, ... ETC 
     (1/SELECT a.vcount 
      FROM (SELECT vm2.visit_id, count(*) AS vcount 
        FROM dbo.visitmanifests AS vm2 
        WHERE vm2.inactive = 0 AND vm2.visit_id = vm.Visit_ID 
        GROUP BY vm2.visit_id) AS a)) AS [No of Visits], 
     COL xyz 
FROM etc etc 

Bien sûr, vous aurez besoin de formater le résultat en fonction de vos besoins d'affichage.

Questions connexes