SQL Mssql

Agt.Romanoff

Lt. Junior Grade
Registriert
Aug. 2011
Beiträge
288
Hallo Leute,

ich habe eine kleines Problem und mir fehlt der letzte Denkansatz.
Eine Abfrage soll mir ausgeben zu welchen Zeiten die Frühschicht angefangen hat. Ich habe bereits mir die entsprechende Informationen zusammengesucht aus diversen Tabellen mit Kürzeln, Anwesenheitszeiten und Arbeitszeiten der Frühschicht.
Das Problem ist, das die Start- und Endzeiten der Frühschicht (also Montag 8.00 Uhr bis Freitag 16.30 Uhr), aber auch die Arbeitszeiten der Mitarbeiter in den selben Spalten gespeichert werden. In einer anderen Spalte wird das Kürzel des Mitarbeiters gespeichert, welcher für die Frühschicht eingeteilt ist.
Es sollen jetzt halt die Arbeitszeiten ausgegeben werden, von dem Mitarbeiter, der in der Woche für die Frühschicht eingeteilt war.


Code:
startdate               Endate                  Subject          Description    rn       Abteilung
2016-05-10 07:45:17.000	2016-05-10 10:26:02.000	Anwesend	07:45 - 10:26	JM	Betriebsteam
2016-05-10 07:45:45.000	2016-05-10 09:38:46.000	Anwesend	07:45 - 09:38	KA	Betriebsteam
2016-05-10 08:00:00.000	2016-05-13 16:30:00.000	Frühdienst Betrieb		JM	Betriebsteam
2016-05-10 08:00:00.000	2016-05-13 16:30:00.000	Frühdienst Betrieb		JM	Betriebsteam
2016-05-10 08:00:00.000	2016-05-13 16:30:00.000	Frühdienst Betrieb		JM	Betriebsteam
2016-05-10 08:00:00.000	2016-05-13 16:30:00.000	Frühdienst Betrieb		JM	Betriebsteam
2016-05-10 08:30:19.000	2016-05-10 17:13:39.000	Anwesend	08:30 - 17:13	RI	Betriebsteam
2016-05-10 08:30:19.000	2016-05-10 17:13:39.000	Anwesend	08:30 - 17:13	RI	Betriebsteam
2016-05-10 08:34:59.000	2016-05-10 12:02:56.000	Anwesend	08:34 - 12:02	HL	Betriebsteam
2016-05-10 09:41:15.000	2016-05-10 10:25:57.000	Anwesend	09:41 - 10:25	MR	Betriebsteam
2016-05-10 10:32:04.000	2016-05-10 18:06:03.000	Anwesend	10:32 - 18:06	MR	Betriebsteam
2016-05-10 10:32:09.000	2016-05-10 12:16:18.000	Anwesend	10:32 - 12:16	JM	Betriebsteam
2016-05-10 12:25:45.000	2016-05-10 18:08:13.000	Anwesend	12:25 - 18:08	HL	Betriebsteam
2016-05-10 12:36:00.000	2016-05-10 17:10:48.000	Anwesend	12:36 - 17:10	PH	Betriebsteam
2016-05-10 13:16:25.000	2016-05-10 17:08:50.000	Anwesend	13:16 - 17:08	JM	Betriebsteam
2016-05-11 07:54:24.000	2016-05-11 12:12:30.000	Anwesend	07:54 - 12:12	JM	Betriebsteam
2016-05-11 08:37:25.000	2016-05-11 12:33:06.000	Anwesend	08:37 - 12:33	HL	Betriebsteam

Die Abfrage hierzu sieht wie folgt aus:


Code:
Select Datenbank.startdate,
Datenbank.enddate,
  Datenbank.subject,
  Datenbank.description,
  Datenbank.resourcename,
  Datenbank.abteilung
From Datenbank
Where (Datenbank.startdate >= '10.05.2016' And
  Datenbank.subject = 'Frühdienst Betrieb' And
  Datenbank.abteilung = 'Betriebsteam') Or
  (Datenbank.startdate >= '10.05.2016' And
  Datenbank.subject = 'anwesend' And
  Datenbank.abteilung = 'Betriebsteam')
  order by startdate

Ich habe schon einen ungefähren Denkansatz, aber kann ihn nicht so richtig in eine Abfrage packen.
So ungefähr: suche mir den Mitarbeiter der bei Frühdienst Betrieb drin steht und gib mir dessen Anwesenheitszeiten aus für den Zeitraum des Frühdienst.

Wäre toll, wenn sich das jemand anschauen könnte, weil ich einfach auf dem Schlauch stehe.
Mein Gedanke war:
Suche mir Frühdienst Betrieb - Merk dir Mitarbeiter-Kürzel und Zeitraum (Woche) der Frühschicht und gibt mir die Anwesenheitszeiten des Mitarbeiter-Kürzel innerhalb seiner Frühschicht-Woche

Danke.
die glühenden Kohle

PS: das mit "startdate >= 10.05.2016" liegt nur daran, dass da das Zeitmodell begonnen hat und er mir sonst alle anderen Anwesendheitszeiten des Betriebsteams ausgeben würde.
 
Zuletzt bearbeitet: (Vereinfacht)
Hallo,
sorry ich stehe gerade auf dem Schlauch. Irgendwie verstehe ich dein Problem nicht. Lt. deiner Tabelle sind die Start- und Endzeiten mit Datum + Uhrzeit (datetime) in jeweils einer separaten Spalte untergebracht...

greetz
hroessler
 
Zuletzt bearbeitet von einem Moderator:
Ich habe es mal mit PostgreSQL gemacht....bei der Gross-/Kleinschreibung und ggf. auch noch Umlauten in den Datebank-, Schema-, Tabellen- und Spaltennamen wird mir immer schlecht.

Hier zunächst das komplette Beispiel aus SQL Fiddle:
http://sqlfiddle.com/#!17/72444/6

Dann noch einmal nur der Code inkl. der finalen Abfrage.
Code:
--
-- PostgreSQL 9.3+
--

--
-- Create sample table
--
CREATE TABLE IF NOT EXISTS datenbank (
  startdate    timestamp without time zone NOT NULL DEFAULT now(),
  enddate      timestamp without time zone NOT NULL DEFAULT now(),
  subject      character varying NOT NULL,
  description  character varying,
  resourcename character varying NOT NULL,
  abteilung    character varying NOT NULL
);

--
-- Constraints
--
ALTER TABLE datenbank ADD CONSTRAINT datenbank_description_chk CHECK((description IS NULL AND subject='Frühdienst Betrieb') OR (description IS NOT NULL AND subject!='Frühdienst Betrieb'));


--
-- Add sample data to table
--
INSERT INTO datenbank (startdate, enddate, subject, description, resourcename, abteilung) SELECT '2016-05-10 07:45:17.000', '2016-05-10 10:26:02.000', 'Anwesend',           '07:45 - 10:26', 'JM', 'Betriebsteam';
INSERT INTO datenbank (startdate, enddate, subject, description, resourcename, abteilung) SELECT '2016-05-10 07:45:45.000', '2016-05-10 09:38:46.000', 'Anwesend',           '07:45 - 09:38', 'KA', 'Betriebsteam';
INSERT INTO datenbank (startdate, enddate, subject, description, resourcename, abteilung) SELECT '2016-05-10 08:00:00.000', '2016-05-13 16:30:00.000', 'Frühdienst Betrieb', NULL,            'JM', 'Betriebsteam';
INSERT INTO datenbank (startdate, enddate, subject, description, resourcename, abteilung) SELECT '2016-05-10 08:00:00.000', '2016-05-13 16:30:00.000', 'Frühdienst Betrieb', NULL,            'JM', 'Betriebsteam';
INSERT INTO datenbank (startdate, enddate, subject, description, resourcename, abteilung) SELECT '2016-05-10 08:00:00.000', '2016-05-13 16:30:00.000', 'Frühdienst Betrieb', NULL,            'JM', 'Betriebsteam';
INSERT INTO datenbank (startdate, enddate, subject, description, resourcename, abteilung) SELECT '2016-05-10 08:00:00.000', '2016-05-13 16:30:00.000', 'Frühdienst Betrieb', NULL,            'JM', 'Betriebsteam';
INSERT INTO datenbank (startdate, enddate, subject, description, resourcename, abteilung) SELECT '2016-05-10 08:30:19.000', '2016-05-10 17:13:39.000', 'Anwesend',           '08:30 - 17:13', 'RI', 'Betriebsteam';
INSERT INTO datenbank (startdate, enddate, subject, description, resourcename, abteilung) SELECT '2016-05-10 08:30:19.000', '2016-05-10 17:13:39.000', 'Anwesend',           '08:30 - 17:13', 'RI', 'Betriebsteam';
INSERT INTO datenbank (startdate, enddate, subject, description, resourcename, abteilung) SELECT '2016-05-10 08:34:59.000', '2016-05-10 12:02:56.000', 'Anwesend',           '08:34 - 12:02', 'HL', 'Betriebsteam';
INSERT INTO datenbank (startdate, enddate, subject, description, resourcename, abteilung) SELECT '2016-05-10 09:41:15.000', '2016-05-10 10:25:57.000', 'Anwesend',           '09:41 - 10:25', 'MR', 'Betriebsteam';
INSERT INTO datenbank (startdate, enddate, subject, description, resourcename, abteilung) SELECT '2016-05-10 10:32:04.000', '2016-05-10 18:06:03.000', 'Anwesend',           '10:32 - 18:06', 'MR', 'Betriebsteam';
INSERT INTO datenbank (startdate, enddate, subject, description, resourcename, abteilung) SELECT '2016-05-10 10:32:09.000', '2016-05-10 12:16:18.000', 'Anwesend',           '10:32 - 12:16', 'JM', 'Betriebsteam';
INSERT INTO datenbank (startdate, enddate, subject, description, resourcename, abteilung) SELECT '2016-05-10 12:25:45.000', '2016-05-10 18:08:13.000', 'Anwesend',           '12:25 - 18:08', 'HL', 'Betriebsteam';
INSERT INTO datenbank (startdate, enddate, subject, description, resourcename, abteilung) SELECT '2016-05-10 12:36:00.000', '2016-05-10 17:10:48.000', 'Anwesend',           '12:36 - 17:10', 'PH', 'Betriebsteam';
INSERT INTO datenbank (startdate, enddate, subject, description, resourcename, abteilung) SELECT '2016-05-10 13:16:25.000', '2016-05-10 17:08:50.000', 'Anwesend',           '13:16 - 17:08', 'JM', 'Betriebsteam';
INSERT INTO datenbank (startdate, enddate, subject, description, resourcename, abteilung) SELECT '2016-05-11 07:54:24.000', '2016-05-11 12:12:30.000', 'Anwesend',           '07:54 - 12:12', 'JM', 'Betriebsteam';
INSERT INTO datenbank (startdate, enddate, subject, description, resourcename, abteilung) SELECT '2016-05-11 08:37:25.000', '2016-05-11 12:33:06.000', 'Anwesend',           '08:37 - 12:33', 'HL', 'Betriebsteam';


--
-- Zeige mit die Mitarbeiterzeiten für die definierten Schichten (mit dem dort aufgeführten Mitarbeiter) an.
--
SELECT datenbank.resourcename
      ,datenbank.subject
      ,CAST(datenbank.startdate AS date) AS startdate
      ,CAST(datenbank.enddate   AS date) AS enddate
      ,datenbank.description
      ,subselect.startdate AS schicht_startdate
      ,subselect.enddate   AS schicht_enddate
FROM datenbank
INNER JOIN (
            SELECT datenbank.startdate
                  ,datenbank.enddate
                  ,datenbank.resourcename
            FROM datenbank
            WHERE CAST(datenbank.startdate AS date) >= '2016-05-10'
                  AND datenbank.subject = 'Frühdienst Betrieb' 
                  AND datenbank.abteilung = 'Betriebsteam'
            -- der GROUP BY dient hier dazu, die doppelten Einträge zu eleminieren
            GROUP BY datenbank.startdate
                    ,datenbank.enddate
                    ,datenbank.resourcename
           ) AS subselect
     ON CAST(datenbank.startdate AS date) BETWEEN CAST(subselect.startdate AS date) AND CAST(subselect.enddate AS date)
        AND datenbank.resourcename = subselect.resourcename
WHERE datenbank.subject='Anwesend' AND datenbank.abteilung='Betriebsteam'
ORDER BY datenbank.startdate DESC, datenbank.description DESC, datenbank.resourcename ASC
;
 
Tausend Dank xmarsx. Kann die gar nicht genug danken.
Habe es ein bisschen auf mich angepasst und genau das bekommen, was ich suchte.
Da sich die Kollegen durch Raucherpausen und Mittagspausen gelegentlich ausstechen und wieder einstechen, würde ich das ganze noch zusammenfassen, so dass immer nur die erste eingestochene Zeit und die letzte ausgestochene Zeit für jeden Tag bzw. Mitarbeiter angezeigt wird.
Aktuell sieht es wie folgt aus:
Code:
2016-05-10	07:45:17	10:26:02	JM	08:00:00	16:30:00	20
2016-05-10	10:32:09	12:16:18	JM	08:00:00	16:30:00	20
2016-05-10	13:16:25	17:08:50	JM	08:00:00	16:30:00	20
2016-05-11	07:54:24	12:12:30	JM	08:00:00	16:30:00	20
2016-05-11	12:39:58	17:06:05	JM	08:00:00	16:30:00	20
2016-05-12	08:01:54	12:10:56	JM	08:00:00	16:30:00	20
2016-05-12	12:56:54	15:54:01	JM	08:00:00	16:30:00	20
2016-05-13	08:43:41	10:50:44	JM	08:00:00	16:30:00	20
2016-05-13	10:55:28	12:04:56	JM	08:00:00	16:30:00	20
2016-05-13	12:49:16	17:35:06	JM	08:00:00	16:30:00	20
2016-05-23	07:45:44	12:02:50	JM	08:00:00	16:30:00	22
2016-05-23	12:30:25	16:52:36	JM	08:00:00	16:30:00	22
2016-05-24	07:45:43	11:34:40	JM	08:00:00	16:30:00	22
2016-05-24	12:18:22	17:09:33	JM	08:00:00	16:30:00	22

Mann muss es über "Group by" machen, und sicherlich noch Min und max oder so, aber was ich da jetzt Group muss bzw. was mit Aggregatsfunktionen versehen werden muss is die Frage.

Meine Abfrage aktuell dazu sieht wie folgt aus:
Code:
Select CAST(Datenbank.startdate AS date) AS Datum,
	CAST(Datenbank.startdate AS time(0)) AS Arbeitsbeginn,
	CAST(Datenbank.enddate AS time(0)) AS Arbeitsende,
	Datenbank.resourcename,
	CAST(subselect.startdate as time(0)) AS Schichtbeginn,
	cast(subselect.enddate as time(0))  AS Schichtende,
	datepart (wk, (CAST(Datenbank.startdate AS date))) as Kalenderwoche
From Datenbank
  INNER JOIN (
	SELECT Datenbank.startdate,
           Datenbank.enddate,
           Datenbank.resourcename
	FROM Datenbank
	WHERE Datenbank.subject like 'Frühdienst B%' 
            GROUP BY Datenbank.startdate,
                     Datenbank.enddate,
                     Datenbank.resourcename
           ) AS subselect
ON CAST(Datenbank.startdate AS date) BETWEEN CAST(subselect.startdate AS date) AND CAST(subselect.enddate AS date)
AND Datenbank.resourcename = subselect.resourcename
Where (Datenbank.subject = 'anwesend' And
  Datenbank.abteilung = 'Betriebsteam')
  ORDER BY Datenbank.startdate

Oder muss das über eine Case-Abfrage erfolgen.
Danke schonmal
 
Zuletzt bearbeitet:
Pro Mitarbeiter und Tag soll also die erste Zeit (MIN(startdate)) und dann die MAX(enddate) ausgegeben werden. Sollte eigentlich nicht so wild sein. Ich werfe heute Abend vielleicht noch mal einen Blick darauf....wenn ich es nicht vergesse.
Die Arbeit ruft (Renovierung).
 
Habe den Fussboden aus dem Bad gestemmt...da war bei dem Krach nix mit einschlafen. ;)
Sorry, die Woche war anstrengend, sonst hätte ich es schon eher gepostet.

Die Lösung für die Gruppierung pro Mitarbeiter und Tag, um die erste Uhrzeit und die letzte Uhrzeit zu ermitteln lautet "Window Function". Der PARTITION BY ist im Grunde ein GROUP BY auf einer Teilmenge der Datenzeilen. Es wird quasi ein Virtuelles Fenster/Quadrat um die entsprechenden Zeilen und Spalten gezogen, die zu berücksichtigen sind. Der Rest wird durch die Sortierung passend gemacht.
Dann zieht man mittels FIRST_VALUE(spalte) den Wert aus der ersten so zurechtgebastelten Gruppe.
Das Ganze macht man einmal mit startdate und einmal mit enddate.
Fertig!

Es folgt die Query, außerdem habe ich das Beispiel wieder in SQL Fiddle bereitgestellt.
http://sqlfiddle.com/#!17/72444/38

Hier der SQL Code (sollte auch mit MS SQL weitgehend so funktionieren):
Code:
--
-- Zeige mit die Mitarbeiterzeiten für die definierten Schichten
-- (mit dem dort aufgeführten Mitarbeiter) an.
-- Die erste Zeitbuchung pro Tag (erste_zeit) sowie die derzeit
-- letzte Zeitbuchung des Tages wird über eine "Window Function"
-- ermittelt, die eine virtuelle Gruppe innerhalb der Datensätze
-- bildet. Dies ist ähnlich einem GROUP BY.
--
SELECT datenbank.resourcename
      ,CAST(datenbank.startdate AS date) AS arbeitsstart
      ,CAST(datenbank.enddate   AS date) AS arbeitsende
      ,FIRST_VALUE(CAST(datenbank.enddate AS time)) OVER (
        PARTITION BY datenbank.resourcename
                    ,CAST(datenbank.enddate AS date)
                    ,subselect.startdate
                    ,subselect.enddate
        ORDER BY datenbank.enddate DESC
       ) AS letzte_zeit       
      ,FIRST_VALUE(CAST(datenbank.startdate AS time)) OVER (
        PARTITION BY datenbank.resourcename
                    ,CAST(datenbank.startdate AS date)
                    ,subselect.startdate
                    ,subselect.enddate
        ORDER BY datenbank.startdate ASC
       ) AS erste_zeit
      ,datenbank.description
      ,DATE_PART('week', datenbank.startdate) AS kalenderwoche
      ,subselect.startdate AS schichtbegin
      ,subselect.enddate   AS schichtende
FROM datenbank
INNER JOIN (
            SELECT datenbank.startdate
                  ,datenbank.enddate
                  ,datenbank.resourcename
            FROM datenbank
            WHERE CAST(datenbank.startdate AS date) >= '2016-05-10'
                  AND datenbank.subject = 'Frühdienst Betrieb' 
                  AND datenbank.abteilung = 'Betriebsteam'
            -- der GROUP BY dient hier dazu, die doppelten Einträge zu eleminieren
            GROUP BY datenbank.startdate
                    ,datenbank.enddate
                    ,datenbank.resourcename
           ) AS subselect
     ON CAST(datenbank.startdate AS date) BETWEEN CAST(subselect.startdate AS date) AND CAST(subselect.enddate AS date)
        AND datenbank.resourcename = subselect.resourcename
WHERE datenbank.subject='Anwesend' AND datenbank.abteilung='Betriebsteam'
GROUP BY datenbank.resourcename
        ,datenbank.startdate
        ,datenbank.enddate
        ,datenbank.description
        ,subselect.startdate
        ,subselect.enddate
ORDER BY datenbank.startdate DESC
        ,datenbank.description DESC
        ,datenbank.resourcename ASC
;
 
Danke mal wieder xmarsx.
Sieht super aus. Wie mache ich es jetzt noch das mir die doppelten Werte gelöscht werden, wenn ich die Descriptionspalte raus nehme - jeder Tag also nur einmal vorkommt mit Anfangs- und Endzeit.
 
Auch wenn es sich hoffentlich schon erledigt hat...am einfachsten Du nimmst die vorherige Abfrage oben und umgibst diese mit einer neuen SELECT-Abfrage ohne die Spalte description aber mit GROUP BY mit den Spalten wie direkt hinter dem SELECT...fertig (siehe unten).

Das Ergebnis hier wieder in SQLFiddle: http://sqlfiddle.com/#!17/72444/46

Also hier mal komplette die Abfrage (die alte Abfrage aus Beitrag 7 habe ich hier den ALIAS inner_select gegegeben):
Code:
--
-- Zeige mit die Mitarbeiterzeiten für die definierten Schichten (mit dem dort aufgeführten Mitarbeiter) an.
-- Die erste Zeitbuchung pro Tag (erste_zeit) sowie die derzeit letzte Zeitbuchung des Tages
-- wird über eine "Window Function" ermittelt, die eine virtuelle Gruppe innerhalb der Datensätze
-- bildet. Dies ist ähnlich einem GROUP BY.
--
SELECT inner_select.resourcename
      ,inner_select.arbeitsstart
      ,inner_select.arbeitsende
      ,inner_select.letzte_zeit
      ,inner_select.erste_zeit
      ,inner_select.kalenderwoche
      ,inner_select.schichtbegin
      ,inner_select.schichtende
FROM (
SELECT datenbank.resourcename
      ,CAST(datenbank.startdate AS date) AS arbeitsstart
      ,CAST(datenbank.enddate   AS date) AS arbeitsende
      ,FIRST_VALUE(CAST(datenbank.enddate AS time)) OVER (
        PARTITION BY datenbank.resourcename
                    ,CAST(datenbank.enddate AS date)
                    ,subselect.startdate
                    ,subselect.enddate
        ORDER BY datenbank.enddate DESC
       ) AS letzte_zeit       
      ,FIRST_VALUE(CAST(datenbank.startdate AS time)) OVER (
        PARTITION BY datenbank.resourcename
                    ,CAST(datenbank.startdate AS date)
                    ,subselect.startdate
                    ,subselect.enddate
        ORDER BY datenbank.startdate ASC
       ) AS erste_zeit
      ,datenbank.description
      ,DATE_PART('week', datenbank.startdate) AS kalenderwoche
      ,subselect.startdate AS schichtbegin
      ,subselect.enddate   AS schichtende
FROM datenbank
INNER JOIN (
            SELECT datenbank.startdate
                  ,datenbank.enddate
                  ,datenbank.resourcename
            FROM datenbank
            WHERE CAST(datenbank.startdate AS date) >= '2016-05-10'
                  AND datenbank.subject = 'Frühdienst Betrieb' 
                  AND datenbank.abteilung = 'Betriebsteam'
            -- der GROUP BY dient hier dazu, die doppelten Einträge zu eleminieren
            GROUP BY datenbank.startdate
                    ,datenbank.enddate
                    ,datenbank.resourcename
           ) AS subselect
     ON CAST(datenbank.startdate AS date) BETWEEN CAST(subselect.startdate AS date) AND CAST(subselect.enddate AS date)
        AND datenbank.resourcename = subselect.resourcename
WHERE datenbank.subject='Anwesend' AND datenbank.abteilung='Betriebsteam'
GROUP BY datenbank.resourcename
        ,datenbank.startdate
        ,datenbank.enddate
        ,datenbank.description
        ,subselect.startdate
        ,subselect.enddate
ORDER BY datenbank.startdate DESC, datenbank.description DESC, datenbank.resourcename ASC
) AS inner_select
GROUP BY  inner_select.resourcename
         ,inner_select.arbeitsstart
         ,inner_select.arbeitsende
         ,inner_select.letzte_zeit
         ,inner_select.erste_zeit
         ,inner_select.kalenderwoche
         ,inner_select.schichtbegin
         ,inner_select.schichtende
ORDER BY inner_select.arbeitsstart DESC
        ,inner_select.resourcename ASC
;
 
Zuletzt bearbeitet: (zu spät in der Nacht ;))
Zurück
Oben