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?

Ответы:


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

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

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

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

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

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


-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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.