Yes, this is probably yet another greatest-n-per-group question... But I've tried at least a dozen of answers with the questions already posted here and could not find one that works for what I'm trying to do. It's either probably something really simple I'm overlooking, or what I want is just a bit slightly different. Probably the first :/
I have two tables (InnoDB), one for imagesets which has one row per set with some info like a set_title, what product it is linked to, on/offline status and dateadded:
GallerySets
- set_id (primary)
- product_id (foreign key)
- set_title (varchar 128)
- set_status (enum online/offline, index)
- set_dateadded (datetime, index)
And a table for the individual images, each set has one or more images. No path field though, I'm just using it kinda like set_id/pic_id.jpg... There are some other fields like viewcounts etc, but those are not needed for this problem so I've left those out.
GalleryPics
- pic_id (primary)
- set_id (primary, foreign key)
- pic_dateadded (datetime, index)
Basically I want to show 2 images for the latest 4 sets. I've got this query below which gets close to what I want, the only thing it doesn't do is limit it to 2 per set:
SELECT s.`set_id`, s.`set_title`, s.`set_dateadded`, p.`pic_id`, p.`pic_dateadded`
FROM `GallerySets` s
LEFT JOIN `GalleryPics` p
ON (s.`set_id` = p.`set_id`)
WHERE s.set_status = 'online'
ORDER BY s.`set_dateadded` DESC, p.`pic_dateadded` ASC
It just shows everything of course. In other questions (like this one) I've seen changing the ON-line and adding HAVING should do the trick:
ON (s.`set_id` = p.`set_id` AND s.`set_dateadded` < p.`pic_dateadded`)
HAVING COUNT(*) < 3
I've tried all sorts of variations on this with HAVING, but it doesn't get me any closer. It's actually returning either zero results or one row per set, as soon as I add any kind of HAVING line to it. Perhaps I'm trying to use the wrong greatest-n-per-group solution? Maybe because I want to sort on a datetime field and not on auto-incrementing IDs?
Wanted results
Actually want more like first three pics from latest five sets, but in order to give a not too long result example... tuned it down a bit.
set_id | set_title | set_dateadded | pic_id | pic_dateadded
4 | blah4 | 2011-10-04 00:00:00 | 32 | 2011-10-04 12:44:01
4 | blah4 | 2011-10-04 00:00:00 | 33 | 2011-10-04 12:44:02
3 | blah3 | 2011-10-03 00:00:00 | 26 | 2011-10-03 12:33:01
3 | blah3 | 2011-10-03 00:00:00 | 27 | 2011-10-03 12:33:02
2 | blah2 | 2011-10-02 00:00:00 | 11 | 2011-10-02 12:22:01
2 | blah2 | 2011-10-02 00:00:00 | 12 | 2011-10-02 12:22:02
1 | blah1 | 2011-10-01 00:00:00 | 1 | 2011-10-01 12:11:01
1 | blah1 | 2011-10-01 00:00:00 | 2 | 2011-10-01 12:11:02
Starting to think it might not be the worst idea to just have one query get the latest sets and then use a while loop in php with a query to get a few pics per set. :/
Solution
Working solution found thanks to George Psarakis. This shows the first two pics in the latest four sets:
SET @rownum=0;
SET @last_group_id=0;
SELECT gp.pic_id, gp.pic_dateadded, gs.*,
@rownum:=IF(gp.set_id<>@last_group_id,1,@rownum+1) AS rn, @last_group_id:=gp.set_id
FROM GalleryPics gp JOIN
(
SELECT * FROM GallerySets WHERE set_status='online'
ORDER BY gs.set_dateadded DESC LIMIT 5
) AS gs
ON gs.set_id = gp.set_id
HAVING rn < 4
ORDER BY gs.set_dateadded DESC, gp.pic_dateadded ASC
(Query time is about 0.0030 sec for 900+ sets and 10000+ pics)