Question

This is very similar to Limit results from joined table to one row, but I'm struggling to get it to work exactly as I need...

The table structure is very similar:

CREATE TABLE tblProducts (
    intProductID int(11) NULL AUTO_INCREMENT,
    strProductName varchar(255) NULL,
    PRIMARY KEY (intProductID)
);

CREATE TABLE tblProductImages (
    intImageID int(11) NULL AUTO_INCREMENT,
    intProductID int(11) NULL,
    strImageName varchar(255) NULL,
    intOrder int(11) NULL,
    PRIMARY KEY (intImageID)
);

Populate the tables with:

INSERT INTO tblProducts (strProductName)
VALUES
('Product #1'), ('Product #2'), ('Product #3');

INSERT INTO tblProductImages (intProductID, strImageName, intOrder) 
VALUES
(1, 'image_for_1.jpg', 1), 
(2, '1st_image_for_2.jpg', 1), 
(2, '2nd_image_for_2.jpg', 2);

I want to do a SELECT that returns all 3 products, with appropriate images. Similar to:

intProductID, strProductName, strImageName
1, Product #1, image_for_1.jpg
2, Product #2, 1st_image_for_2.jpg
3, Product #3, NULL

A simple solution would be:

SELECT intProductID, strProductName, 
 (SELECT strImageName
  FROM tblProductImages
  WHERE tblProductImages.intProductID = tblProducts.intProductID
  ORDER BY intOrder
  LIMIT 1)
FROM tblProducts

But this fails if I want to add & return another field from the tblProductImages (AltText for example)

I've setup a Fiddle at http://sqlfiddle.com/#!2/883c5/1

Thanks

Was it helpful?

Solution

This one will work, but is quite ugly

select p.intProductId, p.strProductName, pi.strImageName 
from tblProducts p
inner join tblProductImages pi on pi.intProductID = p.intProductId
inner JOIN (select min(intOrder) minOrder, intProductID
           FROM tblProductImages
           GROUP BY intProductID) s
           on s.intProductID = p.intProductID and s.minOrder = pi.intOrder
union
select p.intProductId, p.strProductName, pi.strImageName
from tblProducts p
left join tblProductImages pi on pi.intProductID = p.intProductId
where pi.intProductID is null;

alternative :

select p.intProductId, p.strProductName, pi.strImageName
from tblProducts p
left join tblProductImages pi on pi.intProductID = p.intProductId
where pi.intProductId is null or pi.IntOrder = (select min(intOrder)
                                                from tblProductImages
                                                where intProductId = pi.intProductId);

Sqlfiddle

OTHER TIPS

You need to use aggregation (specifically GROUP BY). You want to GROUP BY the product name and use a LEFT JOIN so that the image does not need to exist.

SELECT strProductName, strImageName
FROM tblProducts LEFT JOIN tblProductImages USING (intProductID)
GROUP BY strProductName

Note that the image that is selected in this case is random (though usually it will be in intImageID order). This assumes that the actual image retrieved is not important as long as it is associated with the given product.

http://sqlfiddle.com/#!2/1391e6/1

For that exact output, you might need to modify your insert statement for table tblProductImages as:

INSERT INTO tblProductImages (intProductID, strImageName, intOrder) VALUES
    (1, 'image_for_1.jpg', 1), (2, '1st_image_for_2.jpg', 1), (3, '2nd_image_for_3.jpg', 2);

And use the below Select statement:

SELECT tP.intProductID, tp.strProductName, tPI.strImageName
FROM tblProducts AS tP 
INNER JOIN tblProductImages AS tPI ON tP.intProductID = tPI.intProductID;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top