Разница между sum и count в инструкции aggregate case

#sql #postgresql #presto

#sql #postgresql #presto

Вопрос:

У меня есть запрос ниже в виде:

     SELECT 
       d.name, 
      SUM(CASE WHEN e.salary > 100000 THEN 1 ELSE 0 END)
        / COUNT(DISTINCT e.id)
        AS pct_above_100k,
      COUNT(DISTINCT e.id) AS c
FROM employees e JOIN departments d ON e.department_id = d.id
GROUP BY 1
HAVING COUNT(*) > 10
ORDER BY 2 DESC
  

Я использую здесь sum, но была бы какая-либо разница, если бы я использовал count?

Я знаю, что count подсчитывает количество раз, когда значение существует, а sum суммирует фактические значения, но здесь, поскольку мое условие таково, что если зарплата > 100000, это все равно будет выглядеть как 1, верно?

Спасибо!

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

1. Правильно. Count получил бы тот же результат.

2. Я не согласен, Count все равно будет считать строку, независимо от того, равно ли ее значение 0 или 1, в то время как sum будет суммироваться, т. е. если значение условия if выполняется три раза, то оно вернет 3, в то время как count вернет количество строк, независимо от того, является условие true или false.

3. sum Это то же самое, что count(*) filter (where e.salary > 10000) ....

Ответ №1:

Мы считаем условно с предложением filter:

 COUNT(*) FILTER (WHERE e.salary > 100000)
  

Некоторые другие СУБД не поддерживают предложение filter. Здесь мы используем обходные пути, самостоятельно вычисляя выражение с помощью CASE WHEN и либо используем COUNT or SUM для суммирования совпадений. Вот несколько способов сделать это:

 SUM(CASE WHEN e.salary > 100000 THEN 1 ELSE 0 END)
COUNT(CASE WHEN e.salary > 100000 THEN 1 ELSE NULL END)
COUNT(CASE WHEN e.salary > 100000 THEN 1 END)
COUNT(CASE WHEN e.salary > 100000 THEN 'count this' END)
  

Поскольку PostgreSQL поддерживает предложение filter, вы должны использовать COUNT(*) FILTER (...) .

Ответ №2:

Если бы вы просто заменили sum на count , вы бы получили другой результат, поскольку 0 считается столько же, сколько 1.

Самый элегантный способ написать это — FILTER предложение, как упоминали другие, но вы также могли бы сделать это:

 count(CASE WHEN e.salary > 100000 THEN 0 END)
  

Это сработало бы, потому что значения NULL игнорируются (большинством) агрегатных функций.

Ответ №3:

Предположительно, сотрудники не находятся в нескольких отделах, поэтому count(distinct) нет необходимости.

Это означает, что код может быть более просто записан как:

   AVG(CASE WHEN e.salary > 100000 THEN 1 ELSE 0 END) AS pct_above_100k,
  

Или:

   AVG( (e.salary > 100000)::int ) AS pct_above_100k,
  

Они будут более эффективными, чем альтернативы, потому что COUNT(DISTINCT) обычно немного дороже, чем другие функции агрегирования.