Sql Server эквивалент агрегатной функции COUNTIF

164

Я строю запрос с GROUP BYпредложением, которое нуждается в способности считать записи, основанные только на определенном условии (например, считать только записи, где определенное значение столбца равно 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()Линия явно не удается , так как нет родной функции SQL называется COUNTIF, но идея здесь , чтобы определить процент всех строк , которые имеют значение «1» для MyColumn.

Любые мысли о том, как правильно реализовать это в среде MS SQL 2005?

senfo
источник

Ответы:

339

Вы можете использовать SUM(не COUNT!) В сочетании с CASEоператором, например так:

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

Примечание: в моем собственном тесте NULLпроблем не было, хотя это может зависеть от среды. Вы можете обрабатывать нули, такие как:

SELECT SUM(CASE WHEN ISNULL(myColumn,0)=1 THEN 1 ELSE 0 END)
FROM AD_CurrentView
JoshBerke
источник
3
(Я знаю, что ОП спрашивал о MS SQL, но просто небольшой комментарий для пользователей SQLite, делающих то же самое). У SQLite нет ISNULL, вместо этого вы можете сделать CASE WHEN myColumn IS NULLили использовать ifnull( stackoverflow.com/a/799406/1861346 )
Matt
54

Я обычно делаю то, что рекомендовал Джош, но мозговой штурм и тестирование слегка ненормальной альтернативы, которой я хотел поделиться.

Вы можете воспользоваться тем, что COUNT (ColumnName) не считает NULL, и использовать что-то вроде этого:

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

NULLIF - возвращает NULL, если два переданных значения совпадают.

Преимущество: выражает ваше намерение считать строки вместо записи SUM (). Недостаток: не так ясно, как это работает («магия», как правило, плохо).

Крис Шаффер
источник
2
Это решение может дать ответы, отличные от суммы, когда группа содержит только нули, в результате чего
получается
Старый пост, но спасибо, это помогло. Я продлил магии и получил вокруг «только NULLS» проблему путем добавления ISNULLследующим образом : SELECT COUNT(NULLIF(0, ISNULL(myColumn, 0))). Подождите, это выглядит просто уродливо ...
pcdev
1
Было бы идеально, если бы была функция
NULLIFNOT
21

Я бы использовал этот синтаксис. Он выполняет те же функции, что и предложения Джоша и Криса, но с преимуществом совместим с ANSI и не привязан к конкретному поставщику баз данных.

select count(case when myColumn = 1 then 1 else null end)
from   AD_CurrentView
asgeo1
источник
2
Ответ Криса соответствует стандарту SQL (подсказка: NULLIFвключен стандарт SQL-92). Ответ Джоша может быть легко преобразован в стандартный SQL путем замены isnullна COALESCE.
понедельник,
На самом деле мне больше нравится этот ответ, потому что он получает идею «подсчета строк», которую показывал Крис, но он более расширяемый, поскольку вы можете использовать любой оператор сравнения; не просто =. Я использую его для «подсчета количества ответов> = 2».
Кристен
3

Добавляя к ответу Джоша,

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

Для меня это работало хорошо (в SQL Server 2012) без изменения «count» на «sum», и та же логика переносима на другие «условные агрегаты». Например, суммирование на основе условия:

SELECT SUM(CASE WHEN myColumn=1 THEN AD_CurrentView.NumberColumn ELSE 0 END)
FROM AD_CurrentView
Sturgus
источник
2

Как насчет

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

Короче CASE:)

Работает, потому COUNT()что не считает нулевые значения, и IF/ CASEвозвращает ноль, если условие не выполнено и нет ELSE.

Я думаю, что это лучше, чем использовать SUM().

МАФ-мягкий
источник
1

Не для конкретного продукта, но стандарт SQL обеспечивает

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

для этого. Или что-то, что очень похоже на это, я не знаю, с верху моей шляпы.

И, конечно, поставщики предпочтут придерживаться своих фирменных решений.

Эрвин Смут
источник
1
Я никогда не слышал об этом раньше, поэтому я посмотрел. По мнению modern-sql.com/feature/filter, единственной крупной СУБД, которая фактически предлагает это FILTERпредложение, является PostgreSQL, но имитируется CASEво всех них.
Кристен
1

Почему не так?

SELECT count(1)
FROM AD_CurrentView
WHERE myColumn=1
Michal
источник
1
Потому что ему нужно гораздо больше, чем просто граф. Он пытается получить количество строк для части группы, а затем - совокупность всей группы, чего нельзя сделать с помощью WHERE.
Кристен
1

Мне пришлось использовать COUNTIF () в моем случае как часть моих столбцов SELECT И чтобы имитировать% от числа раз, когда каждый элемент появлялся в моих результатах.

Так что я использовал это ...

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

Конечно, вам нужно будет отформатировать результат в соответствии с вашими требованиями к отображению.

Fandango68
источник
-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
Андрес Саррия
источник