Schlagwort-Archive: PHP

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:
[code lang=’sql‘]
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
[/code]
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.. ;)

PHP: Foreach Variablen Typ definieren

Ja, die Welt ist leider nicht so ganz einfach. Nachdem ich in diesem Beitrag über die Typisierung von Klassenattributen etwas geschrieben habe, dachte ich, dass man Variablen in der foreach-Schleife ebenfalls so typisieren kann. Felix‘ IDE kann das wohl auch mit folgender Methode
[code lang=’php‘]foreach($this->kunden as $kundeData)
{
/**
@var Kunde
*/
$kunde = $kundeData[‚kunde‘];
[…]
}
[/code]

NetBeans kriegt es allerdings so nicht hin, dort muss man diesen Code verwenden:
[code lang=’php‘]foreach($this->kunden as $kundeData)
{
/* @var $kunde Kunde */
$kunde = $kundeData[‚kunde‘];
[…]
}
[/code]
Und zwar genau mit oben angegebenen Format. Sobald eine Abweichung drin ist (ein Slash zu viel), geht’s schon nicht mehr. Sehr entwicklerfreundlich! :)

[via NetBeans Blog]

Frage an die PHP OOP-Experten: Typisierung von Klassenattributen

Die Überschrift hört sich komplizierter an als sie ist. Veranschaulichen wir das Problem mal an einem Beispiel. Ich habe eine Klasse Erde, Mars, Venus etc. Diese Klassen sehen so aus:
[code lang=’php‘]
class Erde{
var $name;
var $groesse;
var $avgEntfernungZurSonne;
var $bevoelkerung;

function __construct(…) {

}

function eineFkt(…) {

}
}
[/code]

Nun habe ich noch eine Klasse Sonnensystem. Und diese Klasse soll als Attribute die einzelnen Planeten beinhalten, also:
[code lang=’php‘]
class Sonnensystem{
var $erde;
var $venus;
var $mars;

function __construct($erde, $venus,…) {
$this->erde = $erde;
$this->venus = $venus;

}

function gebReihenfolgeDerPlanetenAbSonne() {
$arrayEntfernung =
($this->erde->name => $this->erde->avgEntfernungZurSonne,
$this->venus->name => $this->venus->avgEntfernungZurSonne,
…) ;

}
}
[/code]
Das Problem ist jetzt, dass NetBeans in Sonnensystem::gebReihenfolgeDerPlanetenAbSonne() nicht weiß, von welchem Typ die Variablen $this->erde und $this->venus sind. Denn die werden ja nirgendwo genauer in der Klasse Sonnensystem spezifiziert. Folglich kann mir NetBeans auch nicht vorschlagen, welche Attribute/Methoden ich bei $this->erde aufrufen möchte. Das finde ich allerdings extrem schwach, denn genau deswegen setze ich ja ein IDE ein.

Mir sind zwar bei Recherchen Schlagwörter wie z.B. Typehinting über den Weg gelaufen, aber das könnte ich ja nur im Konstruktor einsetzen und dann wäre nur dort sicher gestellt, dass $erde eine Instanz der Klasse Erde ist und hilft folglich nicht wirklich weiter.

Also, mal konkretisiert: Wie kann ich in PHP festlegen, dass ein Attribut von einem bestimmten Typ ist. So zumindest nicht:
[code lang=’php‘]
class Sonnensystem{
var Erde $erde;

[/code]

Update + Update + Update + Update
Damit die IDE NetBeans weiß, von welchem Typ ein Klassen-Attribut ist, muss man PHPDoc-Kommentare verwenden:
[code lang=’php‘]
class Sonnensystem{
/**
* @var Erde
**/
private $erde;

[/code]

Felix schlug dann noch vor, das Attribut auf „private“ zu setzen und dann im Konstruktor mit TypeHinting zu arbeiten. Das funktionierte bei mir in NetBeans allerdings nicht. Da scheint es also Unterschiede zwischen den IDEs zu geben.