web-dev-qa-db-fra.com

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

Je construis une requête avec une clause GROUP BY qui nécessite la possibilité de compter les enregistrements uniquement en fonction d'une condition donnée (par exemple, ne comptabiliser que les enregistrements pour lesquels la valeur d'une 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 ligne COUNTIF() échoue évidemment car il n’existe pas de fonction SQL native appelée COUNTIF, mais l’idée ici est de déterminer le pourcentage de toutes les lignes ayant la valeur «1» pour MyColumn.

Avez-vous des idées sur la manière de mettre en œuvre correctement cela dans un environnement MS SQL 2005?

145
senfo

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

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

Remarque: dans mon propre test, NULLs n'é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
294
JoshBerke

Je fais habituellement ce que Josh avait recommandé, mais j'ai fait un brainstorming et testé une alternative légèrement hokey que j'avais envie de partager.

Vous pouvez tirer parti du fait que COUNT (ColumnName) ne compte pas les valeurs NULL et utilisez 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 compter les lignes au lieu de la notation SUM () . Inconvénient: son fonctionnement n'est pas aussi clair (la "magie" est généralement mauvaise).

45
Chris Shaffer

Je voudrais utiliser cette syntaxe. Cela répond aux suggestions de Josh et Chris, mais avec l'avantage, il est compatible 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
18
asgeo1

Ajoutant à la réponse de Josh,

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

A bien fonctionné pour moi (dans SQL Server 2012) sans changer le "compte" en "somme" et la même logique est transférable vers d'autres "agrégats conditionnels". Exemple: sommation basée sur une condition:

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

Pourquoi pas comme ça?

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

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 que IF/CASE renvoie null lorsque la condition n'est pas remplie et qu'il n'y a pas de ELSE.

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

0
maf-soft

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

0
Fandango68

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 de près, je ne le sais pas du tout.

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

0
Erwin Smout