demo:db<>fiddle (používá starou datovou sadu s překrývající se částí A-B)
Odmítnutí odpovědnosti: Toto funguje pro denní intervaly, nikoli pro časová razítka. Požadavek na ts přišel později.
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
generuje všechna data mezi začátkem a koncem. Takže každé datum, kdy aktivita existuje, dostane jeden řádek s konkrétnímcount
- Seskupit všechna data, agregovat všechny existující aktivity a součet jejich počtů
HAVING
filtruje data, kdy existuje pouze jedna aktivita- Protože existují různé dny se stejnými aktivitami, potřebujeme pouze jednoho zástupce:Filtrujte všechny duplikáty pomocí
DISTINCT ON
- Připojte tento výsledek k původní tabulce, abyste získali začátek a konec. (všimněte si, že „konec“ je v Postgresu vyhrazené slovo, měli byste si najít jiný název sloupce!). Dříve bylo pohodlnější o ně přijít, ale je možné tato data získat v rámci dílčího dotazu.
- Seskupit toto spojení, abyste získali co nejčasnější a nejnovější datum každého intervalu.
Zde je verze časových razítek:
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
Hlavní myšlenkou je identifikovat možné časové úseky. Takže vezmu každý známý čas (jak začátek, tak konec) a dám je do seřazeného seznamu. Mohu tedy vzít první vlek známé časy (17:00 ze startu A a 18:00 ze startu B) a zkontrolovat, který interval je v něm. Pak to zkontroluji pro 2. a 3., pak pro 3. a 4. a tak dále.
V prvním časovém úseku se hodí pouze A. Ve druhém od 18-19 se hodí i B. V dalším slotu 19-20 také C, od 20 do 20:30 A už se nevejde, jen B a C. Další je 20:30-22, kde se vejde jen B, nakonec se přidá 22-23 D B a v neposlední řadě jen D se vejde do 23-23:30.
Vezmu tedy tento časový seznam a připojím ho k tabulce činností, kde se intervaly protínají. Poté je to pouze seskupení podle časového úseku a sečtení svého počtu.
- to umístí obě části řádku do jednoho pole, jehož prvky jsou rozbaleny do jednoho řádku na prvek pomocí
unnest
. Dostanu všechny časy do jednoho sloupce, který lze jednoduše seřadit - pomocí hlavní funkce okna
umožňuje převzít hodnotu dalšího řádku do aktuálního. Takže mohu vytvořit rozsah časových razítek z těchto obou hodnot pomocí
tsrange
- Tento filtr je nezbytný, protože poslední řádek nemá žádnou "další hodnotu". Tím se vytvoří
NULL
hodnota, která je interpretována pomocítsrange
jako nekonečno. Takže by to vytvořilo neuvěřitelně špatný časový úsek. Tento řádek tedy musíme odfiltrovat. - Připojte se k časovým úsekům oproti původnímu stolu.
&&
operátor zkontroluje, zda se dva typy rozsahů překrývají. - Seskupování podle jednotlivých časových úseků, agregace jmen a počtu. Odfiltrujte časové úseky pouze jednou aktivitou pomocí
HAVING
doložka - Trochu složité najít správné počáteční a koncové body. Počáteční body jsou tedy buď maximum začátku aktivity, nebo začátek časového úseku (který lze získat pomocí
lower
). Např. Vezměte si slot 20-20:30:Začíná 20h, ale ani B ani C tam nemají počáteční bod. Podobný čas ukončení.