Reputation: 1390
I tried asking question before, but it's hard to ask in specific without right terminology I am not quite familiar with. So here is an example
Take this query for example:
(
SELECT *
FROM comments
WHERE depth = 0
ORDER BY id DESC
LIMIT 2
)
UNION ALL
(
SELECT c.*
FROM comments c JOIN
(
SELECT id
FROM comments
WHERE depth = 0
ORDER BY id DESC
LIMIT 2
) p ON c.parent_id = p.id
LIMIT 5
)
id parent_id depth title
1 0 0 Title 1
2 0 0 Title 2
3 1 1 Title 3
4 1 1 Title 4
5 1 1 Title 5
6 1 1 Title 6
7 1 1 Title 7
I get two depth 0 rows and in join I get 5 child elements of those two returned queries as well. What I would like to get is to get 5 child elements of each of those two queries, total of 10 rows (of depth 1). For example:
id parent_id depth title
1 0 0 Title 1
2 0 0 Title 2
3 1 1 Title 3
4 1 1 Title 4
5 1 1 Title 5
6 1 1 Title 6
7 1 1 Title 7
8 2 1 Title 8
9 2 1 Title 9
10 2 1 Title 10
11 2 1 Title 11
12 2 1 Title 12
Is that even possible with adjacency list and a requirement to return everything as union (flat)?
edit: Thanks to Bill Karwin's answer, I got it working now. I wonder still if there is a shorter way to write this. I have 6 (0-5) depth levels, so my query is rather long (and probably not optimal). Here is what it looks like for three levels (you can imagine what the full one looks like).
-- DEPTH LEVEL 0
(
SELECT * FROM (
SELECT *, 1 as _rn, @parent:=0
FROM comments
WHERE depth = 0
ORDER BY id DESC
LIMIT 2
) as D0
)
union all
-- DEPTH LEVEL 1
(
SELECT *
FROM (
SELECT c.*, @row:=IF(@parent=c.comment_id,@row+1,1) AS _rn, @parent:=c.comment_id
FROM (SELECT @parent:=null) AS _init
STRAIGHT_JOIN comments c
INNER JOIN
(
SELECT id
FROM comments
WHERE depth = 0
ORDER BY id DESC
LIMIT 2
) p ON c.comment_id = p.id
) AS _ranked
WHERE _ranked._rn <= 5
)
union all
-- DEPTH LEVEL 2
(
SELECT *
FROM (
SELECT c.*, @row:=IF(@parent=c.comment_id,@row+1,1) AS _rn, @parent:=c.comment_id
FROM (SELECT @parent:=null) AS _init
STRAIGHT_JOIN comments c
INNER JOIN
(
(
SELECT *
FROM (
SELECT c.*, @row:=IF(@parent=c.comment_id,@row+1,1) AS _rn, @parent:=c.comment_id
FROM (SELECT @parent:=null) AS _init
STRAIGHT_JOIN comments c
INNER JOIN
(
SELECT id
FROM comments
WHERE depth = 0
ORDER BY id DESC
LIMIT 2
) p ON c.comment_id = p.id
) AS _ranked
WHERE _ranked._rn <= 2
)
) p ON c.comment_id = p.id
) AS _ranked
WHERE _ranked._rn <= 2
)
Upvotes: 1
Views: 352
Reputation: 562731
You can't do this with LIMIT, because LIMIT is applied after the result set is completely finished, after all joining, grouping, sorting, etc.
You're using a variation of the greatest-n-per-group type of query. It's tricky to do this in MySQL because MySQL doesn't support the ROW_NUMBER() window function supported by many other SQL databases.
Here's a workaround for MySQL, in which user-defined variables can take the place of partitioned row numbers:
SELECT *
FROM (
SELECT c.*, @row:=IF(@parent=c.parent_id,@row+1,1) AS _rn, @parent:=c.parent_id
FROM (SELECT @parent:=null) AS _init
STRAIGHT_JOIN comments c
INNER JOIN
(
SELECT id
FROM comments
WHERE depth = 0
ORDER BY id DESC
LIMIT 2
) p ON c.parent_id = p.id
) AS _ranked
WHERE _ranked._rn <= 5
Upvotes: 1