Подтвердить что ты не робот

В Redshift/Postgres, как подсчитывать строки, удовлетворяющие условию?

Я пытаюсь написать запрос, который подсчитывает только строки, соответствующие условию.

Например, в MySQL я бы написал это следующим образом:

SELECT
    COUNT(IF(grade < 70), 1, NULL)
FROM
    grades
ORDER BY
    id DESC;

Однако, когда я пытаюсь сделать это в Redshift, он возвращает следующую ошибку:

ОШИБКА: функция if (boolean, integer, "unknown" ) не существует

Подсказка: никакая функция не соответствует указанному имени и типам аргументов. Возможно, вам придется добавлять явные типы.

Я проверил документацию для условных операторов, и нашел

NULLIF(value1, value2)

но он только сравнивает value1 и value2, и если такие значения равны, он возвращает null.

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

Я попытался использовать выражение CASE, но я не получаю результаты, которые хочу:

SELECT 
    CASE
        WHEN grade < 70 THEN COUNT(rank)
        ELSE COUNT(rank)
    END
FROM
   grades

Вот так я хочу считать вещи:

  • не удалось (класс < 70)

  • средний (70 <= класс < 80)

  • good (80 <= grade < 90)

  • отлично (90 <= grade <= 100)

и я ожидаю увидеть результаты:

+========+=========+======+===========+
| failed | average | good | excellent |
+========+=========+======+===========+
|   4    |    2    |  1   |     4     |
+========+=========+======+===========+

но я получаю следующее:

+========+=========+======+===========+
| failed | average | good | excellent |
+========+=========+======+===========+
|  11    |   11    |  11  |    11     |
+========+=========+======+===========+

Я надеюсь, что кто-то может указать мне в правильном направлении!

Если это поможет здесь несколько примеров информации

CREATE TABLE grades(
  grade integer DEFAULT 0,
);

INSERT INTO grades(grade) VALUES(69, 50, 55, 60, 75, 70, 87, 100, 100, 98, 94);
4b9b3361

Ответ 1

Во-первых, проблема, с которой вы сталкиваетесь, заключается в том, что вы говорите: "Если класс меньше 70, значение этого выражения выражает счет (ранг). В противном случае значение этого выражения подсчитывается (ранг)." Таким образом, в любом случае вы всегда получаете одинаковое значение.

SELECT 
    CASE
        WHEN grade < 70 THEN COUNT(rank)
        ELSE COUNT(rank)
    END
FROM
   grades

count() учитывает только ненулевые значения, так что обычно шаблон, который вы увидите для выполнения того, что вы пытаетесь, таков:

SELECT 
    count(CASE WHEN grade < 70 THEN 1 END) as grade_less_than_70,
    count(CASE WHEN grade >= 70 and grade < 80 THEN 1 END) as grade_between_70_and_80
FROM
   grades

Таким образом, выражение case будет оцениваться только до 1, когда тестовое выражение истинно и в противном случае будет null. Затем count() будет считать только ненулевые экземпляры, т.е. Когда тестовое выражение истинно, что должно дать вам то, что вам нужно.

Изменить: в качестве примечания обратите внимание, что это точно так же, как и то, как вы изначально писали это с помощью count(if(test, true-value, false-value)), только переписанным как count(case when test then true-value end) (и null - это позиция в ложном значении, так как else не был доставлен в корпус).

Изменить: postgres 9.4 был выпущен через несколько месяцев после этого первоначального обмена. В этой версии появились агрегированные фильтры, которые могут сделать сценарии, подобные этому, немного лучше и понятнее. Этот ответ по-прежнему получает некоторые случайные варианты, поэтому, если вы наткнулись на них и используете более новые postgres (т.е. 9.4+), вы можете рассмотреть эту эквивалентную версию:

SELECT
    count(*) filter (where grade < 70) as grade_less_than_70,
    count(*) filter (where grade >= 70 and grade < 80) as grade_between_70_and_80
FROM
   grades

Ответ 2

Другой метод:

SELECT 
    sum(CASE WHEN grade < 70 THEN 1 else 0 END) as grade_less_than_70,
    sum(CASE WHEN grade >= 70 and grade < 80 THEN 1 else 0 END) as grade_between_70_and_80
FROM
   grades

Работает отлично, если вы хотите группировать подсчеты с помощью категориального столбца.

Ответ 3

Решение, данное @yieldsfalsehood, работает отлично:

SELECT
    count(*) filter (where grade < 70) as grade_less_than_70,
    count(*) filter (where grade >= 70 and grade < 80) as grade_between_70_and_80
FROM
    grades

Но так как вы говорили о NULLIF(value1, value2), есть способ с nullif, который может дать тот же результат:

select count(nullif(grade < 70 ,true)) as failed from grades;

Ответ 4

Только красное смещение

Для ленивых печатных машин здесь COUNTIF целочисленная версия приведения к сумме " COUNTIF ", построенная поверх ответа @user1509107:

SELECT 
    SUM((grade < 70)::INT) AS grade_less_than_70,
    SUM((grade >= 70 AND grade < 80)::INT) AS grade_between_70_and_80
FROM
   grades