模拟 row_number 函数
Posted
技术标签:
【中文标题】模拟 row_number 函数【英文标题】:Emulating row_number function 【发布时间】:2012-10-04 12:56:16 【问题描述】:我有 X 个类别、Y 个论坛和 Z 个主题。
主题属于一个论坛论坛属于一个类别
我希望能够选择 X 个类别和
为每个类别选择前 3 个论坛和 为每个论坛选择前 4 个主题。(仅以数字为例)
我通过变量模拟 row_numbers 来做到这一点,因为 mysql 不支持这个开箱即用。
不幸的是,行号仍然有问题。也许有人可以看看这里有什么问题。 为了更好的可读性,我在此处上传了带有代码突出显示的查询和结果
SELECT
CatRow,
c_id,
c_name,
ForumRow,
f_id,
f_name,
ThreadRow,
t_id,
t_title
FROM (
SELECT
@cat_row := IF(@prev_cat = c.id, @cat_row+1, 1) AS CatRow,
@forum_row := IF(@prev_forum = f.id, @forum_row+1, 1) AS ForumRow,
@thread_row := IF(@prev_thread = t.id, @thread_row+1, 1)AS ThreadRow,
c.id AS c_id,
c.name AS c_name,
f.id AS f_id,
f.name AS f_name,
t.id AS t_id,
t.title AS t_title,
@prev_cat := c.id,
@prev_forum := f.id,
@prev_thread:= t.id
FROM (
SELECT
*
FROM
forum_categories c,
(SELECT @cat_row := 1) AS x,
(SELECT @prev_cat := '') AS y
ORDER BY @cat_row
) AS c
LEFT JOIN (
SELECT
*
FROM
forum_forums AS f,
(SELECT @forum_row := 1) AS x,
(SELECT @prev_forum := '') AS y
ORDER BY @forum_row
) AS f ON (c.id = f.fk_forum_category_id )
LEFT JOIN (
SELECT
*
FROM
forum_threads AS t,
(SELECT @thread_row := 1) AS x,
(SELECT @prev_thread := '') As y
ORDER BY @thread_row
) AS t ON (f.id = t.fk_forum_forums_id )
ORDER BY c.id ASC, f.id ASC, t.id ASC
) c
-- This is for later to actually limit the joins
-- WHERE CatRow <= 3 AND
-- ForumRow <= 3 AND
-- ThreadRow <= 4
结果如下:
CatRow c_id c_name ForumRow f_id f_name ThreadRow t_id t_title
4 1 General 4 2 Talk 1 42 talk
5 1 General 5 2 Talk 1 43 Talk...
6 1 General 6 2 Talk 1 44 locked thread
7 1 General 7 2 Talk 1 45 closed thread
3 1 General 3 2 Talk 1 48 :(:red::confuse::)
1 1 General 1 2 Talk 1 50 gsfdgsdg
2 1 General 2 2 Talk 1 51 asdasd
9 1 General 2 5 Voting 1 47 some title
8 1 General 1 5 Voting 1 49 sadfsad
1 2 Support 1 3 Help 1 40 Hueeelefe
2 2 Support 1 4 Features and Bugs 1 41 What is a bug?
3 2 Support 1 7 Test 1 NULL NULL
2 3 News 2 1 News 1 39 News by admin
1 3 News 1 1 News 1 46 further news
最后我需要能够指定:
在哪里 CatRow 论坛行 AND ThreadRow由于row_numbers 错误,这还不可能。 有什么想法???
其实我希望结果是这样的:
CatRow c_id c_name ForumRow f_id f_name ThreadRow t_id t_title
1 1 General 1 2 Talk 1 42 talk
1 1 General 1 2 Talk 2 43 Talk...
1 1 General 1 2 Talk 3 44 locked thread
1 1 General 1 2 Talk 4 45 closed thread
1 1 General 1 2 Talk 5 48 :(:red::confuse::)
1 1 General 1 2 Talk 6 50 gsfdgsdg
1 1 General 1 2 Talk 7 51 asdasd
1 1 General 2 5 Voting 1 47 some title
1 1 General 2 5 Voting 2 49 sadfsad
2 2 Support 1 3 Help 1 40 Hueeelefe
2 2 Support 2 4 Features and Bugs 1 41 What is a bug?
2 2 Support 3 7 Test 1 NULL NULL
3 3 News 1 1 News 1 39 News by admin
3 3 News 1 1 News 2 46 further news
为了更好的可读性,我上传了带有代码突出显示的查询和结果的图片: http://i.stack.imgur.com/9tzmH.png http://i.stack.imgur.com/xXF6U.png
【问题讨论】:
【参考方案1】:SQL Fiddle
set @c_row := 0, @f_row := 0, @t_row := 0;
set @cat := 0, @forum := 0;
select CatRow, c_id, c_name, ForumRow, f_id, f_name, ThreadRow, t_id, title
from (
select
@c_row := if(@cat = c_id, @c_row, @c_row + 1) CatRow,
@f_row := if(@forum = f_id, @f_row, if(@cat = c_id, @f_row + 1, 1)) ForumRow,
@t_row := if(@forum = f_id, @t_row + 1, 1) ThreadRow,
@cat := c_id as a,
@forum := f_id as b,
c_id,
c_name,
f_id,
f_name,
t_id,
title
from (
select
c.id c_id, f.id f_id, t.id t_id,
c.name c_name, f.name f_name, title
from
forum_categories c
inner join
forum_forums f on f.c_id = c.id
inner join
forum_threads t on t.f_id = f.id
order by
c.id, f.id, t.id
) s
) s;
+--------+------+---------+----------+------+-------------------+-----------+------+--------------------+
| CatRow | c_id | c_name | ForumRow | f_id | f_name | ThreadRow | t_id | title |
+--------+------+---------+----------+------+-------------------+-----------+------+--------------------+
| 1 | 1 | General | 1 | 2 | Talk | 1 | 42 | talk |
| 1 | 1 | General | 1 | 2 | Talk | 2 | 43 | Talk... |
| 1 | 1 | General | 1 | 2 | Talk | 3 | 44 | locked thread |
| 1 | 1 | General | 1 | 2 | Talk | 4 | 45 | closed thread |
| 1 | 1 | General | 1 | 2 | Talk | 5 | 48 | :(:red::confuse::) |
| 1 | 1 | General | 1 | 2 | Talk | 6 | 50 | gsfdgsdg |
| 1 | 1 | General | 1 | 2 | Talk | 7 | 51 | asdasd |
| 1 | 1 | General | 2 | 5 | Voting | 1 | 47 | some title |
| 1 | 1 | General | 2 | 5 | Voting | 2 | 49 | sadfsad |
| 2 | 2 | Support | 1 | 3 | Help | 1 | 40 | Hueeelefe |
| 2 | 2 | Support | 2 | 4 | Features and Bugs | 1 | 41 | What is a bug? |
| 3 | 3 | News | 1 | 1 | News | 1 | 39 | News by admin |
| 3 | 3 | News | 1 | 1 | News | 2 | 46 | further news |
+--------+------+---------+----------+------+-------------------+-----------+------+--------------------+
【讨论】:
【参考方案2】:我现在已经解决了: 这适用于所有级别。我也为每个线程添加了帖子
SELECT
CatRow,
c_id,
c_name,
ForumRow,
f_id,
f_name,
ThreadRow,
t_id,
t_title,
PostRow,
p_id,
p_title
FROM (
SELECT
@cat_row := IF(@prev_cat = c.id, @cat_row, @cat_row+1) AS CatRow,
@forum_row := IF(@prev_forum = f.id, @forum_row, IF(@prev_cat = c.id, @forum_row+1, 1)) AS ForumRow,
@thread_row := IF(@prev_thread = t.id, @thread_row, IF(@prev_forum = f.id, @thread_row+1, 1))AS ThreadRow,
@post_row := IF(@prev_post = t.id, @post_row, IF(@prev_thread= t.id, @post_row+1, 1)) AS PostRow,
c.id AS c_id,
c.name AS c_name,
f.id AS f_id,
f.name AS f_name,
t.id AS t_id,
t.title AS t_title,
p.id AS p_id,
p.title AS p_title,
@prev_cat := c.id,
@prev_forum := f.id,
@prev_thread:= t.id,
@prev_post := p.id
FROM (
SELECT
*
FROM
forum_categories,
(SELECT @cat_row := 0) AS x,
(SELECT @prev_cat := '') AS y
ORDER BY YOUR_ORDER_HERE
) AS c
LEFT JOIN (
SELECT
*
FROM
forum_forums,
(SELECT @forum_row := 0) AS x,
(SELECT @prev_forum := '') AS y
ORDER BY YOUR_ORDER_HERE
) AS f ON (c.id = f.fk_forum_category_id )
LEFT JOIN (
SELECT
*
FROM
forum_threads,
(SELECT @thread_row := 0) AS x,
(SELECT @prev_thread := '') As y
ORDER BY YOUR_ORDER_HERE
) AS t ON (f.id = t.fk_forum_forums_id )
LEFT JOIN (
SELECT
*
FROM
forum_posts AS p,
(SELECT @post_row := 0) AS x,
(SELECT @prev_post := '') As y
ORDER BY YOUR_ORDER_HERE
) AS p ON (t.id = p.fk_forum_thread_id )
) c
-- WHERE CatRow <= HOW_MANY_CATS AND ForumRow <= HOW_MANY_FORUMS_PER_CAT AND ThreadRow <= HOW_MANY_THREADS_PER_FORUM AND PostRow <= HOW_MANY_POSTS_PER_THREAD
CatRow c_id c_name ForumRow f_id f_name ThreadRow t_id t_title PostRow p_id p_title
1 1 General 1 2 Talk 1 50 gsfdgsdg 1 NULL NULL
1 1 General 1 2 Talk 2 51 asdasd 1 NULL NULL
1 1 General 1 2 Talk 3 48 :(:red::confuse::) 1 80 t1
1 1 General 1 2 Talk 3 48 :(:red::confuse::) 2 79 NULL
1 1 General 1 2 Talk 3 48 :(:red::confuse::) 3 78 NULL
1 1 General 1 2 Talk 4 42 talk 1 76 NULL
1 1 General 1 2 Talk 4 42 talk 2 75 sdfg
1 1 General 1 2 Talk 4 42 talk 3 74 NULL
1 1 General 1 2 Talk 4 42 talk 4 73 NULL
1 1 General 1 2 Talk 4 42 talk 5 72 NULL
1 1 General 1 2 Talk 5 43 Talk... 1 NULL NULL
1 1 General 1 2 Talk 6 44 locked thread 1 NULL NULL
1 1 General 1 2 Talk 7 45 closed thread 1 NULL NULL
1 1 General 2 5 Voting 1 49 sadfsad 1 77 NULL
1 1 General 2 5 Voting 2 47 some title 1 NULL NULL
2 3 News 1 1 News 1 46 further news 1 NULL NULL
2 3 News 1 1 News 2 39 News by admin 1 71 NULL
2 3 News 1 1 News 2 39 News by admin 2 68 NULL
2 3 News 1 1 News 2 39 News by admin 3 67 NULL
3 2 Support 1 3 Help 1 40 Hueeelefe 1 70 NULL
3 2 Support 1 3 Help 1 40 Hueeelefe 2 69 NULL
3 2 Support 2 4 Features and Bugs 1 41 What is a bug? 1 NULL NULL
3 2 Support 3 7 Test 1 NULL NULL 1 NULL NULL
【讨论】:
【参考方案3】:使用 LIMIT
的依赖子查询可能会解决您的问题:
SELECT CatRow, c_id, c_name, ForumRow, f_id, f_name, ThreadRow, t_id, t_title
FROM forum_categories c
INNER JOIN (SELECT * FROM forum_forums f WHERE f.fk_forum_category_id=c.id LIMIT 3) AS t1
INNER JOIN (SELECT * FROM forum_threads t WHERE t.fk_forum_forums_id=f.id LIMIT 4) AS t2
【讨论】:
这有两个错误。首先every derived table must have its own alias
。其次,无法从联接中的子查询中引用另一个表。以上是关于模拟 row_number 函数的主要内容,如果未能解决你的问题,请参考以下文章
LINQ to SQL 模拟实现 ROW_NUMBER() OVER(ORDER BY ...) 的功能