Операция GROUP BY + CASE
У меня есть рабочий запрос, который группирует данные по модели оборудования и результат, но проблема в том, что существует множество "результатов". Я попытался уменьшить это до "if result = 0, затем сохранить как 0, а затем установить его в 1". Обычно это работает, но в итоге я получаю:
day | name | type | case | count
------------+----------------+------+------+-------
2013-11-06 | modelA | 1 | 0 | 972
2013-11-06 | modelA | 1 | 1 | 42
2013-11-06 | modelA | 1 | 1 | 2
2013-11-06 | modelA | 1 | 1 | 11
2013-11-06 | modelB | 1 | 0 | 456
2013-11-06 | modelB | 1 | 1 | 16
2013-11-06 | modelB | 1 | 1 | 8
2013-11-06 | modelB | 3 | 0 | 21518
2013-11-06 | modelB | 3 | 1 | 5
2013-11-06 | modelB | 3 | 1 | 7
2013-11-06 | modelB | 3 | 1 | 563
Вместо совокупности, которую я пытаюсь достичь, только 1 строка для каждого типа/случая.
day | name | type | case | count
------------+----------------+------+------+-------
2013-11-06 | modelA | 1 | 0 | 972
2013-11-06 | modelA | 1 | 1 | 55
2013-11-06 | modelB | 1 | 0 | 456
2013-11-06 | modelB | 1 | 1 | 24
2013-11-06 | modelB | 3 | 0 | 21518
2013-11-06 | modelB | 3 | 1 | 575
Вот мой запрос:
select CURRENT_DATE-1 AS day, model.name, attempt.type,
CASE WHEN attempt.result = 0 THEN 0 ELSE 1 END,
count(*)
from attempt attempt, prod_hw_id prod_hw_id, model model
where time >= '2013-11-06 00:00:00'
AND time < '2013-11-07 00:00:00'
AND attempt.hard_id = prod_hw_id.hard_id
AND prod_hw_id.model_id = model.model_id
group by model.name, attempt.type, attempt.result
order by model.name, attempt.type, attempt.result;
Любые советы о том, как я могу достичь этого, будут потрясающими.
День всегда будет определяться в предложении WHERE
, поэтому он не будет меняться. name, type, result(case)
и count
будут отличаться. Короче говоря, для любой данной модели я хочу только 1 строку на комбинацию "тип + случай". Как вы можете видеть в первом наборе результатов, у меня есть 3 строки для modelA
, которые имеют type=1
и case=1
(потому что есть много значений "результата", которые я превратил в 0 = 0 и все остальное = 1). Я хочу, чтобы это было представлено как 1 строка с подсчетом, объединенным, как в примере набора данных 2.
Ответы
Ответ 1
Ваш запрос будет работать уже, за исключением того, что вы используете конфликты имен или просто путаете столбцы вывода (выражение CASE
) с столбцом источника result
, который имеет различный контент.
...
GROUP BY model.name, attempt.type, attempt.result
...
Вам нужно GROUP BY
ваше выражение CASE
вместо столбца источника:
...
GROUP BY model.name, attempt.type
, CASE WHEN attempt.result = 0 THEN 0 ELSE 1 END
...
Или укажите псевдоним столбца, отличный от любого имени столбца в списке FROM
, или же этот столбец имеет приоритет:
SELECT ...
, CASE WHEN attempt.result = 0 THEN 0 ELSE 1 END AS result1
...
GROUP BY model.name, attempt.type, result1
...
Стандарт SQL довольно специфичен в этом отношении. Указание руководства здесь:
Имя выходного столбца может использоваться для ссылки на значение столбца в ORDER BY
и GROUP BY
, но не в предложениях WHERE
или HAVING
; там вы должны выписать выражение вместо этого.
и
Если выражение ORDER BY
- простое имя, которое соответствует как выходному имя столбца и имя столбца ввода, ORDER BY
будет интерпретировать его как имя выходного столбца. Это противоположность выбору GROUP BY
будет делать в той же ситуации. Это несогласованность совместимый со стандартом SQL.
Жирный акцент мой.
Эти конфликты можно избежать, используя позиционные ссылки (порядковые номера) в GROUP BY
и ORDER BY
, ссылаясь на элементы в списке SELECT
слева направо. См. Решение ниже.
Недостатком является то, что это может быть труднее читать и подвергать изменениям в списке SELECT
(можно забыть соответствующим образом адаптировать позиционные ссылки).
Но вам не нужно добавлять столбец day
в предложение GROUP BY
, если он содержит постоянное значение (CURRENT_DATE-1
).
Переписанный и упрощенный с помощью правильного синтаксиса JOIN и позиционных ссылок он может выглядеть так:
SELECT m.name
, a.type
, CASE WHEN a.result = 0 THEN 0 ELSE 1 END AS result
, CURRENT_DATE - 1 AS day
, count(*) AS ct
FROM attempt a
JOIN prod_hw_id p USING (hard_id)
JOIN model m USING (model_id)
WHERE ts >= '2013-11-06 00:00:00'
AND ts < '2013-11-07 00:00:00'
GROUP BY 1,2,3
ORDER BY 1,2,3;
Также обратите внимание, что я избегаю имени столбца time
. Это зарезервированное слово и никогда не должно использоваться как идентификатор. Кроме того, ваше "время", очевидно, является timestamp
или date
, поэтому оно довольно вводит в заблуждение.
Ответ 2
можете ли вы попробовать следующее:
замените оператор case ниже на
Sum(CASE WHEN attempt.result = 0 THEN 0 ELSE 1 END) as Count,
Ответ 3
Попробуйте добавить два других столбца не COUNT в GROUP BY:
select CURRENT_DATE-1 AS day,
model.name,
attempt.type,
CASE WHEN attempt.result = 0 THEN 0 ELSE 1 END,
count(*)
from attempt attempt, prod_hw_id prod_hw_id, model model
where time >= '2013-11-06 00:00:00'
AND time < '2013-11-07 00:00:00'
AND attempt.hard_id = prod_hw_id.hard_id
AND prod_hw_id.model_id = model.model_id
group by 1,2,3,4
order by model.name, attempt.type, attempt.result;
Ответ 4
Для TSQL мне нравится инкапсулировать операторы case во внешнем приложении. Это мешает мне потребовать, чтобы оператор case был написан дважды, позволяет ссылаться на аргумент case псевдонимом в будущих объединениях и избегает необходимости в позиционных ссылках.
select oa.day,
model.name,
attempt.type,
oa.result
COUNT(*) MyCount
FROM attempt attempt, prod_hw_id prod_hw_id, model model
WHERE time >= '2013-11-06 00:00:00'
AND time < '2013-11-07 00:00:00'
AND attempt.hard_id = prod_hw_id.hard_id
AND prod_hw_id.model_id = model.model_id
OUTER APPLY (
SELECT CURRENT_DATE-1 AS day,
CASE WHEN attempt.result = 0 THEN 0 ELSE 1 END result
) oa
group by oa.day,
model.name,
attempt.type,
oa.result
order by model.name, attempt.type, oa.result;