Equivalente SQL Server di una funzione aggregata COUNTIF


164

Sto creando una query con una GROUP BYclausola che richiede la possibilità di contare i record solo in base a una determinata condizione (ad es. Contare solo i record in cui un determinato valore di colonna è uguale a 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()linea ovviamente fallisce poiché non viene chiamata alcuna funzione SQL nativa COUNTIF, ma l'idea qui è di determinare la percentuale di tutte le righe che hanno il valore '1' per MyColumn.

Qualche idea su come implementarlo correttamente in un ambiente MS SQL 2005?

Risposte:


339

Potresti usare un SUM(noCOUNT !) Combinato con CASEun'istruzione, in questo modo:

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

Nota: nei miei test NULLnon sono stati un problema, anche se questo può dipendere dall'ambiente. È possibile gestire valori null come:

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

3
(So ​​che l'OP ha chiesto di MS SQL, ma solo un piccolo commento per gli utenti di SQLite che fanno la stessa cosa) SQLite non ha ISNULL, invece puoi fare CASE WHEN myColumn IS NULLo usare ifnull( stackoverflow.com/a/799406/1861346 )
Matt,

54

Di solito faccio ciò che Josh mi ha raccomandato, ma ho fatto un brainstorming e testato un'alternativa leggermente hokey che mi andava di condividere.

Puoi trarre vantaggio dal fatto che COUNT (ColumnName) non conta i NULL e utilizzare qualcosa del genere:

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

NULLIF: restituisce NULL se i due valori passati sono uguali.

Vantaggio: esprime l'intenzione di COUNT righe invece di avere la notazione SUM (). Svantaggio: non è chiaro come funzioni (la "magia" di solito è male).


2
Questa soluzione può dare risposte diverse rispetto alla somma quando un gruppo contiene solo
valori

Vecchio post, ma grazie questo aiutato. Ho esteso la magia e ottenuto intorno al problema "NULLS solo" aggiungendo ISNULLsegue come: SELECT COUNT(NULLIF(0, ISNULL(myColumn, 0))). Aspetta, sembra proprio brutto ...
pcdev,

1
Sarebbe perfetto se ci fosse una funzione
NULLIFNOT

21

Vorrei usare questa sintassi. Accoglie gli stessi suggerimenti di Josh e Chris, ma con il vantaggio è conforme ANSI e non legato a un particolare fornitore di database.

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

2
La risposta di Chris è conforme a Stndard SQL (suggerimento: NULLIFè incluso SQL-92 standard). La risposta di Josh può essere facilmente trasformata in SQL standard sostituendola isnullcon COALESCE.
giorno

In realtà mi piace molto questa risposta, perché mi viene l'idea di "contare le righe" che Chris stava mostrando, ma è più estensibile, dato che puoi usare qualsiasi operatore di confronto; non solo =. Lo sto usando per "contare il numero di risposte> = 2".
Kristen Hammack,

3

Aggiungendo alla risposta di Josh,

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

Ha funzionato bene per me (in SQL Server 2012) senza cambiare il "conteggio" in un "somma" e la stessa logica è portabile con altri "aggregati condizionali". Ad esempio, sommando in base a una condizione:

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

2

Che ne dite di

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

Più corto di CASE:)

Funziona perché COUNT()non conta valori null e IF/ CASErestituisce null quando la condizione non è soddisfatta e non c'èELSE .

Penso che sia meglio dell'uso SUM().


1

Non specifico del prodotto, ma lo standard SQL fornisce

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

per questo scopo. O qualcosa che gli assomiglia da vicino, non lo so bene.

E ovviamente i venditori preferiranno attenersi alle loro soluzioni proprietarie.


1
Non ne avevo mai sentito parlare prima, quindi ho cercato. Secondo modern-sql.com/feature/filter l'unico DBMS principale che offre effettivamente la FILTERclausola è PostgreSQL, ma è emulato da CASEtutti.
Kristen Hammack,

1

Perché non così?

SELECT count(1)
FROM AD_CurrentView
WHERE myColumn=1

1
Perché ha bisogno di molto più del semplice conteggio. Sta cercando di ottenere il conteggio delle righe di una parte di un gruppo e quindi un aggregato di tutto il gruppo, cosa che non puoi fare con un WHERE.
Kristen Hammack,

1

Ho dovuto usare COUNTIF () nel mio caso come parte delle mie colonne SELECT E per imitare una percentuale del numero di volte in cui ogni elemento è apparso nei miei risultati.

Quindi ho usato questo ...

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

Ovviamente dovrai formattare il risultato in base alle tue esigenze di visualizzazione.


-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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.