Verschiedene Datumswerte analysieren und korrekt ausgeben

ny_unity

SQL-Guru
Beiträge
208
Hallo,

ich habe eine Ergebnisliste wo ich vier Datumswerte habe, die ich korrekt ausgeben will, aber ein kurzes Beispiel:

Einsatz von
Einsatz bis
Referenz von
Referenz bis
Preis
05.04.202230.09.202208.11.201831.05.202317,40
01.10.202204.07.202301.06.202318,31
01.10.202204.07.202308.11.201831.05.202317,40
05.07.202304.10.202301.06.202318,31

Ich möchte jetzt die Zeilen, wo mehrere Referenzen in dem Zeitraum liegen, logisch zusammenfassen, also, das Ergebnis soll sein:

Einsatz von
Einsatz bis
Referenz von
Referenz bis
Preis
05.04.202230.09.202208.11.201831.05.202317,40
01.10.202231.05.202208.11.201831.05.202317,40
01.06.202304.07.202301.06.202318,31
05.07.202304.10.202301.06.202318,31

Hat jemand ne Idee? Ich denke, hier kommt wieder row_number() und so zum Einsatz, oder?

Besten Dank.
 
Werbung:
Referenz bis ist manchmal ein Datum und manchmal ein String?

Falls das nur in der falschen Spalte ist und nach Preis gehören sollte, denke ich, du brauchst nur sortieren mit NULLS LAST. Du könntest auch, eleganter, DATERANGE nehmen und dann das obere Ende auf unendlich setzen und so sortieren.
 
Code:
postgres=# create table ny_unity(einsatz daterange, referenz daterange, preis numeric);
CREATE TABLE
postgres=# insert into ny_unity values ('[2022-04-05,2022-09-30)','[2018-11-08,2023-05-31)',17.4);
INSERT 0 1
postgres=# insert into ny_unity values ('[2022-10-01,2023-07-04)','[2023-06-01,infinity)',18.31);
INSERT 0 1
postgres=# insert into ny_unity values ('[2022-10-01,2023-07-04)','[2018-11-08,2023-05-31)',17.4);
INSERT 0 1
postgres=# insert into ny_unity values ('[2023-07-05,2023-10-04)','[2023-06-01,infinity)',18.31);
INSERT 0 1
postgres=# select * from ny_unity ;
         einsatz         |        referenz         | preis 
-------------------------+-------------------------+-------
 [2022-04-05,2022-09-30) | [2018-11-08,2023-05-31) |  17.4
 [2022-10-01,2023-07-04) | [2023-06-01,infinity)   | 18.31
 [2022-10-01,2023-07-04) | [2018-11-08,2023-05-31) |  17.4
 [2023-07-05,2023-10-04) | [2023-06-01,infinity)   | 18.31
(4 rows)

postgres=# select * from ny_unity order by upper(referenz) nulls last;
         einsatz         |        referenz         | preis 
-------------------------+-------------------------+-------
 [2022-04-05,2022-09-30) | [2018-11-08,2023-05-31) |  17.4
 [2022-10-01,2023-07-04) | [2018-11-08,2023-05-31) |  17.4
 [2022-10-01,2023-07-04) | [2023-06-01,infinity)   | 18.31
 [2023-07-05,2023-10-04) | [2023-06-01,infinity)   | 18.31
(4 rows)

postgres=#
 
so, habs, aber so richtig bin ich aus dem ergebnis nicht schlau geworden @akretschmer

Code:
select daterange(einsatzvon, einsatzbis, '[]') as daterangeinsatz, daterange(refvon, refbis, '[]') as daterangereferenz
order by upper(daterange(refvon, refbis, '[]')) nulls last

daterangeeinsatzdaterangereferenzpreis
2022-04-05,2022-10-012018-11-08,2023-06-0117,40
2022-10-01,2023-07-052018-11-08,2023-06-0117,40
2023-07-05,2023-10-052023-06-01,2100-01-0118,31
2022-10-01,2023-07-052023-06-01,2100-01-0118,31

muss dazusagen, habe wenn refpreisbis null war/ist, auf 2099-12-31 gestellt
 
Zuletzt bearbeitet:
ja gut, wenn ich null lasse, erhalte ich bei daterangereferenz dann "2023-06-01," statt "2023-06-01,2100-01-01"

was ich nicht verstanden habe, wie ich diese Werte jetzt verarbeiten kann, also mein start und ende datum.
 
ja gut, wenn ich null lasse, erhalte ich bei daterangereferenz dann "2023-06-01," statt "2023-06-01,2100-01-01"
Letztendlich ist das das Gleiche. 2023-06-01, bedeutet "beginnt am 1.6. 2023 und hat kein Ende."
Wenn Du "kein Ende" wirklich explizit machen willst, würde ich eher zu 'infininity' raten, statt 2100-01-01
 
was ich nicht verstanden habe, wie ich diese Werte jetzt verarbeiten kann, also mein start und ende datum.
Code:
postgres=# select * from ny_unity ;
         einsatz         |        referenz         | preis 
-------------------------+-------------------------+-------
 [2022-04-05,2022-09-30) | [2018-11-08,2023-05-31) |  17.4
 [2022-10-01,2023-07-04) | [2023-06-01,infinity)   | 18.31
 [2022-10-01,2023-07-04) | [2018-11-08,2023-05-31) |  17.4
 [2023-07-05,2023-10-04) | [2023-06-01,infinity)   | 18.31
(4 rows)

postgres=# select lower(einsatz), upper(einsatz) from ny_unity ;
   lower    |   upper    
------------+------------
 2022-04-05 | 2022-09-30
 2022-10-01 | 2023-07-04
 2022-10-01 | 2023-07-04
 2023-07-05 | 2023-10-04
(4 rows)
 
danke, da bekomme ich fast das, was ich haben möchte:

einsatz voneinsatz bispreis
2023-07-052023-10-0518,31
2022-04-052022-10-0117,40
2022-10-012023-07-0518,31
2022-10-012023-07-0517,40

Bei den letzten zwei Zeilen wollte ich ja folgendes:
2022-10-01 bis 2023-05-31 mit 17,40
2023-06-01 bis 2023-07-05 mit 18,31
 
update, habe es glaube lösen können:
SQL:
select
cast(upper(daterange(case when einsatzvon < refvon then einsatzvon else refvon end, case when einsatzvon > refvon then einsatzvon else refvon end, '[]')) - interval '1 day' as date) as von,
    case when refbis is null then einsatzbis else cast(lower(daterange(case when einsatzbis < refbis then einsatzbis else refbis end, case when einsatzbis > refbis then einsatzbis else refbis end, '[]')) as date) end as bis
 
hallo @akretschmer,

ich muss das thema nochmal aufgreifen, ich hab zwar das erste problem jetzt sauber gelöst, allerdingt benötige ich noch ein weiteres datum,

Personalnrvonbis
119172024-03-182025-06-17
119172023-09-132023-12-12
119172022-06-132022-09-12
119172021-12-072022-03-06
119172021-04-012021-12-06
119172020-09-072020-03-31
119172020-03-032020-06-01
119172020-01-312020-03-02
119172020-01-282020-01-30
119172019-03-042020-01-27
119172018-12-032019-03-03

Was ich jetzt haben möchte ist ein Zeitraum über alle Datensätze hinweg, aber nicht einfach min() und max() auf personalnr gruppiert, sondern nur dann min und max, wenn zwischen dem vorherigen bis und dem jetzigen von, weniger als drei monate liegen.

Personalnrvonbis
119172024-03-182025-06-17
119172022-06-132023-12-12
119172020-09-072022-03-06
119172018-12-032020-06-01

Kann man das auch mit daterange machen, oder mit rowcounter?
 
Werbung:
Ich glaube du hast Rechenfehler in deiner Zielmenge, zwischen von 2020-09-07 bis 2020-03-31 und von 2021-04-01 liegt ein Jahr, das wird in deinem Ergebnis irgendwie verschluckt. Oder ich habe noch einen Denkfehler.

Denkfehler und Range-Datentyp hin oder her, etwa so könnte es gehen:
Code:
WITH tabelle(Personalnr,von,bis) AS (
    SELECT 11917,'2024-03-18','2025-06-17' UNION ALL
    SELECT 11917,'2023-09-13','2023-12-12' UNION ALL
    SELECT 11917,'2022-06-13','2022-09-12' UNION ALL
    SELECT 11917,'2021-12-07','2022-03-06' UNION ALL
    SELECT 11917,'2021-04-01','2021-12-06' UNION ALL
    SELECT 11917,'2020-09-07','2020-03-31' UNION ALL
    SELECT 11917,'2020-03-03','2020-06-01' UNION ALL
    SELECT 11917,'2020-01-31','2020-03-02' UNION ALL
    SELECT 11917,'2020-01-28','2020-01-30' UNION ALL
    SELECT 11917,'2019-03-04','2020-01-27' UNION ALL
    SELECT 11917,'2018-12-03','2019-03-03'
    )
SELECT    Personalnr,
        sum_unterbrechung,
        min(von) AS min_von,
        max(bis) AS min_bis
FROM    (
SELECT    *,
        sum(unterbrechung) OVER (PARTITION BY Personalnr ORDER BY von) AS sum_unterbrechung
FROM    (

SELECT    *,
--        lag(bis) OVER (PARTITION BY Personalnr ORDER BY von) AS lagbis,
--        datediff(day,lag(bis) OVER (PARTITION BY Personalnr ORDER BY von),von) AS diff_lagbis_von,
        ( CASE WHEN datediff(day,lag(bis) OVER (PARTITION BY Personalnr ORDER BY von),von) > 90 THEN 1 ELSE 0 END ) AS unterbrechung
FROM    tabelle

        ) t1

        ) t2
GROUP BY Personalnr,sum_unterbrechung
ORDER BY Personalnr,sum_unterbrechung DESC
Kurz erläutert:
1) Du bringst die Datensätze in eine Reihe und ermittelst mit lag() das bis des Vorgänger-Datensatzes.
2) Du ermittelst die Zeit zwischen dem bis und von, z.B: mittels datediff(). *
3) Wenn dir der Zeitraum zu groß ist, gibst du eine 1 aus. Du zeigst damit eine Unterbrechung an.
4) Du bringst die Daten erneut in Reihe und bildest eine laufende Summe der Unterbrechungen, es entsteht sowas wie eine Gruppen ID zusammengehöriger Datensätze.
5) Du gruppierst nach den Untergruppen (natürlich immer alles innerhalb der Personalnr) und ermittelst min(von) und max(bis) abhängig zur Untergruppe.

Wenn dein CASE stimmt, sollte das Wunschergebnis kommen. Aber Achtung (*): datediff(month,...) ermittelt nur die Differenz im Monats-Zeitanteil, nicht der Zeitunterschied in Monaten. Das kann man hier sehr gut sehen und wird sehr leicht mit Rundungsdifferenzen verwechselt:
Code:
SELECT    datediff(month,'2024-03-01','2024-03-31') AS diff_0,
        datediff(month,'2024-03-31','2024-04-01') AS diff_1
Daher habe ich jetzt hier erstmal den Weg über 90 Tage gewählt. Am Grundprinzip ändert das ja nichts.
 
Zuletzt bearbeitet:
Zurück
Oben