facebraker
Goto Top

MySQL Schleife in Stored Procedure

Hallo,

ich bin gerade echt am grübeln. Ich habe in meiner DB, bestimmte Zeiträume gespeichert.

z.B.

User_ID ; von Datum ; bisDatum ; AnzTage
1; 2019-10-14;2019-10-27;14
2; 2019-06-03;2019-06-14;12

Ich brächte eine Stored Procedure ,die über die Tage zählt und mir eine Liste der Tage ausgibt:

User_id; Tage; Wert(fest)
1;2019-10-14; X
1;2019-10-15; X
.
.
.
1;2019-10.27;X
...
2;2019-06-03;X
2;2019-06-04;X
.
.
.
2;2019-06-14;X

usw.

Ich hoffe mein Anliegen ist klar, kann mir jemand sagen wie ich das in MySQL umsetzen kann?

Vielen Dank!

Gruß Alex

Content-ID: 431098

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

Ausgedruckt am: 14.11.2024 um 13:11 Uhr

ukulele-7
ukulele-7 21.03.2019 um 20:00:26 Uhr
Goto Top
Das geht , abhängig von der MySQL Version, auch ohne SP und Schleife, Schleifen sind das letzte Mittel. Notwendig dafür ist CTE:
https://dev.mysql.com/doc/refman/8.0/en/with.html

Kannst du keine aktuelle Version nutzen wäre eine Schleife denkbar. Es müsste aber eine temporäre Tabelle oder eine Tabellenvariable genutzt werden.
akretschmer
akretschmer 21.03.2019 um 20:37:55 Uhr
Goto Top
so vielleicht?

[code]
test=*# select * from facebraker ;
id | von | bis
+------------+------------
1 | 2019-10-14 | 2019-10-27
2 | 2019-06-03 | 2019-06-14
(2 rows)

test=*# select f.von, f.bis, x.wert::date from facebraker f left join lateral (select * from generate_series(f.von, f.bis, '1day'::interval) as wert) x on (true);
von | bis | wert
+------------+------------
2019-10-14 | 2019-10-27 | 2019-10-14
2019-10-14 | 2019-10-27 | 2019-10-15
2019-10-14 | 2019-10-27 | 2019-10-16
2019-10-14 | 2019-10-27 | 2019-10-17
2019-10-14 | 2019-10-27 | 2019-10-18
2019-10-14 | 2019-10-27 | 2019-10-19
2019-10-14 | 2019-10-27 | 2019-10-20
2019-10-14 | 2019-10-27 | 2019-10-21
2019-10-14 | 2019-10-27 | 2019-10-22
2019-10-14 | 2019-10-27 | 2019-10-23
2019-10-14 | 2019-10-27 | 2019-10-24
2019-10-14 | 2019-10-27 | 2019-10-25
2019-10-14 | 2019-10-27 | 2019-10-26
2019-10-14 | 2019-10-27 | 2019-10-27
2019-06-03 | 2019-06-14 | 2019-06-03
2019-06-03 | 2019-06-14 | 2019-06-04
2019-06-03 | 2019-06-14 | 2019-06-05
2019-06-03 | 2019-06-14 | 2019-06-06
2019-06-03 | 2019-06-14 | 2019-06-07
2019-06-03 | 2019-06-14 | 2019-06-08
2019-06-03 | 2019-06-14 | 2019-06-09
2019-06-03 | 2019-06-14 | 2019-06-10
2019-06-03 | 2019-06-14 | 2019-06-11
2019-06-03 | 2019-06-14 | 2019-06-12
2019-06-03 | 2019-06-14 | 2019-06-13
2019-06-03 | 2019-06-14 | 2019-06-14
(26 rows)

test=*#
[/code]

allerdings müßtest Du Dein MySQL gegen PostgreSQL austauschen.
em-pie
em-pie 22.03.2019 aktualisiert um 07:26:08 Uhr
Goto Top
<Edit>
vergiss nachfolgendes. Nach nochmaligem lesen willst du ja den umgekehrten Weg -.-
<\edit>

Also wenn ich dich richtig verstanden habe, willst du für einen User das erste und das letzte Datum wissen und dann beide Daten von einander abziehen in die Zahl der Tage zu erhalten?

Warum dann nicht die Tabelle nochmal mit sich selbst joinen, die UserId als Key:

Select a.User, a.MinDate, b.MaxDate, b.MaxDate - a.MinDate as Tage
From
 (select Userid, Min(Datum) as MinDate GROUP by Userid) as a
 Left Join
 (Select Userid, Max(Datum) as MaxDate GROUP by Userid ) as b on a.Userid = b.Userid

Oben die Differenz beider Tage musst du noch mit der richtigen Syntax versehen, da fehlt sicherlich noch ein Convert as date und ein FORMAT, dass dort Tage und kein Datum bei rauskommt...


Gruß
em-pie
facebraker
facebraker 22.03.2019 aktualisiert um 09:00:12 Uhr
Goto Top
@akretschmer

*lach* ... genau , aber ich möchte keine BER haben wo ich mitten in der Arbeit wichtige Komponenten wechsle.
Aber sexy ist die Lösung, behalte ich im Hinterkopf!
facebraker
facebraker 22.03.2019 um 09:05:26 Uhr
Goto Top
@em-pie

Ich glaube es ist noch einfacher.

Ich habe ein Start-Datum, ein End-Datum und für jeden Tag in diesem Zeitraum möchte ich einen Datensatz erzeugen, schau mal in meinen ersten post. da habe ich es versucht aufzuzeigen .

Pseudocode:

Selektiere jeden Datensatz von der Tabelle Zeitraum
Zähle von Anfangdsatum bis Enddatrum , Tageweise
Gib Datum,User_ID, Wert X aus

Nächster Durchlauf
Nächster Datensatz

So würde ich es in einer Skriptsprache machen.

Danke Gruss Alex
ukulele-7
ukulele-7 22.03.2019 um 10:51:17 Uhr
Goto Top
MSSQL (MySQL vielleicht mit der richtigen Version)
in performant:
WITH tabelle(id,von,bis) AS (
	SELECT 1,convert(DATE,'2019-10-14',20),convert(DATE,'2019-10-27',20) UNION ALL  
	SELECT 2,'2019-06-03','2019-06-14'  
	), t(id,von,bis,datum) AS (
	SELECT	tabelle.id,tabelle.von,tabelle.bis,tabelle.von
	FROM	tabelle
	UNION ALL
	SELECT	t.id,t.von,t.bis,dateadd(day,1,t.datum)
	FROM	t
	WHERE	dateadd(day,1,t.datum) <= t.bis
	)
SELECT	t.id,
		t.datum
FROM	t
ORDER BY t.id,t.datum

In langsam und fehleranfällig möchte ich das eigentlich gar nicht schreiben. Es geht aber auch per Cursor oder WHILE-Schleife innerhalb einer Funktion. Die muss dann irgendwo in eine Tabelle schreiben oder eine Tabellenvariable ausspucken.
akretschmer
akretschmer 22.03.2019 um 11:41:23 Uhr
Goto Top
Nun ja, mit MySQL hast Du ja JETZT Dein BER...
facebraker
facebraker 22.03.2019 um 13:53:06 Uhr
Goto Top
@akretschmer da hast du recht aber einen BER, der funktioniert , zwar nicht alle schicken Features hat , aber funktioniert.

Im Ernst das Projekt ist zu groß um im laufenden Betrieb DBMS zu ändern face-wink
facebraker
facebraker 22.03.2019 um 13:54:43 Uhr
Goto Top
@ukulele-7 , ich möchte eigentlich nicht die MySQL Version ändern.

Und mit temporären Tabellen, da kann ich in der Anwendung eine Funktion schreiben, die mir die Tabellen so erzeugt.

Dachte halt, es geht "einfacher" gleich in einer Abfrage.

Danke schön!
ukulele-7
ukulele-7 23.03.2019 um 09:31:19 Uhr
Goto Top
Naja wie es einfach geht (in einer Abfrage) siehst du oben, welche MySQL Version du nutzt hast du noch nicht verraten, vielleicht geht es ja.
akretschmer
akretschmer 23.03.2019 um 11:29:07 Uhr
Goto Top
es ist nie zu spät, eine Fehlentscheidung zu korrigieren. Und: je länger Du wartest, umso höher wird der Aufwand.
ukulele-7
ukulele-7 23.03.2019 um 11:36:51 Uhr
Goto Top
Hier mal mit Funktion, Temp-Tabelle und Tabellen-Variable in der Funktion. Ist auch ein MSSQL Beispiel aber möglicherweise auf MySQL übertragbar.
CREATE TABLE #tabelle(id SMALLINT,von DATE,bis DATE)
INSERT INTO #tabelle
SELECT 1,convert(DATE,'2019-10-14',20),convert(DATE,'2019-10-27',20)  
INSERT INTO #tabelle
SELECT 2,convert(DATE,'2019-06-03',20),convert(DATE,'2019-06-14',20)  

CREATE FUNCTION [dbo].[wusa](@von DATE,@bis DATE)
RETURNS @dates TABLE(datum DATE)
AS
BEGIN
	WHILE @von <= @bis
	BEGIN
		INSERT INTO @dates(datum)
		VALUES(@von)

		SET		@von = dateadd(day,1,@von)
	END

	RETURN
END

SELECT	#tabelle.id,
		f.*
FROM	#tabelle
CROSS APPLY [dbo].[wusa](von,bis) f
akretschmer
akretschmer 23.03.2019 um 11:49:22 Uhr
Goto Top
Nett, @4400667902, aber bei signifikanten Datenmengen wird das von der Performance ein Horror werden ...
ukulele-7
ukulele-7 23.03.2019 um 12:14:16 Uhr
Goto Top
Das besteite ich nicht. Er hat 3 Optionen:

1) Einsatz einer MySQL Version oder einer anderen DB die CTE beherscht und dann Berechnung mit CTE zur Laufzeit.

2) Tabellenwertfunktion und damit Berechnung zur Laufzeit.

3) Trigger oder Script das die Daten generiert, dann eben nicht zur Laufzeit.
akretschmer
akretschmer 23.03.2019 um 13:35:10 Uhr
Goto Top
4. eine DB, die lateral JOIN's und idealerweise auch noch generate_series() kann face-wink
facebraker
facebraker 25.03.2019 um 16:19:33 Uhr
Goto Top
Danke schön an @4400667902 und @akretschmer,

ich nutze eine MySQL 5.5 ...

Die Datenmengen sind nicht so riesig , ich denke mal das würde laufen, ich muss bloss testen wie ich das mit meinen MySQL 5.5 umsetzen kann.
Zur Not muss ich halt die temporäre Tabelle zur Laufzeit per Skript erstellen.

Danke für eure Tipps

Gruß Alex