2009-08-13 8 views
1

J'ai deux tableau: Véhicules (ID, VIN) et Images (Id, VehicleId, Nom, Default). Je dois sélectionner le véhicule VIN et son image par défaut à afficher dans un tableau. Le problème que j'ai est que si une image par défaut n'est pas définie, je voudrais toujours sélectionner une image à afficher si elle existe. Si aucune image n'existe, les informations sur le véhicule doivent également toujours être affichées. Voici ce que j'ai jusqu'à présent:MySQL complexe problème JOIN - Impossible d'obtenir des résultats corrects

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 

Cette déclaration affiche les véhicules qui ont une image par défaut mais n'affichera rien si aucune valeur par défaut est. De quoi ai-je besoin pour changer?

EDIT *

Pour décrire mon problème mieux voici quelques données de test:

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 

Même si un véhicule 2 n'a pas défaut, je veux pour sélectionner une image à afficher. De plus, le véhicule 3 n'a pas d'images du tout mais j'en ai encore besoin pour apparaître dans le tableau sans image. Le véhicule 1 affichera son image par défaut parce qu'elle est définie. J'espère que cela clarifie les choses.

+0

Donc, si une image par défaut n'est pas définie, est-ce que cela signifie qu'il n'y a pas d'entrée dans la table Images, ou juste qu'il n'y a pas d'entrée avec une valeur par défaut de 1? – Brandon

+0

Cela signifie qu'il n'y a pas d'entrée avec la valeur par défaut. 1 –

Répondre

2

OK, voici la configuration:

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'); 

Remarque je devais renommer la colonne DEFAULT-DEF dans la table IMAGES:

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); 

Et voici la solution.

D'abord, nous avons besoin d'une requête qui obtient une rangée d'image par véhicule, en choisissant celle par défaut s'il y en a une. Nous faisons cela en triant les images dans l'ordre décroissant de DEF (de sorte que les 1 sont en haut), puis en regroupant par VEHICLEID pour nous assurer qu'il n'y a qu'une seule ligne par véhicule.

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 | 
+----+-----------+------+------+ 

Maintenant, nous sélectionnons de la table VEHICLES et LEFT OUTER JOIN à la requête ci-dessus pour nous assurer d'avoir toujours une ligne par véhicule:

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

OÙ Images.Default = 1 OU Images.Default IS NULL

La clause where tourne la jointure gauche dans une jointure interne, car il nécessite un enregistrement correspondant à satisfaire aux critères.

+0

Si je le fais de cette façon, il liste le véhicule pour chaque image. Alors disons que le véhicule a 4 images qu'il l'affichera 4 fois pour chacune de ses images. Je dois lister le véhicule une fois avec une image par défaut ou non (si elle n'est pas définie) ou aucune image du tout si aucun n'est dans la base de données. –

+0

S'il vous plaît consulter ma question qui a été mis à jour avec des données de test. J'espère que cela aidera à afficher mon problème. –

+0

Si la table d'images a 0 par rapport aux 3 autres lignes, la valeur null ne les amènera pas, mais si elles ont une valeur nulle, cela sera le cas. – Andrew

0

Je voudrais écrire cette requête ainsi:

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; 

Je comprends que vous voulez une image à afficher, même s'il n'y en a pas marqué comme la valeur par défaut. Voici une solution qui fait cela:

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; 

Vous n'avez pas spécifié qui l'image devrait afficher si aucun ne défaut. J'ai donc arbitrairement conçu l'expression pour montrer le nom de l'image qui est le premier par ordre alphabétique.

Notez également qu'il existe un produit cartésien entre i1 et i2 dans cette requête. Je suppose que vous ne marquez pas plus d'une image par défaut pour un véhicule donné, mais si vous le faites, vous obtiendrez des résultats inattendus, par ex. ImageCount sera multiplié par le nombre d'images par défaut. Ce n'est pas un problème s'il y a au plus une image par défaut par véhicule.

0

Je ferais quelque chose comme ceci pour obtenir le nom par défaut sur ceux sans image.

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 

Cela suppose que toutes les images ont un nom. Le problème avec votre requête d'origine est que le fait d'exiger "Images.Default = 1" implique que la requête doit correspondre à une image. Autoriser Images.Id à être NULL va également capturer le cas où aucune image n'a été trouvée et la jointure gauche remplie de valeurs nulles.