tsunami
Goto Top

Group y und Werte in ein Array

Hallo,
ich habe ein mysql Problem:
Eine Tabelle mit vielen Datumseinträgen. Sowas wie ein Dienstplan.
2019-02-19|8:00|12:00|Mueller
2019-02-19|12:00|16:00|Schmidt
2019-02-19|16:00|20:00|Meier
2019-02-19|20:00|00:00|Klaus

2019-02-20|8:00|12:00|Schmidt
2019-02-20|12:00|16:00|Meier
2019-02-20|16:00|20:00|Mueller
2019-02-20|20:00|00:00|Klaus

...

Ich möchte nun eine Ausgabe wie:
2019-02-19
08:00-12:00 Müller, 12:00-16:00 Schmidt, 16:00-20:00 Meier, 20:00-0:00 Klaus

2019-02-20
08:00-12:00 Schmidt, 12:00-16:00 Meier, 16:00-20:00 Müller, 20:00-0:00 Klaus
usw.
Mache ich eine normale Abfrage, spuckt er mir 4 Zeilen pro tag raus. Gruppiere ich, schmeisste er mir eine Zeile raus, aber nur den letzten Eintrag von Klaus. Eine Möglichkeit wäre, alles per Sub select in ein mehrdimensionales Array packen, dann ein Foreach durchlaufen lassen, Dann das nächste Datum...

Kriege ich das eleganter hin? Die felder sind id, startzeit, endzeit, kommentar. Es müsste sowas sein wie group by datum und packe startzeiten und endzeiten in ein Array..

Irgendwer eine Idee? Vielen Dank
tsunami

Content-Key: 432480

Url: https://administrator.de/contentid/432480

Ausgedruckt am: 29.03.2024 um 05:03 Uhr

Mitglied: akretschmer
akretschmer 24.03.2019 um 12:35:42 Uhr
Goto Top
Also, die Trennung von Datum, Startzeit und Endzeit ist schon mal Suboptimal. Ideal um sowas abzubilden wären RANGE-Typen, die aber MySQL nicht hat. Ich zeige Dir mal eine Lösung mit einer anderen DB:

test=*# select * from tsunami ;
                    von_bis                    |  name   
-----------------------------------------------+---------
 ["2019-02-19 08:00:00","2019-02-19 12:00:00") | Mueller  
 ["2019-02-19 12:00:00","2019-02-19 16:00:00") | Schmidt  
 ["2019-02-19 16:00:00","2019-02-19 20:00:00") | Meier  
 ["2019-02-19 20:00:00","2019-02-20 00:00:00") | Klaus  
 ["2019-02-20 08:00:00","2019-02-20 12:00:00") | Schmidt  
 ["2019-02-20 12:00:00","2019-02-20 16:00:00") | Meier  
 ["2019-02-20 16:00:00","2019-02-20 20:00:00") | Mueller  
 ["2019-02-20 20:00:00","2019-02-21 00:00:00") | Klaus  
(8 rows)

test=*# select lower(von_bis)::date::text as "Datum", string_agg(extract(hour from lower(von_bis)) || ' - ' || extract(hour from upper(von_bis)) || ': ' || name, ', ') as "Dienstplan" from tsunami group by 1;  
   Datum    |                            Dienstplan                            
------------+------------------------------------------------------------------
 2019-02-19 | 8 - 12: Mueller, 12 - 16: Schmidt, 16 - 20: Meier, 20 - 0: Klaus
 2019-02-20 | 8 - 12: Schmidt, 12 - 16: Meier, 16 - 20: Mueller, 20 - 0: Klaus
(2 rows)

test=*#
Mitglied: ukulele-7
ukulele-7 24.03.2019 um 17:36:11 Uhr
Goto Top
Wenn die Zeit immer die selbe ist tun es auch drei Joins. Wenn es flexibler sein soll ist die MySQL Version entscheidend, Schleifen sind das schlechteste was du tun kannst.
Mitglied: akretschmer
akretschmer 24.03.2019 um 18:02:21 Uhr
Goto Top
das sollte sogar mit den begrenzten Möglichkeiten von MySQL ähnlich wie meine Lösung funktionieren. IIRC gibt es da auch eine String-Aggregation, dazu passend den String zusammenbasteln und über das Datum aggregieren. Schleifen sollte man prinzipiell vermeiden, diese führen bei wachsenden Datenmengen sehr schnell zu extremen Reaktionszeiten.
Mitglied: tsunami
tsunami 25.03.2019 um 10:00:45 Uhr
Goto Top
Hallo,
erstmal vielen Dank. Die Zeiten sind nicht immer gleich. Eine Spalte für von bis, ist eine Idee. Mysql 5.12.
Ungewöhnlich, normal sind doch mehr Spalten immer besser. Ich teste mal...

Gruß
tsunami
Mitglied: akretschmer
akretschmer 25.03.2019 um 10:57:26 Uhr
Goto Top
Zitat von @tsunami:

Ungewöhnlich, normal sind doch mehr Spalten immer besser. Ich teste mal...


Wo hast Du das gelernt?

Da die Zeiten nicht immer gleich sind, gehören Datum und Zeit zusammen und bilden einen Timestamp. Da es außerdem noch um Zeitbereiche geht (von, bis) wäre die Angabe als RANGE noch besser (wie von mir gezeigt), nur kann MySQL das natürlich nicht. Mittels RANGE könntest Du hier sogar einen Constraint setzen, daß sich niemals die Schichten von 2 oder mehr Leuten überschneiden. Das mag hier vielleicht keinen Sinn machen, aber z.B. bei einem Hotel wäre es peinlich, wenn ein und dasselbe Zimmer doppelt belegt wird. Das geht sehr einfach via RANGE-Typen zu vermeiden.
Mitglied: tsunami
tsunami 25.03.2019 um 11:49:24 Uhr
Goto Top
20 Fehler....

Habe ich da was mißverstanden? Der aufgezeigte Ansatz galt schon für mysql? Oder war das ein Bsp für eine viel vessere Datenbank...
20 Fehler wurden während der Analyse gefunden.

Variablenname wurde erwartet. (near ":" at position 22)  
Variablenname wurde erwartet. (near ":" at position 28)  
Unerkanntes Schlüsselwort. (near "text" at position 29)  
Unerkanntes Schlüsselwort. (near "as" at position 34)  
Unerwartetes Zeichen. (near ""Datum"" at position 37)  
Unerwartetes Zeichen. (near "," at position 44)  
Unerwartetes Zeichen. (near "string_agg" at position 46)  
Unerwartetes Zeichen. (near "(" at position 56)  
Unerkanntes Schlüsselwort. (near "extract" at position 57)  
Unerwartetes Zeichen. (near "(" at position 64)  
Unerkanntes Schlüsselwort. (near "hour" at position 65)  
Unerwartetes Zeichen. (near ")" at position 89)  
Unerwartetes Zeichen. (near "||" at position 91)  
Unerwartetes Zeichen. (near "' - '" at position 94)  
Unerwartetes Zeichen. (near "||" at position 100)  
Unerkanntes Schlüsselwort. (near "extract" at position 103)  
Unerwartetes Zeichen. (near "(" at position 110)  
Unerkanntes Schlüsselwort. (near "hour" at position 111)  
Diese Art von Klausel wurde zuvor analysiert. (near "from" at position 116)  
Unerkannte Statement-Typ. (near "from" at position 116)  
Mitglied: akretschmer
akretschmer 25.03.2019 um 12:06:26 Uhr
Goto Top
Da ich in Zeile 9 ein "string_agg" sehe vermute ich mal, Du hast ohne zu lesen, was ich schrieb, einfach per Copy&Paste mein gezeigtes SQL übernommen. Ja, das ist für eine andere, bessere Datenbank bestimmt. Schrieb ich auch.

Was Du machen solltest: Dir das Grundprinzip anschauen.

Übrigens: wenn Du schreibst: "Gruppiere ich, schmeisste er mir eine Zeile raus, aber nur den letzten Eintrag von Klaus. " dann ist das eine relativ wertlose Information hier, weil keiner weiß, was Du *genau* versucht hast. MySQL führt syntaktisch falsche Abfragen mit Gruppierung aus und liefert ein falsches Resultat. Korrekt wäre eine Fehlermeldung. Keine Ahnung, was für komisches Kraut die da rauchen ...
Mitglied: tsunami
tsunami 28.03.2019 um 16:00:27 Uhr
Goto Top
Guten Tag,
ich bin nun mittels group_concat etwas weiter gekommen:
select DATE_FORMAT(startzeit, '%Y-%m-%d') AS datumsplit,group_concat(DATE_FORMAT(startzeit,'%H:%i')) as startzeit,group_concat(date_format(endzeit,'%H:%i')) as endzeit, kategorien.* from taetigkeiten,kategorien where taetigkeiten.taetigkeit_id=kategorien.id group by datumsplit  

Das gibt im Grunde genau das was ich haben möchte. Nun kommt das "Aber". Die Werte startzeit und endzeit sind durcheinander.
Normalerweise müsste ich doch sowas haben wie (startzeit):
8:00
8:15
10:00
14:00
14:30
...
Aber er wirft mir raus:
08:00,
18:15,
17:45,
16:45,
16:15,
15:45,
15:15,
12:15,
11...

Also ist der erste Wert korrekt,danach scheint er rückwärts aus zu geben. 18:15 ist der letzte Wert (letzte Startzeit) vom 01.02.2019 und 8:00 die erste..
Mitglied: akretschmer
akretschmer 28.03.2019 um 17:47:10 Uhr
Goto Top
das SQL ist syntaktisch und logisch falsch, nur merkt MySQL das nicht und liefert Murks. Abgesehen davon, kann group_concat sortieren?
Mitglied: TheJoker2305
TheJoker2305 24.04.2019 um 14:35:43 Uhr
Goto Top
Hallo zusammen,
wieviele Zeitfenster gibt es denn? Sind es immer glatte Zeiten?
Dann würde ich so vorgehen:

Tabelle:
datum schicht mitarbeiter
2019-02-19 08:00-12:00 Mueller
2019-02-19 12:00-16:00 Schmidt
2019-02-19 16:00-20:00 Meier
2019-02-20 08:00-12:00 Schmidt
2019-02-20 12:00-16:00 Meier
2019-02-20 16:00-20:00 Mueller
2019-02-20 20:00-00:00 Klaus
2019-02-19 20:00-00:00 Klaus

Abfrage:
select 
	distinct s.datum, 
	m1.mitarbeiter as '08:00-12:00',   
	m2.mitarbeiter as '12:00-16:00',    
	m3.mitarbeiter as '16:00-20:00',  
	m4.mitarbeiter as '20:00-00:00'  
from 
	dbo.schichten s
left join
	dbo.schichten m1 on s.datum = m1.datum and m1.schicht = '08:00-12:00'  
left join
	dbo.schichten m2 on s.datum = m2.datum and m2.schicht = '12:00-16:00'  
left join
	dbo.schichten m3 on s.datum = m3.datum and m3.schicht = '16:00-20:00'  
left join
	dbo.schichten m4 on s.datum = m4.datum and m4.schicht = '20:00-00:00'  

order by s.datum

Ergebnis:
datum 08:00-12:00 12:00-16:00 16:00-20:00 20:00-00:00
2019-02-19 Mueller Schmidt Meier Klaus
2019-02-20 Schmidt Meier Mueller Klaus


Liste der Schichten kann dann natürlich erweitern werden.
Mit MS SQL getestet, sollte auch mit MySQL gehen.

Gruß thejoker230580
Mitglied: TheJoker2305
TheJoker2305 24.04.2019 um 14:45:24 Uhr
Goto Top
Oder so:
select 
	distinct s.datum, 
	m1.mitarbeiter + ' '+'08:00-12:00' + ' '+     
	m2.mitarbeiter + ' '+ '12:00-16:00' + ' '+    
	m3.mitarbeiter + ' '+ '16:00-20:00' + ' '+  
	m4.mitarbeiter + ' '+ '20:00-00:00' as Schichtplan  
from 
	dbo.schichten s
left join
	dbo.schichten m1 on s.datum = m1.datum and m1.schicht = '08:00-12:00'  
left join
	dbo.schichten m2 on s.datum = m2.datum and m2.schicht = '12:00-16:00'  
left join
	dbo.schichten m3 on s.datum = m3.datum and m3.schicht = '16:00-20:00'  
left join
	dbo.schichten m4 on s.datum = m4.datum and m4.schicht = '20:00-00:00'  

order by s.datum

select * 
  FROM dbo.schichten

Ergebnis:

datum Schichtplan
2019-02-19 Mueller 08:00-12:00 Schmidt 12:00-16:00 Meier 16:00-20:00 Klaus 20:00-00:00
2019-02-20 Schmidt 08:00-12:00 Meier 12:00-16:00 Mueller 16:00-20:00 Klaus 20:00-00:00