tags:

views:

443

answers:

3

Hey Everyone,

I am looking to do the following (see pseudo code); I want to select 4 rows for each gd.id (7, 11 or 9). I've incorrectly use limit because that only brings up 4 rows in total. Anyone have an idea on how to change this query to accomplish my goal?

SELECT gd.gid, gd.aid, li.ads, li.til
FROM gd
JOIN li ON li.a_id = gd.aid
WHERE gd.gid
IN (
'7', '11', '9'
)
ORDER BY li.timestamp DESC
LIMIT 4 #FOR EACH ;-)

Thank you!

Ice

p.s. Maybe sometype of group_by?

+2  A: 

Okay I'm posting this second answer now that I understand the relationship between your tables.

CREATE TABLE gd (
  aid INT AUTO_INCREMENT PRIMARY KEY,
  gid INT
);

INSERT INTO gd (gid) VALUES
  (7), (7), (7),                -- fewer than four rows
  (9), (9), (9), (9),           -- exactly four rows
  (11), (11), (11), (11), (11); -- greater than four rows

CREATE TABLE li (
  a_id INT AUTO_INCREMENT PRIMARY KEY,
  ads VARCHAR(10),
  til VARCHAR(10),
  `timestamp` TIMESTAMP
);

INSERT INTO li (ads, til, `timestamp`) VALUES
  ('foo1', 'bar1', '2008-01-01'),
  ('foo2', 'bar2', '2008-02-01'),
  ('foo3', 'bar3', '2008-03-01'),
  ('foo4', 'bar4', '2008-04-01'),
  ('foo5', 'bar5', '2008-05-01'),
  ('foo6', 'bar6', '2008-06-01'),
  ('foo7', 'bar7', '2008-07-01'),
  ('foo8', 'bar8', '2008-08-01'),
  ('foo9', 'bar9', '2008-09-01'),
  ('foo10', 'bar10', '2008-10-01'),
  ('foo11', 'bar11', '2008-11-01'),
  ('foo12', 'bar12', '2008-12-01');

So you want the top four rows per value of gd.gid, depending on the timestamp value in the associated table li.

SELECT g1.gid, g1.aid, l1.ads, l1.til, l1.`timestamp`
FROM gd AS g1
  INNER JOIN li AS l1 ON (g1.aid = l1.a_id)
  LEFT OUTER JOIN (
    gd AS g2 INNER JOIN li AS l2 ON (g2.aid = l2.a_id)
  ) ON (g1.gid = g2.gid AND l1.`timestamp` <= l2.`timestamp`)
WHERE g1.gid IN ('7', '11', '9')
GROUP BY g1.aid
HAVING COUNT(*) <= 4
ORDER BY g1.gid ASC, l1.`timestamp` DESC;

The output is the following:

+------+-----+-------+-------+---------------------+
| gid  | aid | ads   | til   | timestamp           |
+------+-----+-------+-------+---------------------+
|    7 |   3 | foo3  | bar3  | 2008-03-01 00:00:00 | 
|    7 |   2 | foo2  | bar2  | 2008-02-01 00:00:00 | 
|    7 |   1 | foo1  | bar1  | 2008-01-01 00:00:00 | 
|    9 |   7 | foo7  | bar7  | 2008-07-01 00:00:00 | 
|    9 |   6 | foo6  | bar6  | 2008-06-01 00:00:00 | 
|    9 |   5 | foo5  | bar5  | 2008-05-01 00:00:00 | 
|    9 |   4 | foo4  | bar4  | 2008-04-01 00:00:00 | 
|   11 |  12 | foo12 | bar12 | 2008-12-01 00:00:00 | 
|   11 |  11 | foo11 | bar11 | 2008-11-01 00:00:00 | 
|   11 |  10 | foo10 | bar10 | 2008-10-01 00:00:00 | 
|   11 |   9 | foo9  | bar9  | 2008-09-01 00:00:00 | 
+------+-----+-------+-------+---------------------+
Bill Karwin
Thank you so much Bill! You really went the extra mile hereI was able to make it work with one small change. Adding gl.id in the SELECT and in the GROUP BY clause since that was the primary key of gd and not aid.Again thank you for solving what I thought was a near impossible MySQL problem.Ice
Glad to help! Please remember to give my answer a good vote. :-)
Bill Karwin
A: 

Thank you so much Bill! Your help has been greatly appreciated and you really went the extra mile here.

I was able to make it work with one small change. Adding gl.id in the SELECT and in the GROUP BY clause since that was the primary key of gd and not aid.

Again thank you for solving what I thought was a near impossible MySQL problem.

Ice

Usually one would express appreciation by upvoting and/or accepting the working answer.
mluebke
A: 

the usual approach is:

(SELECT * FROM table WHERE key=X LIMIT 4) UNION ALL (SELECT * FROM table WHERE key=Y LIMIT 4) UNION ALL (SELECT * FROM table WHERE key=Z LIMIT 4) ORDER BY ... LIMIT ...

do note, it will materialize each subselect in the temptable, so isn't very efficient if your outer LIMIT is low, and internal ones are high.

Domas Mituzas