SQL Server: нулевые значения при подсчете (*)

#sql #sql-server

#sql #sql-сервер

Вопрос:

У меня есть таблица @MemberAttribute

 MemberID AttributeID AttributeValue
1            1         False 
1            2         True 
2            1         False
2            2         True 
3            1         False
3            2         False
  

Я хочу сгруппировать по AttributeID и получить количество атрибутов, значение которых равно True. Но когда attributetype имеет значение false для определенного атрибута, я хочу, чтобы он отображал 0. Прямо сейчас AttributeID со всеми значениями false просто не отображается.
Вот sql-запрос

 SELECT MA.AttributeID, GA.Name,
--COUNT(isNull(MA.AttributeID,0)) as AttributCount,
CASE WHEN COUNT(MA.AttributeID) > 0 THEN COUNT(MA.AttributeID) Else 0 END AS 'AttributCount'
--CASE WHEN COUNT(MA.AttributeID) < 0 THEN 0 Else COUNT(MA.AttributeID) END AS 'TOTAL Attributes'
from GroupAttribute GA
inner join   @MemberAttribute MA on GA.GroupAttributeID = MA.AttributeID
WHERE MA.AttributeValue = 'True'
GROUP BY MA.AttributeID,GA.Name
  

ДЛЯ AttributeID = 1 все значения = False … таким образом, результат выглядит следующим образом

 AttributeID      Name      AttributeCount <br/>
2                Attr2        2 <br/>
  

Я хочу

 1                 Attr1      0  <br/>
  

тоже в результирующем наборе.

Ответ №1:

Попробуйте это — обратите внимание, что 1 в ...THEN 1 ELSE ... является произвольным ненулевым значением — это может быть ‘fred’ или 12345 — важно то, что оно не равно NULL.

 SELECT MA.AttributeID, GA.Name,
COUNT(CASE WHEN MA.AttributeValue = 'True' THEN 1 ELSE NULL END) AS 'AttributeCount'
from GroupAttribute GA
inner join   @MemberAttribute MA on GA.GroupAttributeID = MA.AttributeID
GROUP BY MA.AttributeID,GA.Name
  

…несколько более интуитивно (спасибо Кену) — и обратите внимание, что здесь важны 1 и 0…

 SELECT MA.AttributeID, GA.Name,
SUM(CASE WHEN MA.AttributeValue = 'True' THEN 1 ELSE 0 END) AS 'AttributeCount'
from GroupAttribute GA
inner join   @MemberAttribute MA on GA.GroupAttributeID = MA.AttributeID
GROUP BY MA.AttributeID,GA.Name
  

Комментарии:

1. Я думаю, вам нужно изменить COUNT на SUM и ELSE NULL на ELSE 0 , чтобы это сработало.

2. @Ken — в этом не должно быть необходимости — агрегирующие функции игнорируют нули, COUNT (something) возвращает количество ненулевых значений. Попробовал это, и это работает нормально. Тем не менее, возможно, СУММА с 1 и 0 была бы более интуитивно понятной.

3. @Ken — ответ обновлен, чтобы включить предложенное вами изменение в качестве достойной альтернативы.

4. @Will: Хорошая мысль. Я нахожу SUM более интуитивно понятным. Я только что сделал комментарий и не голосовал против; поскольку вы абсолютно правы в своем первом комментарии, это хорошо. 🙂

5. @Will: 1 за ваш ответ (особенно за представление обоих вариантов). Приятно. 🙂

Ответ №2:

После некоторых ухищрений я придумал эту красоту, не содержащую CASE выражения:

 SELECT GA.GroupAttributeID AS AttributeID, GA.Name, 
       COUNT(MA.AttributeID) AS AttributeCount
FROM GroupAttribute AS GA 
LEFT OUTER JOIN @MemberAttribute AS MA 
  ON GA.GroupAttributeID = MA.AttributeID AND MA.AttributeValue = 'True'
GROUP BY GA.GroupAttributeID, GA.Name
  

Это использует тот факт, что если для определенного значения нет «истинных» значений, AttributeID то MA.AttributeID результатом LEFT OUTER JOIN будет NULL . NULL Значение, переданное в COUNT() , приведет к AttributeCount нулевому значению. LEFT OUTER JOIN Также гарантирует, что в результирующем наборе будет присутствовать строка для AttributeID строк с нулевым количеством.

Предполагается, что в этом запросе все атрибуты группы представлены в @MemberAttribute табличной переменной. В противном случае будут строки с нулевым количеством, представляющие те атрибуты группы, которые отсутствуют. Если это нежелательно, можно добавить WHERE предложение для их фильтрации, усложняющее этот запрос. Решения Уилла были бы гораздо более практичными, если бы это было так.

План выполнения хорошо сопоставим с первым решением Уилла, содержащим на один шаг меньше (вычислительный скалярный). Однако он использует LEFT OUTER JOIN vs INNER JOIN , что делает два метода практически идентичными для этого простого примера. Было бы интересно посмотреть, как масштабируются два решения, если вместо этого табличная переменная преобразуется в довольно большую таблицу.

Фактический план Уилла для его решения, включающий COUNT() :

введите описание изображения здесь

Мой фактический план:

введите описание изображения здесь

Комментарии:

1. Не голосую против, но это кажется чрезвычайно сложным для заданного вопроса. Вы серьезно предлагаете это в качестве жизнеспособной альтернативы (наряду с дополнительными накладными расходами на добавленный индекс) для вопроса, который был задан здесь?

2. Я согласен, это слишком сложно для заданного вопроса. Однако я просто показываю альтернативу, если решение экстраполируется на реальную проблему, связанную с чем-то большим, чем табличная переменная с несколькими значениями… Кроме того, это альтернатива без регистра…

3. А, понял. Итак, если я спрошу, как добавить 1 1, вместо объяснения, как это сделать, вы захотите объяснить схему калькулятора обратной польской нотации на случай, если он мне понадобится в будущем?

4. @Кен, лол! Вы заставили меня усердно работать над упрощением. Теперь ознакомьтесь с моим улучшенным решением, в котором исключен регистр и даже нет предложения WHERE. Мне интересно, что вы об этом думаете 🙂 .

5. @Michael ..извините, забыл отметить это как ответ… Если возможно, можете ли вы объяснить, почему перемещение условия MA.AttributeValue = ‘True’ из предложения where в LEFT join имеет такое большое значение… Если я верну это условие в предложение Where, это не даст мне желаемого sol..it это тот же результат, который я получал ранее… но простое перемещение этого оператора в левое соединение имеет большое значение… вы можете это объяснить?