Как вы ВЫБИРАЕТЕ как с, так и без WHERE в запросе?

#sql #sum #pivot #aggregate-functions

#sql #сумма #сводная #агрегатные функции

Вопрос:

У меня есть одна таблица, my_table, с данными, подобными следующим:

  --------------------------- 
|Accesses | Fruit   | Level |
 --------------------------- 
|      5  | Apple   |     2 |
|      2  | Banana  |     3 |
|      2  | Apple   |     1 |
|      6  | Cherry  |     4 |
|     12  | Apple   |     4 |
|      9  | Banana  |     1 |
|      1  | Durian  |     2 |
 --------------------------- 
  

Я пытаюсь написать запрос, который суммировал бы количество обращений к ГРУППЕ ПО фруктам и общее количество обращений к каждому фрукту, но только для обращений к уровням меньше 4. Результат для этого запроса будет:

  ---------------------------|--------------  
|Fruit   | FilteredAccesses | TotalAccesses |
 --------------------------- --------------- 
|Apple   |                7 |            19 | 
|Banana  |               11 |            11 |
|Durian  |                1 |             1 |
 ------------------------------------------- 
  

Я попробовал следующее самосоединение, но не получил ожидаемых результатов:

 SELECT A.Fruit, SUM(A.Accesses) AS FilteredAccesses, SUM(B.Accesses) As TotalAccesses
FROM my_table A, my_table B
WHERE (A.Fruit = B.Fruit) AND (A.Level < 4)
GROUP BY A.Fruit
  

Есть ли что-то, чего мне не хватает? Можно ли это сделать даже с помощью self join?

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

1. Почему ‘cherry’ исключен из результатов?

2. Потому что меня интересуют записи, где уровень < 4.

Ответ №1:

Используйте условную агрегацию:

 SELECT A.Fruit, SUM(CASE WHEN A.LEVEL < 4 THEN A.Accesses ELSE 0 END) AS FilteredAccesses,
      SUM(A.Accesses) As TotalAccesses
FROM my_table A
GROUP BY A.Fruit
  

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

1. Так что в этом случае строки, которые A.Level < 4 не удовлетворяют, все равно будут включены в результаты? Думаю, я мог ORDER BY A.Fruit DESC бы.

2. Вы можете использовать having фильтр в конце запроса: having SUM(CASE WHEN A.LEVEL < 4 THEN A.Accesses ELSE 0 END) > 0

3. @AndrewSayer Похоже, это приводит к синтаксической ошибке: SELECT A.Fruit, HAVING SUM(CASE WHEN A.LEVEL < 4 THEN A.Accesses ELSE 0 END) > 0 AS FilteredAccesses, SUM(A.Accesses) As TotalAccesses FROM my_table A GROUP BY A.Fruit я что-то пропустил?

4. Вы пропустили «в конце запроса». SELECT A.Fruit, SUM(CASE WHEN A.LEVEL < 4 THEN A.Accesses ELSE 0 END) AS FilteredAccesses, SUM(A.Accesses) As TotalAccesses FROM my_table A GROUP BY A.Fruit having SUM(CASE WHEN A.LEVEL < 4 THEN A.Accesses ELSE 0 END) > 0

Ответ №2:

Нет необходимости в самосоединении. Вы можете сделать это в одном запросе агрегации. Хитрость заключается в том, чтобы использовать case выражение on level для вычисления «отфильтрованных» обращений, чтобы учитывались только строки, удовлетворяющие условиям.

 select fruit,
    sum(case when level < 4 then accesses else 0 end) filtered_accesses,
    sum(accesses) total_accesses
from mytable
group by fruit
  

Этот метод называется условной агрегацией.

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

1. Интересно. Можно ли не включать строки, которые не соответствуют Level < 4 условию? В противном случае они включены из того, что я вижу.

2. @HerbPowell: они включены в total_accesses , но не в filtered_accesses — вот как я понял ваш вопрос.