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 имат начална точка там. Подобно на крайния час.