User Tools

Site Tools


mysqlintersectperiodesold

Alte Version

Nun, diese Version ist eigentlich schon richtig. Aber viel zu kompliziert. Aber da ich mir soviel Mühe gab wollte ich nicht einfach alles löschen

SELECT
	p1.*,
	p2.*
FROM
	tbl_periode_1 AS p1,
	tbl_periode_2 AS p2
WHERE
	-- Start von P2 ist innerhalb von p1
	(p2.start_2 BETWEEN p1.start_1 AND p1.end_1)
	-- Ende von P2 ist innerhalb von p1
	OR (p2.end_2 BETWEEN p1.start_1 AND p1.end_1)
	-- p2 ist Grösser als p1 und p2 beginnt vor und endet nach p1
	OR (p2.start_2 <= p1.start_1 AND p1.end_1 <= p2.end_2)
ORDER BY 
	p2.id_2,
	p1.id_1

Formel

Ich verwende dazu die folgende Logik

[Länge von P1] = [Ende 1] - [Start 1] + 1

[Differenz der Starts] = Wenn([Start 2] nach [Start 1] dann ([Start 2] - [Start 1])) sonst 0

[Differenz der Enden] = Wenn([Ende 2] vor [Ende 1] dann ([Ende 1] - [Ende 2])) sonst 0

[Anzahl Tage] = [Länge von P1] - [Differenz der Starts] - [Differenz der Enden]

[Anzahl Tage] = [Anzahl Tage] grösser als 0 dann [Anzahl Tage] sonst 0

Grafische Darstellung zum Verständnis

um die Logik etwas grafisch darzustellen, hier eine Tabelle mit den Fällen. In den Zeilen [Differenzen] sieht man die Umsetzung der Formeln [Differenz der Starts] und [Differenz der Enden]. In der Spalte [Days] dementsprechend die Umsetzung der Formeln für [Anzahl Tage]

? Überschneidungen der Perioden
[
(x:7) @@Tage@@
= ={width: 70px} 1 ={width: 70px} 2 ={width: 70px} 3 ={width: 70px} 4 ={width: 70px} 5 ={width: 70px} 6 ={width: 70px} 7 = days
#
= Periode 1 (x:3){background-color:red; border-left-width:thick; border-left-color:black; border-right-width:thick; border-right-color:black;} 3
=(x:9) Periode 2 ist innerhalb der Periode 1
Periode 2 {border-left-width:thick; border-left-color:black;} {background-color:yellow;} {border-left-width:thick; border-left-color:black;}
Differenzen {background-color:grey; border-left-width:thick; border-left-color:black;} 1 {background-color:grey;} 1 {border-left-width:thick; border-left-color:black;} 3-1-1
Schnittperiode {border-left-width:thick; border-left-color:black;} {background-color:green;} {border-left-width:thick; border-left-color:black;} 1
=(x:9) Periode 2 beginnt vor und endet in der Periode
Periode 2 {background-color:yellow;} (x:2){background-color:yellow; border-left-width:thick; border-left-color:black;} {border-left-width:thick; border-left-color:black;}
Differenzen {border-left-width:thick; border-left-color:black;} {background-color:grey;} 1 {border-left-width:thick; border-left-color:black;} 3-1
Schnittperiode (x:2){background-color:green; border-left-width:thick; border-left-color:black;} {border-left-width:thick; border-left-color:black;} 2
=(x:9) Periode 2 beginnt innerhalb und endet nach der Periode 1
Periode 2 {border-left-width:thick; border-left-color:black;}(x:2){background-color:yellow; border-right-width:thick; border-right-color:black;} {background-color:yellow;}
Differenzen {background-color:grey; border-left-width:thick; border-left-color:black;} 1 {border-left-width:thick; border-left-color:black;} 3-1
Schnittperiode {border-left-width:thick; border-left-color:black;} (x:2){background-color:green;} {border-left-width:thick; border-left-color:black;} 2
=(x:9) Periode 2 beginnt vor und endet nach der Periode 1
Periode 2 {background-color:yellow;} (x:3){background-color:yellow; border-left-width:thick; border-left-color:black; border-right-width:thick; border-right-color:black;} {background-color:yellow;}
Differenzen {border-left-width:thick; border-left-color:black;} {border-left-width:thick; border-left-color:black;} 3
Schnittperiode (x:3){background-color:green; border-left-width:thick; border-left-color:black;} {border-left-width:thick; border-left-color:black;} 3
=(x:9) Periode 2 ist vor der Periode 1
Periode 2 {background-color:yellow;} {border-left-width:thick; border-left-color:black;} {border-left-width:thick; border-left-color:black;}
Differenzen {background-color:grey;} 4 (x:3){background-color:grey; border-left-width:thick; border-left-color:black;} {border-left-width:thick; border-left-color:black;} 3-4
Schnittperiode {border-left-width:thick; border-left-color:black;} {border-left-width:thick; border-left-color:black;} 0
=(x:9) Periode 2 ist nach der Periode 1
Periode 2 {border-left-width:thick; border-left-color:black;} {border-left-width:thick; border-left-color:black;} {background-color:yellow;}
Differenzen (x:3){background-color:grey; border-left-width:thick; border-left-color:black; border-right-width:thick; border-right-color:black;} 4 {background-color:grey;} 3-4
Schnittperiode {border-left-width:thick; border-left-color:black;} {border-left-width:thick; border-left-color:black;} 0

SQL Statement

Und in SQL umgesetzt, könnte das etwa so aussehen

SELECT
	id_1,
	start_1,
	end_1,
	id_2,
	start_2,
	end_2,
	days,
	start_date,
	end_date
FROM
	(
		-- Die eigentliche Berechnung:
		SELECT
			-- Beide Perioden auslesen
			p1.id_1,
			p1.start_1,
			p1.end_1,
			p2.id_2,
			p2.start_2,
			p2.end_2,	
 
			-- Länge der Periode 1 berechnen.
			-- [[Länge von P1] = [Ende 1] - [Start 1] + 1
			@p1_length 	:= DATEDIFF(p1.end_1, p1.start_1) + 1 AS p1_length,
 
			-- Nur die positiven Differenzen zwieschen den Starts übernehmen
			-- [Differenz der Starts] = Wenn([Start 2] nach [Start 1] dann ([Start 2] - [Start 1])) sonst 0
			@diff_start := GREATEST(DATEDIFF(p2.start_2, p1.start_1), 0) AS diff_start,
 
			-- Nur die positiven Differenzen zwieschen den Enden übernehmen
			-- [[Differenz der Enden] = Wenn([Ende 2] vor [Ende 1] dann ([Ende 1] - [Ende 2])) sonst 0
			@diff_end 	:= GREATEST(DATEDIFF(p1.end_1,   p2.end_2)  , 0) AS diff_end,
 
			-- übereinstimmende Länge berechnen (uns interessieren nur positive Längen):
			-- [Anzahl Tage] = [Länge von P1] - [Differenz der Starts] - [Differenz der Enden]
			-- [Anzahl Tage] = [Anzahl Tage] grösser als 0 dann [Anzahl Tage] sonst 0
			@days := GREATEST((@p1_length - @diff_start - @diff_end), 0) AS days,
 
			-- Start und Endzeit von der Resultat-Periode berechnen
			IF(@days > 0, GREATEST(p2.start_2, p1.start_1)	, NULL) AS start_date,
			IF(@days > 0, LEAST(p2.end_2, p1.end_1)			, NULL) AS end_date
		FROM
			tbl_periode_1 AS p1,
			tbl_periode_2 AS p2
	) AS all_data
WHERE
	days > 0
ORDER BY
	id_1,
	id_2;

Resultat

Und das ist das Resultat mit unseren Beispieldaten

? Schnittperioden
(x:3) Periode 1 (x:3) Periode 2 (x:3) Schnittperiode
= id_1 = start_1 = end_1 = id_2 = start_2 = end_2 = days = start_date = end_date
1 2012-02-01 2012-02-28 2 2012-02-15 2012-02-15 1 2012-02-15 2012-02-15
1 2012-02-01 2012-02-28 3 2012-01-01 2012-02-02 2 2012-02-01 2012-02-02
1 2012-02-01 2012-02-28 4 2012-01-01 2012-12-31 28 2012-02-01 2012-02-28
2 2012-02-15 2012-03-15 2 2012-02-15 2012-02-15 1 2012-02-15 2012-02-15
2 2012-02-15 2012-03-15 4 2012-01-01 2012-12-31 30 2012-02-15 2012-03-15
3 2012-04-01 2012-04-15 1 2012-03-20 2012-04-02 2 2012-04-01 2012-04-02
3 2012-04-01 2012-04-15 4 2012-01-01 2012-12-31 15 2012-04-01 2012-04-15

SQL Statement ohne Variablen

Hier noch eine Version ohne Variablen. Diese braucht man, wenn man eine View anlegen will. Auch ist diese Version sehr nahe an anderen Datenbanken als MySQL, so dass das Konzept auch auf anderen Plattformen etwa so funktionieren kann

SELECT
	id_1,
	start_1,
	end_1,
	id_2,
	start_2,
	end_2,
	-- übereinstimmende Länge berechnen (uns interessieren nur positive Längen):
	-- [Anzahl Tage] = [Länge von P1] - [Differenz der Starts] - [Differenz der Enden]
	-- [Anzahl Tage] = [Anzahl Tage] grösser als 0 dann [Anzahl Tage] sonst 0
	GREATEST((p1_length - diff_start - diff_end), 0) AS days,
 
	CASE 
		WHEN end_date >= start_date
		THEN start_date
		ELSE NULL
	END AS start_date,
	CASE 
		WHEN end_date >= start_date
		THEN end_date
		ELSE NULL
	END AS end_date
 
FROM
	(
		SELECT
			-- Beide Perioden auslesen
			p1.id_1,
			p1.start_1,
			p1.end_1,
			p2.id_2,
			p2.start_2,
			p2.end_2,	
 
			-- Länge der Periode 1 berechnen.
			-- [[Länge von P1] = [Ende 1] - [Start 1] + 1
			DATEDIFF(p1.end_1, p1.start_1) + 1 AS p1_length,
 
			-- Nur die positiven Differenzen zwieschen den Starts übernehmen
			-- [Differenz der Starts] = Wenn([Start 2] nach [Start 1] dann ([Start 2] - [Start 1])) sonst 0
			GREATEST(DATEDIFF(p2.start_2, p1.start_1), 0) AS diff_start,
 
			-- Nur die positiven Differenzen zwieschen den Enden übernehmen
			-- [[Differenz der Enden] = Wenn([Ende 2] vor [Ende 1] dann ([Ende 1] - [Ende 2])) sonst 0
			GREATEST(DATEDIFF(p1.end_1,   p2.end_2)  , 0) AS diff_end,
 
			-- Start und Endzeit von der Resultat-Periode berechnen
			GREATEST(start_2, start_1) AS start_date,
			LEAST(end_2, end_1) AS end_date
		FROM
			tbl_periode_1 AS p1,
			tbl_periode_2 AS p2
	) AS prepared_data
ORDER BY
	id_1,
	id_2;
mysqlintersectperiodesold.txt · Last modified: 09.12.2013 09:39:54 (external edit)