Sql Server équivalent d'une fonction d'agrégation COUNTIF

164

Je construis une requête avec une GROUP BYclause qui a besoin de la capacité de compter les enregistrements basés uniquement sur une certaine condition (par exemple, ne comptez que les enregistrements où une certaine valeur de colonne est égale à 1).

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 COUNTIF()ligne échoue évidemment car il n'y a pas de fonction SQL native appelée COUNTIF, mais l'idée ici est de déterminer le pourcentage de toutes les lignes qui ont la valeur «1» pour MyColumn.

Des idées sur la façon de mettre en œuvre correctement cela dans un environnement MS SQL 2005?

senfo
la source

Réponses:

339

Vous pouvez utiliser un SUM(pas COUNT!) Combiné avec une CASEinstruction, comme ceci:

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

Remarque: dans mes propres tests, ce NULLn'était pas un problème, bien que cela puisse dépendre de l'environnement. Vous pouvez gérer des valeurs nulles telles que:

SELECT SUM(CASE WHEN ISNULL(myColumn,0)=1 THEN 1 ELSE 0 END)
FROM AD_CurrentView
JoshBerke
la source
3
(Je sais que l'OP a posé des questions sur MS SQL, mais juste un petit commentaire pour les utilisateurs de SQLite faisant la même chose) SQLite n'a pas ISNULL, à la place, vous pouvez le faire CASE WHEN myColumn IS NULLou utiliser ifnull( stackoverflow.com/a/799406/1861346 )
Matt
54

Je fais généralement 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 profiter du fait que COUNT (ColumnName) ne compte pas les NULL et utiliser quelque chose comme ceci:

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

NULLIF - renvoie NULL si les deux valeurs transmises sont identiques.

Avantage: exprime votre intention de COUNT lignes au lieu d'avoir la notation SUM (). Inconvénient: son fonctionnement n'est pas aussi clair (la «magie» est généralement mauvaise).

Chris Shaffer
la source
2
Cette solution peut donner des réponses différentes de la somme lorsqu'un groupe ne contient que des valeurs nulles, cela donne 1 au lieu de 0.
KimvdLinde
Ancien message, mais merci, cela a aidé. Je tendis la magie et a obtenu autour du problème « que NULLS » en ajoutant ISNULLcomme suit: SELECT COUNT(NULLIF(0, ISNULL(myColumn, 0))). Attendez, ça a l'air moche ...
pcdev
1
Ce serait parfait s'il y avait une fonction
NULLIFNOT
21

J'utiliserais cette syntaxe. Il atteint la même chose que les suggestions de Josh et Chris, mais avec l'avantage qu'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
asgeo1
la source
2
La réponse de Chris est conforme à Stndard SQL (indice: NULLIFest inclus Standard SQL-92). La réponse de Josh peut être facilement transformée en SQL standard en remplaçant isnullpar COALESCE.
jour du
En fait, j'aime mieux cette réponse, car elle a l'idée de "compter les lignes" que Chris montrait, mais elle est plus extensible, car vous pouvez utiliser n'importe quel opérateur de comparaison; pas seulement =. Je l'utilise pour "compter le nombre de réponses> = 2".
Kristen Hammack
3

En plus de la réponse de Josh,

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

Cela a bien fonctionné pour moi (dans SQL Server 2012) sans changer le «nombre» en «somme» et la même logique est transférable à d'autres «agrégats conditionnels». Par exemple, sommation basée sur une condition:

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

Que diriez-vous

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

Plus court que CASE:)

Fonctionne car COUNT()ne compte pas les valeurs nulles et IF/ CASErenvoie null lorsque la condition n'est pas remplie et qu'il n'y en a pas ELSE.

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

maf-doux
la source
1

Pas spécifique au produit, mais le standard SQL fournit

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

dans ce but. Ou quelque chose qui lui ressemble beaucoup, je ne sais pas de haut en bas de mon chapeau.

Et bien sûr, les fournisseurs préféreront s'en tenir à leurs solutions propriétaires.

Erwin Smout
la source
1
Je n'avais jamais entendu parler de cela auparavant, alors j'ai recherché. Selon modern-sql.com/feature/filter, le seul SGBD majeur qui offre réellement la FILTERclause est PostgreSQL, mais il est émulé par CASEtous.
Kristen Hammack
1

Pourquoi pas comme ça?

SELECT count(1)
FROM AD_CurrentView
WHERE myColumn=1
Michal
la source
1
Parce qu'il a besoin de bien plus que le décompte. Il essaie d'obtenir le nombre de lignes d'une partie d'un groupe, puis un agrégat de l'ensemble du groupe, ce que vous ne pouvez pas faire avec un WHERE.
Kristen Hammack
1

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

Alors j'ai utilisé ça ...

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 devrez formater le résultat en fonction de vos besoins d'affichage.

Fandango68
la source
-2
SELECT COALESCE(IF(myColumn = 1,COUNT(DISTINCT NumberColumn),NULL),0) column1,
COALESCE(CASE WHEN myColumn = 1 THEN COUNT(DISTINCT NumberColumn) ELSE NULL END,0) AS column2
FROM AD_CurrentView
Andres Sarria
la source