SQL: Anzahl der Arbeitstage zwischen zwei Datumsangaben ermitteln

Aus DBWiki
Wechseln zu: Navigation, Suche

Zurück zur Übersicht



Durch Pivotisierung des Anfangs- und Enddatums über eine entsprechende Pivot- oder auch Kalendertabelle, lässt sich eine Folge von Datumswerten bilden. Mit einer dem SQL-Dialekt entsprechenden Funktion lassen sich dann Wochenenden (Samstag und Sonntag) im Kriterium herausfiltern. Anschleßend kann durch die Count()-Funktion die Anzahl der Werktage ermittelt werden. Um auch arbeitsfreie Tage an Werktagen auszuschließen, kann zusätzlich eine Tabelle mit zumindest einem Datumsfeld und der Angabe des arbeitsfreien Tags zu der Abfrage in einer NOT IN-Klausel verknüpft werden.

Eine Kalendertabelle hat den Vorteil, dass sie schon Angaben zu Wochentagen, freien Tagen, etc. beinhaltet, womit die Abfrage vermutlich schneller in der Ausführung sein wird, als eine Pivottabelle mit fortlaufenden Zahlen. Andererseits benötigt eine Kalendertabelle aber auch mehr Pflege und Speicherplatz als eine einfache Pivottabelle. Weiterhin können nur Datumsbereiche abgefragt werden, für die auch in der Kalendertabelle Daten vorliegen.


Als Übung sollen die Arbeitstage inklusive Anfangs- und Enddatum gezählt werden, die zwischen dem Einstelldatum von Mitarbeiter Johann und Blank liegen.

Dazu ist folgende Tabelle feiertag manuell anzulegen.

ft ft_name
01.01.1981 Neujahr
17.04.1981 Karfreitag
20.04.1981 Ostermontag
01.05.1981 Tag der Arbeit
28.05.1981 Christi Himmelfahrt
08.06.1981 Pfingstmontag
18.11.1981 Buß- und Bettag
24.12.1981 Heiligabend
25.12.1981 1. Weihnachtstag
31.12.1981 Silvester

Lösung für Jet-SQL

Hinweis für Access-Anwender
Um die folgenden Abfragen selbst testen zu können, müssen die Tabellen der Übungsdatenbank und die Pivivottabelle t1000 in der Datenbank vorliegen.

Zunächst benötigen wir für Johann und Blank jeweils das Einstelldatum:

SELECT Min( IIf( ma_name = 'Johann', eingestellt ) ) AS johann_ed,
       Min( IIf( ma_name = 'Blank',  eingestellt ) ) AS blank_ed
FROM mitarbeiter
WHERE  ma_name IN ( 'Blank', 'Johann' )
johann_ed blank_ed
12.04.1981 11.05.1981

Dieses verwenden wir dann als Unterabfrage:

SELECT Count(*) AS anz_arb_tage
FROM   ( SELECT Min( IIf( ma_name = 'Johann', eingestellt ) ) AS johann_ed,
                Min( IIf( ma_name = 'Blank',  eingestellt ) ) AS blank_ed
                FROM mitarbeiter
                WHERE  ma_name IN ( 'Blank', 'Johann' ) ) AS v,
       t1000
WHERE  n <= blank_ed - johann_ed AND
       Weekday( johann_ed + n, 2 ) < 6 AND
       johann_ed + n NOT IN ( SELECT ft
                              FROM   feiertag
                              WHERE  ft BETWEEN johann_ed AND blank_ed )

Lösung für PostgreSQL

SELECT COUNT( * ) AS anz_arb_tage
FROM   ( SELECT MIN( CASE WHEN ma_name = 'Johann' THEN eingestellt END ) AS johann_ed,
                MIN( CASE WHEN ma_name = 'Blank'  THEN eingestellt END ) AS blank_ed
         FROM   mitarbeiter
         WHERE  ma_name IN ( 'Johann', 'Blank' ) ) AS v,
        generate_series( johann_ed, blank_ed, INTERVAL '1 day' ) AS dt
WHERE  EXTRACT( 'ISODOW' FROM dt ) < 6 AND
       dt NOT IN ( SELECT ft
                   FROM   feiertag
                   WHERE  ft BETWEEN johann_ed AND blank_ed )

Hier ist ein entsprechender DBFiddle.


Als Ergebnis erhalten wir 18. Das sind 21 Werktage minus Karfreitag, Ostermontag und 1. Mai.



Zurück zur Übersicht