2009-02-24 23 views
123

Sto costruendo una query con una clausola GROUP BY che richiede la possibilità di contare i record basati solo su una determinata condizione (ad esempio conteggio solo record in cui un determinato valore di colonna è uguale a 1).Equivalente Sql Server di una funzione aggregata 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 linea COUNTIF() ovviamente fallisce poiché non v'è alcuna funzione SQL nativo chiamato COUNTIF, ma l'idea è quella 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?

risposta

241

si potrebbe usare un SUM (non COUNT!) In combinazione con una dichiarazione CASE, in questo modo:

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

Nota: nel mio test di NULL s non erano un problema, anche se questo può essere l'ambiente dipendente. Si potrebbe gestire i null come ad esempio:

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

(So che l'OP ha richiesto MS SQL, ma solo un piccolo commento per gli utenti SQLite che fanno la stessa cosa) SQLite non ha 'ISNULL', invece si può fare' CASE WHEN myColumn IS NULL', o usare 'ifnull' (https://stackoverflow.com/a/799406/1861346) – Matt

43

faccio di solito che cosa ha suggerito Josh, ma brainstorming e testato un po 'hokey alternativa che mi sentivo come la condivisione.

Si può approfittare del fatto che COUNT (ColumnName) non conta NULL, e usare qualcosa di simile:

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

NULLIF - restituisce NULL se i due passati in valori sono gli stessi.

Vantaggio: Esprime il tuo intento di COUNT righe anziché la notazione SUM(). Svantaggio: non è chiaro come funzioni ("magia" di solito è cattiva).

+2

Questa soluzione può dare una risposta diversa dalla somma quando un gruppo contiene solo valori nulli risultanti in 1 invece di 0. – KimvdLinde

+0

Vecchio post, ma grazie a ciò è stato utile. Ho esteso la magia e aggirato il problema dei "soli null" aggiungendo 'ISNULL' come segue:' SELECT COUNT (NULLIF (0, ISNULL (myColumn, 0))) '. Aspetta, sembra solo brutto ... – pcdev

16

Vorrei usare questa sintassi. Avanza come i suggerimenti di Josh e Chris, ma con il vantaggio che è ANSI complient 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 Standard SQL-92). La risposta di Josh può essere facilmente trasformata in SQL standard sostituendo 'isnull' con' COALESCE'. – onedaywhen

+0

Mi piace molto questa risposta, perché ha l'idea di "contare le righe" che Chris stava mostrando, ma è più estendibile, dal momento che puoi usare qualsiasi operatore di confronto; non solo '='. Lo sto usando per "contare il numero di risposte> = 2". –

0

Non specifica per il prodotto, ma lo standard SQL fornisce

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

per questo scopo. O qualcosa che ti assomiglia da vicino, non so in cima al mio cappello.

E ovviamente i fornitori preferiranno attenersi alle loro soluzioni proprietarie.

+1

Non ne avevo mai sentito parlare prima, quindi l'ho cercato. Secondo http://modern-sql.com/feature/filter l'unico DBMS principale che attualmente offre la clausola 'FILTER' è PostgreSQL, ma è emulato da' CASE' in tutti loro. –

0

Perché non piace questo?

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

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

1

Aggiungendo alla risposta di Josh,

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

funzionato bene per me (in SQL Server 2012) senza cambiare il 'conteggio' ad una 'somma' e la stessa logica è portabile su altri ' aggregati condizionali '. Per esempio., Riassumendo base a una condizione:

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

Come su

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

Shorter di CASE :)

funziona perché COUNT() non conta valori nulli, e IF/CASE ritorno nullo quando la condizione non è soddisfatta e non c'è ELSE.

Penso che sia meglio che utilizzare SUM().

0

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

Così 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 

Naturalmente è necessario formattare il risultato in base alle proprie esigenze di visualizzazione.

Problemi correlati