Sql Server odpowiednik funkcji agregującej COUNTIF


164

Buduję zapytanie z GROUP BYklauzulą, która wymaga możliwości liczenia rekordów tylko na podstawie określonego warunku (np. Licz tylko rekordy, w których określona wartość kolumny jest równa 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

COUNTIF()Linia oczywiście nie powiedzie się, ponieważ nie ma natywna funkcja SQL nazywa COUNTIF, ale idea jest tu określenie procentu wszystkich wierszy, które mają wartość „1” dla MyColumn.

Czy są jakieś przemyślenia, jak poprawnie zaimplementować to w środowisku MS SQL 2005?

Odpowiedzi:


339

Możesz użyć SUM(nie COUNT!) W połączeniu z CASEinstrukcją, na przykład:

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

Uwaga: w moich własnych testach NULLnie było problemu, chociaż może to zależeć od środowiska. Możesz obsłużyć wartości zerowe, takie jak:

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

3
(Wiem, że OP pytał o MS SQL, ale tylko mały komentarz dla użytkowników SQLite robiących to samo) SQLite nie ma ISNULL, zamiast tego możesz zrobić CASE WHEN myColumn IS NULLlub użyć ifnull( stackoverflow.com/a/799406/1861346 )
Matt

54

Zwykle robię to, co zalecił Josh, ale przeprowadziłem burzę mózgów i przetestowałem nieco hokerską alternatywę, którą chciałem się podzielić.

Możesz wykorzystać fakt, że COUNT (ColumnName) nie liczy wartości NULL i użyć czegoś takiego:

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

NULLIF - zwraca NULL, jeśli dwie przekazane wartości są takie same.

Zaleta: Wyraża zamiar COUNT wierszy zamiast notacji SUMA (). Wada: nie jest tak jasne, jak to działa („magia” jest zwykle zła).


2
To rozwiązanie może
dać

Stary post, ale dzięki temu pomogło. I przedłużony magię i dostał tylko wokół „null” problem dodając ISNULLw następujący sposób: SELECT COUNT(NULLIF(0, ISNULL(myColumn, 0))). Czekaj, to po prostu wygląda brzydko ...
pcdev

1
Byłoby idealnie, gdyby istniała funkcja
NULLIFNOT

21

Użyłbym tej składni. Osiąga to samo, co sugestie Josha i Chrisa, ale ma tę zaletę, że jest zgodny z ANSI i nie jest powiązany z konkretnym dostawcą bazy danych.

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

2
Odpowiedź Chrisa jest zgodna ze standardem Stndard SQL (wskazówka: NULLIFdołączony jest standardowy SQL-92). Odpowiedź Josha można łatwo przekształcić w standardowy SQL, zastępując isnullCOALESCE.
onedaywhen

Właściwie najbardziej podoba mi się ta odpowiedź, ponieważ pochodzi ona z pomysłu „liczenia wierszy”, które pokazywał Chris, ale jest bardziej rozszerzalna, ponieważ można użyć dowolnego operatora porównania; nie tylko =. Używam go do „policz liczbę odpowiedzi> = 2”.
Kristen Hammack

3

Dodając do odpowiedzi Josha,

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

U mnie zadziałało (w SQL Server 2012) bez zmiany „licznika” na „sumę”, a ta sama logika jest przenoszona na inne „agregacje warunkowe”. Np. Sumowanie na podstawie warunku:

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

2

Co powiesz na

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

Krótszy niż CASE:)

Działa, ponieważ COUNT()nie liczy wartości null i IF/ CASEzwraca wartość null, gdy warunek nie jest spełniony i nie ma ELSE.

Myślę, że to lepsze niż używanie SUM().


1

Nie jest to specyficzne dla produktu, ale standard SQL zapewnia

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

w tym celu. Lub coś, co bardzo to przypomina, nie wiem z góry mojego kapelusza.

I oczywiście dostawcy będą woleli trzymać się swoich zastrzeżonych rozwiązań.


1
Nigdy wcześniej o tym nie słyszałem, więc poszukałem tego. Według modern-sql.com/feature/filter jedynym głównym DBMS, który faktycznie oferuje tę FILTERklauzulę, jest PostgreSQL, ale CASEwe wszystkich jest on emulowany .
Kristen Hammack

1

Dlaczego nie tak?

SELECT count(1)
FROM AD_CurrentView
WHERE myColumn=1

1
Ponieważ potrzebuje czegoś więcej niż tylko hrabiego. Próbuje uzyskać liczbę wierszy z części grupy, a następnie zagregować całą grupę, czego nie można zrobić z GDZIE.
Kristen Hammack

1

Musiałem użyć LICZ.JEŻELI () w moim przypadku jako część moich kolumn WYBIERZ ORAZ, aby naśladować% liczby przypadków, w których każdy element pojawił się w moich wynikach.

Więc użyłem tego ...

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

Oczywiście będziesz musiał sformatować wynik zgodnie z wymaganiami dotyczącymi wyświetlania.


-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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.