Рассчитать общий спред, охватываемый несколькими диапазонами

#postgresql

#postgresql

Вопрос:

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

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

 CREATE TABLE records(id int, spread int4range);
INSERT INTO records VALUES
    (1, int4range(1, 4)),
    (1, int4range(2, 7)),
    (1, int4range(11, 15)),
    (2, int4range(3, 5)),
    (2, int4range(6, 10));
SELECT * FROM records;
  

Получение выходных данных:

  id | spread
---- ---------
  1 | [1,4)
  1 | [2,7)
  1 | [11,15)
  2 | [3,5)
  2 | [6,10)
(5 rows)
  

Теперь я хотел бы получить запрос, который выдает следующий результат:

 id | total
--- --------
 1 |  10
 2 |   6
  

Откуда взялись числа 10 и 6?Для идентификатора 1 у нас есть диапазоны, которые включают 1, 2, 3, 4, 5, 6, 11, 12, 13, и 14; всего 10 различных целых чисел. Для идентификатора 2 у нас есть диапазоны, которые включают 3, 4, 6, 7, 8, и 9; всего шесть различных целых чисел.

Если это поможет вам понять проблему, вы можете представить ее примерно так: «Если эти записи представляют собой диапазон дней и времени для встреч в моем календаре, сколько всего часов в каждом дне я забронировал хотя бы один раз?»

Версия Postgres — 9.4.8, если это имеет значение.

Ответ №1:

 select id, count(*)
from (
    select distinct id, generate_series(lower(spread), upper(spread) - 1)
    from records
) s
group by id
;
 id | count 
---- -------
  1 |    10
  2 |     6
  

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

1. Ха-ха, ответ оказывается таким простым. Забавно, как ваш разум застревает — я действительно был зациклен на гораздо более сложном процессе, когда я сначала беру объединение (обработка разрозненных диапазонов), а затем смотрю на их границы. Большое спасибо за вашу помощь.