Выберите все значения с последней даты, которые разделяются между строками, сгруппированными по значению - PullRequest
2 голосов
/ 29 мая 2020

У меня есть таблица Postgresql со списком значений для стран с течением времени и их континентов. Значения могут быть NULL. Я хотел бы получить сумму для каждого континента с течением времени, вплоть до последней даты, за которую для каждого континента есть данные.

Это моя таблица ( просмотр в DB Fiddle ):

| continent | country | date       | value | id  |
| --------- | ------- | ---------- | ----- | --- |
| Europe    | Germany | 2020-05-25 | 10    | 1   |
| Europe    | Germany | 2020-05-26 | 11    | 2   |
| Europe    | Germany | 2020-05-27 | 12    | 3   |
| Europe    | Germany | 2020-05-28 | 13    | 4   |
| Europe    | Italy   | 2020-05-25 | 20    | 5   |
| Europe    | Italy   | 2020-05-26 | 21    | 6   |
| Europe    | Italy   | 2020-05-27 | 22    | 7   |
| Europe    | Italy   | 2020-05-28 | 23    | 8   |
| Europe    | France  | 2020-05-25 | 30    | 9   |
| Europe    | France  | 2020-05-26 | 31    | 10  |
| Europe    | France  | 2020-05-27 | 32    | 11  |
| Europe    | France  | 2020-05-28 | NULL  | 12  |
| Africa    | Congo   | 2020-05-25 | 40    | 13  |
| Africa    | Congo   | 2020-05-26 | 41    | 14  |
| Africa    | Congo   | 2020-05-27 | NULL  | 15  |

И это то, что я хотел бы получить обратно. Обратите внимание, что Европа включает данные до 27-го числа, потому что Франция не имеет данных за 28-е число, а Африка - до 26-го числа, потому что это последняя дата, на которую у ее стран есть данные.

| continent | date       | value |
| --------- | ---------- | ----- |
| Europe    | 2020-05-27 | 66    |
| Africa    | 2020-05-26 | 41    |
| Europe    | 2020-05-26 | 63    |
| Africa    | 2020-05-25 | 40    |
| Europe    | 2020-05-25 | 60    |

Мне удалось почти добраться туда, включив количество стран на континенте, по которым есть данные на каждую дату.

SELECT
    countries.continent,
    countries.date,
    SUM(countries.value) AS value,
    COUNT(countries.country) AS countries_count
FROM
    countries
WHERE
    countries.value IS NOT NULL
GROUP BY
    countries.continent,
    countries.date
ORDER BY
    countries.date DESC,
    countries.continent;
| continent | date       | value | countries_count |
| --------- | ---------- | ----- | --------------- |
| Europe    | 2020-05-28 | 36    | 2               |
| Europe    | 2020-05-27 | 66    | 3               |
| Africa    | 2020-05-26 | 41    | 1               |
| Europe    | 2020-05-26 | 63    | 3               |
| Africa    | 2020-05-25 | 40    | 1               |
| Europe    | 2020-05-25 | 60    | 3               |

Мне также удалось получить количество стран на континенте.

SELECT
    countries.continent,
    COUNT(DISTINCT countries.country) as number_of_countries
FROM
    countries
GROUP BY
    countries.continent;
| continent | number_of_countries |
| --------- | ------------------- |
| Africa    | 1                   |
| Europe    | 3                   |

Я застрял в том, как объединить два запроса, чтобы отфильтровать строки, в которых нет полного количества стран для континента (например, выберите строки, где countries_count равно 3 для Europe и 1 для Africa.

Это конечный результат, который я бы хотел вернуть:

| continent | date       | value |
| --------- | ---------- | ----- |
| Europe    | 2020-05-27 | 66    |
| Africa    | 2020-05-26 | 41    |
| Europe    | 2020-05-26 | 63    |
| Africa    | 2020-05-25 | 40    |
| Europe    | 2020-05-25 | 60    |

Или, может быть, есть совершенно другой способ go по этому поводу?

Просмотр в DB Fiddle

Ответы [ 4 ]

1 голос
/ 29 мая 2020

Вы можете сравнить количество стран на континенте с количеством, доступным на каждую дату - а затем просто использовать даты, в которых они совпадают («полные данные»).

К сожалению, Postgres соответствует не поддерживает count(distinct) как оконную функцию. Но вы можете:

SELECT c.continent, c.date,
       SUM(c.value) AS value,
        COUNT(c.country) AS countries_count
FROM (SELECT c.*,
             COUNT(*) OVER (PARTITION BY continent, date) as num_on_date
      FROM countries c
      WHERE value IS NOT NULL
     ) c JOIN
     (SELECT continent, COUNT(DISTINCT country) as num_countries
      FROM countries
      GROUP BY continent
     ) cc
     ON cc.continent = c.continent
WHERE num_on_date = num_countries
GROUP BY c.continent, c.date
ORDER BY c.date DESC, c.continent;

Здесь - это скрипт db <>.

Вы также можете сделать это с помощью фильтра в предложении HAVING:

SELECT c.continent, c.date,
       SUM(c.value) AS value,
        COUNT(c.country) AS countries_count
FROM countries c
WHERE value IS NOT NULL
GROUP BY c.continent, c.date
HAVING COUNT(*) = (SELECT COUNT(DISTINCT c2.country)
                   FROM countries c2
                   WHERE c2.continent = c.continent
                  )
ORDER BY c.date DESC, c.continent;

Выполняется агрегирование, а затем сохраняются только строки, количество строк в которых соответствует количеству стран.

1 голос
/ 29 мая 2020

Вы можете использовать NOT IN в предложении WHERE:

SELECT
    c.continent,
    c.date,
    SUM(c.value) AS value,
    COUNT(DISTINCT c.country) AS countries_count
FROM countries c
WHERE date NOT IN 
    ( SELECT date 
        FROM countries 
       WHERE value IS NULL )
GROUP BY c.continent, c.date
ORDER BY c.date DESC, c.continent;
0 голосов
/ 29 мая 2020

С функцией окна SUM():

select distinct c.continent, c.date, 
  sum(c.value) over (partition by c.continent, c.date) "value"
from countries c
where not exists (
  select 1 from countries
  where continent = c.continent and date = c.date and value is null
)  
order by c.date desc, c.continent;

См. Демонстрацию . Результатов:

| continent | date                     | value |
| --------- | ------------------------ | ----- |
| Europe    | 2020-05-27T00:00:00.000Z | 66    |
| Africa    | 2020-05-26T00:00:00.000Z | 41    |
| Europe    | 2020-05-26T00:00:00.000Z | 63    |
| Africa    | 2020-05-25T00:00:00.000Z | 40    |
| Europe    | 2020-05-25T00:00:00.000Z | 60    |
0 голосов
/ 29 мая 2020

Вы можете отфильтровать с помощью предложения having, чтобы исключить группы, в которых любая страна - null

SELECT
    continent,
    date,
    SUM(value) AS value
FROM countries
GROUP BY continent, date
HAVING BOOL_AND(value is not null)
ORDER BY date DESC, continent
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...