2009-08-13 4 views
1

Ich habe zwei Tabellen: Fahrzeuge (Id, VIN) und Bilder (Id, VehicleId, Name, Default). Ich muss die VIN des Fahrzeugs und sein Standardbild auswählen, um es in einer Tabelle anzuzeigen. Das Problem, das ich habe, ist, dass ich, wenn ein Standardbild nicht eingestellt ist, möchte ich noch ein Bild auswählen, um anzuzeigen, wenn es existiert. Wenn keine Bilder vorhanden sind, muss die Fahrzeuginformation natürlich auch noch angezeigt werden. Hier ist, was ich bisher:MySQL komplexe JOIN-Problem - Kann nicht die richtigen Ergebnisse

SELECT 
    Vehicles.Id, Vehicles.VIN, Images.Name AS Image, 
    (SELECT COUNT(*) FROM Images WHERE VehicleId = Vehicles.Id) AS ImageCount 
FROM 
    Vehicles 
LEFT JOIN 
    Images ON Images.VehicleId = Vehicles.Id 
WHERE 
    Images.Default = 1 

Diese Aussage Fahrzeuge angezeigt werden, die ein Standardbild, aber nicht etwas angezeigt, wenn kein Standard festgelegt ist. Was muss ich ändern?

EDIT *

Um mein Problem besser zu beschreiben hier einige Testdaten:

VEHICLES: 
ID VIN 
1 12341234123412341 
2 23452345234523452 
3 34534534534534534 

IMAGES: 
ID VEHICLEID NAME DEFAULT 
1 1   a 1 
2 1   b 0 
3 2   c 0 
4 2   d 0 

Obwohl Fahrzeug 2 keinen Standard hat Ich will es ein Bild auszuwählen anzuzeigen. Auch Fahrzeug 3 hat keine Bilder, aber ich brauche es immer noch in der Tabelle ohne Bild. Fahrzeug 1 zeigt sein Standardbild an, weil es eingestellt ist. Ich hoffe, das klärt die Dinge auf.

+0

Also, wenn ein Standardbild nicht gesetzt ist, geht die mittlere dass es überhaupt keinen Eintrag in der Tabelle Images gibt, oder nur, dass es keinen Eintrag mit einem Standardwert von 1 gibt? – Brandon

+0

Es bedeutet, dass es keinen Eintrag mit Default 1 gibt. –

Antwort

2

OK, hier ist das Setup:

mysql> create table VEHICLES (ID INT PRIMARY KEY, VIN CHAR(17)); 
mysql> INSERT INTO VEHICLES (ID, VIN) VALUES(1, '12341234123412341'); 
mysql> INSERT INTO VEHICLES (ID, VIN) VALUES(2, '23452345234523452'); 
mysql> INSERT INTO VEHICLES (ID, VIN) VALUES(3, '34534534534534534'); 

Hinweis ich die Spalte DEFAULT-DEF in der IMAGES Tabelle umbenennen musste:

mysql> CREATE TABLE IMAGES (ID INT PRIMARY KEY, VEHICLEID INT, NAME VARCHAR(20), DEF INT); 
mysql> INSERT INTO IMAGES(ID, VEHICLEID, NAME, DEF) VALUES(1, 1, 'a', 1); 
mysql> INSERT INTO IMAGES(ID, VEHICLEID, NAME, DEF) VALUES(2, 1, 'b', 0); 
mysql> INSERT INTO IMAGES(ID, VEHICLEID, NAME, DEF) VALUES(3, 2, 'c', 0); 
mysql> INSERT INTO IMAGES(ID, VEHICLEID, NAME, DEF) VALUES(4, 2, 'd', 0); 

Und hier ist die Lösung.

Zuerst benötigen wir eine Abfrage, die eine Bildzeile pro Fahrzeug erhält und die Standardreihe auswählt, wenn es eine gibt.

Dazu sortieren wir die Bilder in absteigender Reihenfolge von DEF (so dass die 1en oben liegen) und gruppieren dann durch VEHICLEID, um sicherzustellen, dass es nur eine Zeile pro Fahrzeug gibt.

mysql> SELECT * FROM (SELECT * FROM IMAGES ORDER BY DEF DESC) sortedimages GROUP BY VEHICLEID; 
+----+-----------+------+------+ 
| ID | VEHICLEID | NAME | DEF | 
+----+-----------+------+------+ 
| 1 |   1 | a | 1 | 
| 3 |   2 | c | 0 | 
+----+-----------+------+------+ 

Nun wählen wir aus der VEHICLES Tabelle und LEFT OUTER JOIN auf die obige Abfrage sicherstellen, dass wir immer pro Fahrzeug, eine Reihe bekommen:

mysql> SELECT * FROM VEHICLES LEFT OUTER JOIN (SELECT * FROM (SELECT * FROM IMAGES ORDER BY DEF DESC) sortedimages GROUP BY VEHICLEID) defaultimages ON VEHICLES.ID = defaultimages.VEHICLEID; 
+----+-------------------+------+-----------+------+------+ 
| ID | VIN    | ID | VEHICLEID | NAME | DEF | 
+----+-------------------+------+-----------+------+------+ 
| 1 | 12341234123412341 | 1 |   1 | a | 1 | 
| 2 | 23452345234523452 | 3 |   2 | c | 0 | 
| 3 | 34534534534534534 | NULL |  NULL | NULL | NULL | 
+----+-------------------+------+-----------+------+------+ 
3

WHERE Images.Default = 1 oder Images.Default NULL IST

Die WHERE-Klausel links dreht kommen in einen inneren verbinden, da sie einen passenden Datensatz erfordert, die Kriterien zu erfüllen.

+0

Wenn ich es so mache, listet es das Fahrzeug für jedes Bild auf. Nehmen wir an, das Fahrzeug hat 4 Bilder, es wird 4 Mal für jedes seiner Bilder angezeigt. Ich muss das Fahrzeug einmal mit einem Bild Standard oder nicht (wenn es nicht gesetzt ist) oder kein Bild auflisten, wenn keines in der Datenbank ist. –

+0

Bitte überprüfen Sie meine Frage, die mit Testdaten aktualisiert wurde. Hoffentlich wird dies helfen, mein Problem anzuzeigen. –

+0

Wenn die Bildtabelle 0 gegen die anderen 3 Zeilen hat, werden sie durch den Nullwert nicht importiert, aber wenn sie null haben, wird dies der Fall sein. – Andrew

0

Ich würde diese Abfrage auf diese Weise schreiben:

SELECT v.Id, v.VIN, 
    GROUP_CONCAT(IF(i.Default=1, i.Name, NULL)) AS ImageName, 
    COUNT(i.Id) AS ImageCount 
FROM Vehicles v 
LEFT JOIN Images i ON (i.VehicleId = v.Id AND i.Default = 1) 
GROUP BY v.Id; 

Ich verstehe, dass Sie ein Bild wollen auch angezeigt werden, wenn es keine als Standard markiert sind. Hier ist eine Lösung, die dies tut:

SELECT v.Id, v.VIN, 
    COALESCE(i1.Name, MIN(i2.Name)) AS ImageName, 
    COUNT(i2.Id) AS ImageCount 
FROM Vehicles v 
LEFT JOIN Images i1 ON (i1.VehicleId = v.Id AND i1.Default = 1) 
LEFT JOIN Images i2 ON (i2.VehicleId = v.Id) 
GROUP BY v.Id; 

Sie nicht angegeben haben, welche Bild sollte es angezeigt, wenn kein Standard ist. Also habe ich den Ausdruck willkürlich so gestaltet, dass der Bildname zuerst alphabetisch angezeigt wird.

Beachten Sie auch, dass es in dieser Abfrage ein kartesisches Produkt zwischen i1 und i2 gibt. Ich würde annehmen, dass Sie nicht mehr als ein Standardbild für ein bestimmtes Fahrzeug markieren, aber wenn Sie dies tun, erhalten Sie unerwartete Ergebnisse, z. ImageCount wird mit der Anzahl der Standardbilder multipliziert. Dies ist kein Problem, wenn es höchstens ein Standardbild pro Fahrzeug gibt.

0

Ich würde so etwas tun, um den Standardnamen für Bilder ohne Bild zu erhalten.

SELECT 
    Vehicles.Id, Vehicles.VIN, COALESCE(Images.Name,'default image name')AS Image, 
    (SELECT COUNT(*) FROM Images WHERE VehicleId = Vehicles.Id) AS ImageCount 
FROM 
    Vehicles 
LEFT JOIN 
    Images ON Images.VehicleId = Vehicles.Id 
WHERE 
    Images.Default = 1 OR Images.Name IS NULL 
0
SELECT 
    Vehicles.Id, Vehicles.VIN, Images.Name AS Image, 
    (SELECT COUNT(*) FROM Images WHERE VehicleId = Vehicles.Id) AS ImageCount 
FROM 
    Vehicles 
LEFT JOIN 
    Images ON Images.VehicleId = Vehicles.Id 
WHERE 
    Images.Default = 1 OR Images.Name IS NULL 

Dies setzt voraus, dass alle Bilder einen Namen haben. Das Problem mit Ihrer ursprünglichen Abfrage besteht darin, dass "Images.Default = 1" erfordert, dass die Abfrage mit einem Image übereinstimmen muss. Wenn Sie zulassen, dass Images.Id NULL ist, wird auch der Fall erfasst, in dem kein Bild gefunden wurde und der linke Join NULL-Werte enthält.