Vinzenz Mai: MySQL und korreliertes Subselect, berechnete Spalten.

Beitrag lesen

Hallo Bodo,

Gut, ich habe mir jetzt mal Gedanken gemacht, wie ich

  1. das Beispiel vereinfachen, dabei
  2. von der eigentlich zu lösenden Aufgabe entkoppeln, und dennoch
  3. das eigentlich zu lösende Problem beibehalten
    kann.

ok, an Deinem Beispiel kann ich Dir weiterhelfen.

Das folgende in sich vollständige Beispiel zeigt, was ich prinzipiell haben will an einem konkreten Beispiel. Insbesondere zeigt es die in meinem zweiten Post genannte Kuriosität mit dem ORDER BY:

Nicht kurios, schlicht und einfach erwartungsgemäß. Du scheiterst immer noch an der Schlampigkeit von MySQL.

CREATE TABLE tabelle (n CHAR(1), s TINYINT, w DOUBLE);

INSERT INTO tabelle (n, s, w) VALUES
("a",1,3),("b",1,5),("c",0,2),("d",0,7),("e",0,4),("f",0,8);[code]

OK, eine Tabelle mit 6 Zeilen. Bis dahin ist noch alles richtig.

[code lang=sql]SELECT t1.n AS n1, t2.n AS n2, (t1.w-t2.w)(t1.w-t2.w) AS d
FROM tabelle AS t1, tabelle AS t2
HAVING d = (
SELECT MIN((t1.w-t3.w)
(t1.w-t3.w))
FROM tabelle AS t3 WHERE t3.s=1
);

  
Nein, so nicht. Das Ergebnis ist leider mal wieder Zufall. Es sieht richtig aus, aber auch nicht mehr. Jedes andere mir bekannte Datenbankmanagementsystem weist Deine SQL-Statement als syntaktisch fehlerhaft zurück.  
  
Du verwendest HAVING. Damit sieht es so aus, als könntest Du die berechnete Spalte d mit dem Spaltenalias zum Eingrenzen der Ergebnismenge benutzen. In Wirklichkeit sollte da eine WHERE-Klausel stehen. Nur ist es in dieser "dummerweise" nicht möglich, Spalten-Aliasnamen zu verwenden, wie ich heute [schon einmal](https://forum.selfhtml.org/?t=148901&m=966551) erwähnt habe.  
  
Nun zu den Konsequenzen von HAVING:  
[HAVING](http://dev.mysql.com/doc/refman/5.0/en/select.html#id3305657) impliziert, dass alle Spalten der Abfrage entweder eine Aggregatsfunktion aufweisen oder gruppiert werden. Du tust das nicht, somit sind die Werte schlicht und einfach mal wieder zufällig.  
  

> // Liefert wie erwartet:  
> // +------+------+------+  
> // | n1   | n2   | d    |  
> // +------+------+------+  
> // | a    | a    |    0 |  
> // | c    | a    |    1 |  
> // | e    | a    |    1 |  
> // | b    | b    |    0 |  
> // | d    | b    |    4 |  
> // | e    | b    |    1 |  
> // | f    | b    |    9 |  
> // +------+------+------+  
  
Dieses Ergebnis erreichst Du korrekt über folgendes Statement:  
  
~~~sql
SELECT  
    t1.n AS n1,  
    t2.n AS n2,  
    (t1.w-t2.w)*(t1.w-t2.w) AS d  
FROM  
    tabelle t1,    -- nicht jeder SQL-Dialekt erlaubt AS für Tabellen-Aliasnamen  
    tabelle t2     -- deswegen lasse ich AS prinzipiell weg :-)  
WHERE (t1.w-t2.w)*(t1.w-t2.w) = (  -- hier muss der gleiche Ausdruck hin, mit dem d berechnet wird  
    SELECT  
        MIN((t1.w-t3.w)*(t1.w-t3.w))  
    FROM tabelle t3  
    WHERE t3.s=1  
)

Nun ist es auch ganz einfach, richtig zu sortieren. Dabei ist zu beachten, dass Du in der ORDER-BY-Klausel einen Spaltenalias verwenden darfst:

SELECT  
    t1.n AS n1,  
    t2.n AS n2,  
    (t1.w-t2.w)*(t1.w-t2.w) AS d  
FROM  
    tabelle t1,    -- nicht jeder SQL-Dialekt erlaubt AS für Tabellen-Aliasnamen  
    tabelle t2     -- deswegen lasse ich AS prinzipiell weg :-)  
WHERE (t1.w-t2.w)*(t1.w-t2.w) = (  -- hier muss der gleiche Ausdruck hin, mit dem d berechnet wird  
    SELECT         -- korrelierte Unterabfrage  
        MIN((t1.w-t3.w)*(t1.w-t3.w))  
    FROM tabelle t3  
    WHERE t3.s=1  
)  
ORDER BY d

liefert das von Dir gewünschte Ergebnis:

+------+------+------+
| n1   | n2   | d    |
+------+------+------+
| a    | a    |    0 |
| b    | b    |    0 |
| c    | a    |    1 |
| e    | a    |    1 |
| e    | b    |    1 |
| d    | b    |    4 |
| f    | b    |    9 |
+------+------+------+
7 rows in set (0.00 sec)

Ich hoffe, Du kannst dieses Beispiel auf Deinen Bedarf hin extrapolieren.

Freundliche Grüße

Vinzenz