Статьи

Awesome PostgreSQL 9.4 / SQL: 2003 FILTER для агрегатных функций

Иногда при объединении данных с SQL мы хотели бы добавить несколько дополнительных фильтров. Например, рассмотрим следующие данные мирового банка:

ВВП на душу населения (в текущих ценах)

1
2
3
4
5
6
7
8
9
2009    2010    2011    2012
CA      40,764  47,465  51,791  52,409 
DE      40,270  40,408  44,355  42,598 
FR      40,488  39,448  42,578  39,759 
GB      35,455  36,573  38,927  38,649 
IT      35,724  34,673  36,988  33,814 
JP      39,473  43,118  46,204  46,548 
RU       8,616  10,710  13,324  14,091 
US      46,999  48,358  49,855  51,755

И структура таблицы:

1
2
3
4
5
CREATE TABLE countries (
  code CHAR(2) NOT NULL,
  year INT NOT NULL,
  gdp_per_capita DECIMAL(10, 2) NOT NULL
);

Теперь предположим, что мы хотели бы найти число стран с ВВП выше 40 000 за каждый год.

Со стандартным SQL: 2003, а теперь и с недавно выпущенным PostgreSQL 9.4 мы можем теперь воспользоваться новым предложением FILTER , которое позволяет нам написать следующий запрос:

1
2
3
4
5
6
7
SELECT
  year,
  count(*) FILTER (WHERE gdp_per_capita >= 40000)
FROM
  countries
GROUP BY
  year

Приведенный выше запрос теперь даст:

1
2
3
4
5
6
year   count
------------
2012   4
2011   5
2010   4
2009   4

И это еще не все! Как всегда, вы можете использовать любую агрегатную функцию также как оконную функцию, просто добавив в конец предложение OVER() :

1
2
3
4
5
6
7
8
9
SELECT
  year,
  code,
  gdp_per_capita,
  count(*)
    FILTER (WHERE gdp_per_capita >= 40000)
    OVER   (PARTITION BY year)
FROM
  countries

Результат будет выглядеть примерно так:

1
2
3
4
5
6
year   code   gdp_per_capita   count
------------------------------------
2009   CA           40764.00       4
2009   DE           40270.00       4
2009   FR           40488.00       4
2009   GB           35455.00       4

jOOQ 3.6 также будет поддерживать новое предложение FILTER для агрегатных функций

Хорошие новости для пользователей jOOQ. Вы можете написать тот же запрос с jOOQ интуитивно как таковой:

1
2
3
4
5
6
7
8
9
DSL.using(configuration)
   .select(
        COUNTRIES.YEAR,
        count().filterWhere(
            COUNTRIES.GDP_PER_CAPITA.ge(40000)
        ))
   .from(COUNTRIES)
   .groupBy(COUNTRIES.YEAR)
   .fetch();

… и

01
02
03
04
05
06
07
08
09
10
DSL.using(configuration)
   .select(
        COUNTRIES.YEAR,
        COUNTRIES.CODE,
        COUNTRIES.GDP_PER_CAPITA
        count().filterWhere(
                   COUNTRIES.GDP_PER_CAPITA.ge(40000))
               .over(partitionBy(COUNTRIES.YEAR)))
   .from(COUNTRIES)
   .fetch();

И лучше всего то, что jOOQ (как обычно) эмулирует вышеприведенное предложение для вас, если вы не используете PostgreSQL. Эквивалентный запрос будет:

1
2
3
4
5
6
7
SELECT
  year,
  count(CASE WHEN gdp_per_capita >= 40000 THEN 1 END)
FROM
  countries
GROUP BY
  year

jooq-The-лучший способ к записи-SQL-в-Java-маленький

Подробнее о том, что нового в PostgreSQL 9.4, читайте здесь