Komplizierteste SQL-Query ever

Es gibt so ein tolles Zitat:

“Was nicht auf einer einzigen Manuskriptseite zusammengefasst werden kann, ist nicht durchdacht und nicht entscheidungsreif.”

Wenn man das auf meine SQL-Abfrage überträgt, muss ich wohl eingestehen, dass diese Abfrage undurchdacht wäre. Ist sie aber nicht! Ich habe 3 Tage an dieser Abfrage zugebracht, damit sie das Ergebnis ausgibt, welches sie jetzt ausgibt.

Die Abfrage ist für ein Wettkampf, der aus mehreren Einzeldisziplinen besteht, die an unterschiedlichen Tagen veranstaltet werden (zeitgleich und hintereinander). Der Wettkampf ist für Teams. Für jede Einzeldisziplin ist die DisziplinId, TeamId, Platz, Datum, Punktestand vorhanden. Allerdings wird für die Gesamtwertung nicht der Punktestand in der Einzeldisziplin genommen, sondern inder Gesamtwertung werden neue Punkte anhand der Platzierung in der Einzeldisziplin vergeben – z.B. so: Platz 1 – 100 Punkte, Platz 2 – 90 Punkte, usw.

Es klingt schon so natürlich sprachlich kompliziert und ehe ich hier weitere Einzelheiten versuche zu erklären, platziere ich hier einfach die 117 Zeilen lange SQL-Abfrage:

SELECT
	@anzTeam := (SELECT COUNT(*) FROM pentathlon_team WHERE freigeschaltet='1'),
	@seq := @anzTeam + 1,
	@rank := @anzTeam,
	@letzteDatum := (SELECT DISTINCT SUBSTR(MIN(zeitpunkt),1,10) FROM pentathlon_score WHERE pentathlon_credits != 0),
	@letztePunkte := 999999;
SELECT
	gesamt.teamid,
	@seq := IF(@letzteDatum = gesamt.zeitpunkt, @seq - 1, @anzTeam) AS seq, /*Sequenz ...,3,2,1 je Datum*/
	@rank := IF(@letztePunkte = gesamt.punkte, @rank, @seq) AS rank, /*Der Rank eines Teams (wenn gleiche Punktzahl gleicher Rank)*/
	@letztePunkte := gesamt.punkte AS punkte,
	@letzteDatum := gesamt.zeitpunkt AS zeitpunkt
FROM
	(SELECT
		einzelgesamt.teamid,
		SUM(einzelgesamt.punkte) AS punkte,
		einzelgesamt.zeitpunkt AS zeitpunkt
	FROM
		(SELECT DISTINCT
			normale_daten.zeitpunkt,
			normale_daten.teamid,
			normale_daten.disziplinid,
			IF(normale_daten.punkte IS NOT NULL,
				normale_daten.punkte,
				IF((SELECT von FROM pentathlon_disziplin WHERE disziplinid = normale_daten.disziplinid) > normale_daten.zeitpunkt,
					0,	/*Disziplin noch nicht gestartet*/
					(SELECT	DISTINCT /*Disziplin ist beendet: Punkte des letzten Stands*/
						IF (pentathlon_score.rank > 25,
							0, /*Keine Punkte mehr ab Platz 25*/
							pentathlon_punkte.punkte
						) AS punkte
					FROM
						pentathlon_score
						LEFT JOIN
							pentathlon_punkte
							ON
								pentathlon_score.rank = pentathlon_punkte.rank
					WHERE
						pentathlon_score.disziplinid = normale_daten.disziplinid AND
						pentathlon_score.teamid = normale_daten.teamid AND
						pentathlon_score.zeitpunkt = (SELECT DISTINCT
															MAX(zeitpunkt)
														FROM pentathlon_score
														WHERE disziplinid = normale_daten.disziplinid)
					)
				)
			) AS punkte,
			IF(normale_daten.rank IS NOT NULL,
				normale_daten.rank,
				IF((SELECT von FROM pentathlon_disziplin WHERE disziplinid = normale_daten.disziplinid) > normale_daten.zeitpunkt,
					0,	/*Disziplin noch nicht gestartet*/
					(SELECT	DISTINCT /*Disziplin ist beendet: Punkte des letzten Stands*/
						pentathlon_score.rank
					FROM
						pentathlon_score
					WHERE
						pentathlon_score.disziplinid = normale_daten.disziplinid AND
						pentathlon_score.teamid = normale_daten.teamid AND
						pentathlon_score.zeitpunkt = (SELECT DISTINCT
															MAX(zeitpunkt)
														FROM pentathlon_score
														WHERE disziplinid = normale_daten.disziplinid)
					)
				)
			) AS rank
		FROM
			(SELECT DISTINCT
				alle_daten.zeitpunkt,
				alle_daten.teamid,
				alle_daten.disziplinid,
				pentathlon_score.rank,
				IF (pentathlon_score.rank > 25,
					0, /*Keine Punkte mehr ab Platz 25*/
					pentathlon_punkte.punkte
				) AS punkte
				FROM
					(SELECT DISTINCT
						SUBSTR(pentathlon_score.zeitpunkt,1,10) as zeitpunkt,
						pentathlon_team.teamid,
						pentathlon_disziplin.disziplinid,
						pentathlon_teamdisziplin.webrpc
					FROM
						pentathlon_score
						JOIN
							pentathlon_team
						JOIN
							pentathlon_disziplin
						INNER JOIN
							pentathlon_teamdisziplin
							ON pentathlon_disziplin.disziplinid = pentathlon_teamdisziplin.disziplinid
					WHERE
						pentathlon_score.pentathlon_credits != 0 AND
						pentathlon_team.freigeschaltet = '1') AS alle_daten
				LEFT JOIN
					pentathlon_score
						ON
							alle_daten.teamid = pentathlon_score.teamid AND
							alle_daten.disziplinid = pentathlon_score.disziplinid AND
							IF(SUBSTR(alle_daten.webrpc,1,33) = 'http://www.worldcommunitygrid.org',
								alle_daten.zeitpunkt = SUBSTR(pentathlon_score.zeitpunkt,1,10),
									alle_daten.zeitpunkt = DATE_ADD(SUBSTR(pentathlon_score.zeitpunkt,1,10), INTERVAL -1 DAY) AND
									SUBSTR(pentathlon_score.zeitpunkt,12,2)='01'
							)
				LEFT JOIN
					pentathlon_punkte
						ON
							pentathlon_score.rank = pentathlon_punkte.rank) AS normale_daten
	) AS einzelgesamt
	GROUP BY
		einzelgesamt.zeitpunkt,
		einzelgesamt.teamid
	) AS gesamt
ORDER BY
		gesamt.zeitpunkt,
		gesamt.punkte DESC

Natürlich hätte ich das auch alles durch einfache Abfragen lösen können, die ich dann in PHP zusammenfüge, aber in PHP ist das einfügen und sortieren der Daten so ineffizient.

Die @variable sind User-Variablen in MySQL, mit denen man die Platzierung in der Abfrage berechnen kann.

Jetzt würde ich meine Fähigkeiten in SQL mit einer 1,2 benoten.. ;)

Auch interessant:

  1. SQL IN und =
  2. MySQL: UPDATE auf mehreren Tabellen und Subqueries