Sumowanie elementów w grupie w danym przedziale czasowym

0

Hej, dopiero zaczynam swoją przygodę z PostgreSQL i mam mały zadaniowy problem.

Jest taka sytuacja, że muszę obliczyć łączoną sumę czasów (elementów) oddzielnie dla każdej z grup gdzie każda składa się ze swoich elementów.
Czyli mamy element z pewnym czasem trwania obliczonym na podstawie czasu start i stop (czas trwania = czas stop - czas start). Teraz muszę posumować odpowiednie elementy (w zależności od tego jaką grupę biorę pod uwagę) ale tak, żeby czasy występowania się nie duplikowały i dodatkowo znajdowały się w przedziale czasowym jaka każda z grup ma nałożoną z góry (tylko w tym oknie czasowym elementy powinny sie sumować). Tak myślę, że muszę te elementy nałożyć na swego rodzaju oś czasu i obliczyć różnice między czasem start a czasem stop krańcowych elementów ale nie mam pojęcia jak to zapisać w PostgreSQL (dodam może prowizoryczny rysunek o co mi chodzi).

Problem polega na tym, że każda dana (okna czasowe grup, jakie elementy składają się na grupę, czasy strat/stop każdego elementu) znajduję się w innej tabel w bazie powiązanej odpowiednimi relacjami.
Na ten moment udało mi się jakoś wyciągnąć do oddzielnej tabeli daną grupę z jej elementami, czasem trwania niedostępności danego elementu i oknem czasowym grupy ale nie mam pojęcia jak obliczyć sumę tych elementów dla całej grupy tak aby uwzględnić te moje parametry o oknie czasowym i o nie duplikowaniu się czasu.

Mam nadzieję, że dobrze to opisałam.
Może mogłabym Was prosić o pomoc, wskazówki jak podejść do tego rozwiązania i to obliczyć (łączny czas zaznaczony na zielono na rysunku)?

Jak sumować niedostepność w czasie.png

2

ja bym zaczynał od modelu danych i kilku przykładowych wierszy.

do łączenia wyniku z kilku tabel https://www.w3schools.com/sql/sql_union.asp

0

Masz min i max na tej postawie wyciągniesz start i stop dla grupy a potem na tym zakładasz odpowiednie funkcję w celu wyliczenia różnicy. Pokaż co ma masz i jak to robisz, bo może coś źle rozumiem.

0
S4t napisał(a):

Masz min i max na tej postawie wyciągniesz start i stop dla grupy a potem na tym zakładasz odpowiednie funkcję w celu wyliczenia różnicy. Pokaż co ma masz i jak to robisz, bo może coś źle rozumiem.

myślałam o takim podejściu jak piszesz ale co w sytuacji jeśli pomiędzy czasem min a max jest "luka" czasowa, która nie powinna być liczona? może zdarzyć się tak, że jeden element pojawi sie kilkakrotnie ale w różnym czasie np. mamy awarię, która wpływa na dostępność (a raczej jej brak) kilku aplikacji, te z kolei wpływają na dostępność danej usługi (usługa składa się z kilku różnych aplikacji) do, której są przypisane. Awarii może być kilka w różnym czasie i wtedy z tym min max może być ciężko bo uwzględniałabym też czas między wystąpieniem jednej a drugiej awarii.

Na ten moment zwykłam selectem wyciągam z tabel w bazie zestawienie usług i aplikacji z jakich się składają + zwykła suma czasów aplikacji (tutaj niestety one sie nakładają i sumuję mi się kilka razy ten sam czas).

SELECT DISTINCT 
    ur.nazwa_raportu,
    ur.usluga_id,
    ur.nazwa_uslugi,
    ua.nazwa_aplikacja,
    ua.app_id,
    sum(li.czas_trwania::interval) OVER (PARTITION BY ua.nazwa_aplikacja) AS sum_czas_niedostepnosc,
FROM 
    usluga_raport ur,
    usluga_aplikacja ua,
    usluga u,
    aplikacja a,
    lista_inc li
WHERE 
    ur.raport_id = 1 AND li.app_id = a.app_id AND a.app_id = ua.app_id AND ua.usluga_id = u.usluga_id AND u.usluga_id = ur.usluga_id
ORDER BY ur.nazwa_uslugi;
0

Raczej nie zrobisz tego pojedyńczym zapytaniem - może jako procedura, ale też będzie siermiężne - przypuszczalnie będziesz musiała te dane obrobić poza SQL'em.
Robiłem coś podobnego i też się tak to skończyło. Wyjęciem wszystkich zdarzeń do tymczasowej tablicy i tam ich analiza.

0

pomysł 1) Nie wszystko trzeba robić po stronie SQL, czasami łatwiej i szybciej jest ściągnąć dane do klienta i policzyć w jakimś normalnym języku
pomysł 2) Te zadanie chyba nie jest banalne może trzeba usiąść z kartką i przemyśleć wszystkie możliwe przypadki
pomysł 3) Jak już wiemy jak to zrobić to w PostgreSql sa procedury składowane i piekne rzeczy mozna w tym zrobić

0
Paulina_ina napisał(a):
S4t napisał(a):

Masz min i max na tej postawie wyciągniesz start i stop dla grupy a potem na tym zakładasz odpowiednie funkcję w celu wyliczenia różnicy. Pokaż co ma masz i jak to robisz, bo może coś źle rozumiem.

myślałam o takim podejściu jak piszesz ale co w sytuacji jeśli pomiędzy czasem min a max jest "luka" czasowa, która nie powinna być liczona? może zdarzyć się tak, że jeden element pojawi sie kilkakrotnie ale w różnym czasie np. mamy awarię, która wpływa na dostępność (a raczej jej brak) kilku aplikacji, te z kolei wpływają na dostępność danej usługi (usługa składa się z kilku różnych aplikacji) do, której są przypisane. Awarii może być kilka w różnym czasie i wtedy z tym min max może być ciężko bo uwzględniałabym też czas między wystąpieniem jednej a drugiej awarii.

Na ten moment zwykłam selectem wyciągam z tabel w bazie zestawienie usług i aplikacji z jakich się składają + zwykła suma czasów aplikacji (tutaj niestety one sie nakładają i sumuję mi się kilka razy ten sam czas).

SELECT DISTINCT
ur.nazwa_raportu,
ur.usluga_id,
ur.nazwa_uslugi,
ua.nazwa_aplikacja,
ua.app_id,
sum(li.czas_trwania::interval) OVER (PARTITION BY ua.nazwa_aplikacja) AS sum_czas_niedostepnosc,
FROM
usluga_raport ur,
usluga_aplikacja ua,
usluga u,
aplikacja a,
lista_inc li
WHERE
ur.raport_id = 1 AND li.app_id = a.app_id AND a.app_id = ua.app_id AND ua.usluga_id = u.usluga_id AND u.usluga_id = ur.usluga_id
ORDER BY ur.nazwa_uslugi;

Tu chyba tylko ratuje procedura składowana. Ja bym to zrobił tak że najpierw wyciągał początek i koniec taki globalny a potem drugim zapytaniem "dziury" tak na sucho to mi ciężko powiedzieć czy jest to możliwe w jednym zapytaniu ( raczej nie i trzeba przejść po wszystkich wierszach).

0

Czy liczba tych komponentów: A,B,C,D jest stała? Skończona?

0

W calym Twoim zapytaniu, nie widzę warunku where , ani jak nazywają się te pola ze startem i końcem.

Ja podszedłbym do problemu tak wybrał z bazy rekordy które mieszczą się w przedziale czasowym i interesujące usługi, w selecie zmienił data startu i stopu jeżeli wychodzą poza zakres, od tych zmienionych zakresów policzył czas i zsumował.

Coś w ten deseń:

WITH rpt AS (
select *
,case when ustart < '08:00:00' then '08:00:00' else ustart end nstart
,case when ustop > '20:00:00' then '20:00:00' else ustop end nstop
from tb
where
  ('08:00:00' between ustart and ustop
   or
   '20:00:00' between ustart and ustop
   or
     ustart between '08:00:00' and '20:00:00'
   or
     ustop between '08:00:00' and '20:00:00'   
   )
   and usluga in ('A','B','C')
 )
 
 select *,nstop-nstart from rpt

http://sqlfiddle.com/#!15/db5b27/16

P.S. nie jestem pewny tego warunku na czas, to do sprawdzenia

0

Może ten wątek coś ci pomoże - łączenie okresów czasowych:

Sumowanie czasów z pominięciem nakładających się na siebie okresów

1 użytkowników online, w tym zalogowanych: 0, gości: 1