#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 это тот же результат, который я получал ранее… но простое перемещение этого оператора в левое соединение имеет большое значение… вы можете это объяснить?