demo:db<>fiddle (използва стария набор от данни с припокриващата се A-B-част)
Отказ от отговорност: Това работи за дневни интервали, а не за времеви отпечатъци. Изискването за ts дойде по-късно.
SELECT
s.acts,
s.sum,
MIN(a.start) as start,
MAX(a.end) as end
FROM (
SELECT DISTINCT ON (acts)
array_agg(name) as acts,
SUM(count)
FROM
activities, generate_series(start, "end", interval '1 day') gs
GROUP BY gs
HAVING cardinality(array_agg(name)) > 1
) s
JOIN activities a
ON a.name = ANY(s.acts)
GROUP BY s.acts, s.sum
generate_series
генерира всички дати между началото и края. Така че всяка дата, на която съществува дейност, получава един ред с конкретнияcount
- Групиране на всички дати, обобщаване на всички съществуващи дейности и сумата от броя им
HAVING
филтрира датите, на които съществува само една дейност- Тъй като има различни дни с едни и същи дейности, имаме нужда само от един представител:Филтрирайте всички дубликати с
DISTINCT ON
- Свържете този резултат с оригиналната таблица, за да получите началото и края. (обърнете внимание, че „край“ е запазена дума в Postgres, по-добре е да намерите друго име на колона!). Беше по-удобно да ги загубите преди, но е възможно да получите тези данни в подзаявката.
- Групирайте това присъединяване, за да получите най-ранната и най-късната дата за всеки интервал.
Ето версия за клеймото за време:
WITH timeslots AS (
SELECT * FROM (
SELECT
tsrange(timepoint, lead(timepoint) OVER (ORDER BY timepoint)),
lead(timepoint) OVER (ORDER BY timepoint) -- 2
FROM (
SELECT
unnest(ARRAY[start, "end"]) as timepoint -- 1
FROM
activities
ORDER BY timepoint
) s
)s WHERE lead IS NOT NULL -- 3
)
SELECT
GREATEST(MAX(start), lower(tsrange)), -- 6
LEAST(MIN("end"), upper(tsrange)),
array_agg(name), -- 5
sum(count)
FROM
timeslots t
JOIN activities a
ON t.tsrange && tsrange(a.start, a.end) -- 4
GROUP BY tsrange
HAVING cardinality(array_agg(name)) > 1
Основната идея е да се идентифицират възможните времеви интервали. Така че вземам всяко известно време (начало и край) и ги поставям в сортиран списък. Така че мога да взема познатите часове за първото теглене (17:00 от начало А и 18:00 от начало B) и да проверя кой интервал е в него. След това го проверявам за 2-ри и 3-ти, след това за 3-ти, 4-ти и така нататък.
В първия времеви интервал само A пасва. Във втория от 18-19 също B се вписва. В следващия слот 19-20 също C, от 20 до 20:30 A вече не пасва, само B и C. Следващият е 20:30-22, където само B пасва, накрая 22-23 D се добавя към B и не на последно място само D се вписва в 23-23:30.
Така че вземам този списък с време и го свързвам с таблицата с дейности, където интервалите се пресичат. След това има само групиране по времеви интервал и обобщаване на вашия брой.
- това поставя и двете ts на ред в един масив, чиито елементи се разширяват в един ред на елемент с
unnest
. Така че получавам всички времена в една колона, която може просто да се подреди - използване на водещата прозоречна функция
позволява да се вземе стойността на следващия ред в текущия. Така че мога да създам диапазон от времево клеймо от тези две стойности с
tsrange
- Този филтър е необходим, защото последният ред няма „следваща стойност“. Това създава
NULL
стойност, която се интерпретира отtsrange
като безкрайност. Така че това би създало невероятно грешен времеви интервал. Така че трябва да филтрираме този ред. - Съединете времевите интервали спрямо оригиналната таблица.
&&
операторът проверява дали два типа диапазони се припокриват. - Групиране по отделни времеви интервали, обобщаване на имената и броя. Филтрирайте времевите интервали само с една дейност, като използвате
HAVING
клауза - Малко трудно е да получите точната начална и крайна точка. Така че началните точки са или максимумът на началото на дейността, или началото на времеви интервал (който може да бъде получен чрез
lower
). напр. Вземете интервала 20-20:30:Започва в 20h, но нито B, нито C имат начална точка там. Подобно на крайния час.