为表中的每个组选择前 N 行
我面临一个非常常见的问题,即“为表中的每个组选择前 N 行”。
考虑一个包含 id、name、hair_colour、score 列的表。
我想要一个结果集,对于每种头发颜色,都能得到前 3 名得分手的名字。
为了解决这个问题,我得到了我所需要的 Rick Osborne 的博文“sql-getting-top-n-rows-for-a-grouped-query”
当我的分数相同时,该解决方案无法按预期工作。
在上面的例子中,结果如下。
id name hair score ranknum
---------------------------------
12 Kit Blonde 10 1
9 Becca Blonde 9 2
8 Katie Blonde 8 3
3 Sarah Brunette 10 1
4 Deborah Brunette 9 2 - ------- - - > if
1 Kim Brunette 8 3
考虑行 4 Deborah Brunette 9 2
。如果这也与 Sarah 具有相同的分数 (10),那么对于“黑发”类型的头发,ranknum 将是 2,2,3。
解决这个问题的办法是什么?
I am facing a very common issue regarding "Selecting top N rows for each group in a table".
Consider a table with id, name, hair_colour, score
columns.
I want a resultset such that, for each hair colour, get me top 3 scorer names.
To solve this i got exactly what i need on Rick Osborne's blogpost "sql-getting-top-n-rows-for-a-grouped-query"
That solution doesn't work as expected when my scores are equal.
In above example the result as follow.
id name hair score ranknum
---------------------------------
12 Kit Blonde 10 1
9 Becca Blonde 9 2
8 Katie Blonde 8 3
3 Sarah Brunette 10 1
4 Deborah Brunette 9 2 - ------- - - > if
1 Kim Brunette 8 3
Consider the row 4 Deborah Brunette 9 2
. If this also has same score (10) same as Sarah, then ranknum will be 2,2,3 for "Brunette" type of hair.
What's the solution to this?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
如果您使用的是 SQL Server 2005 或更高版本,则可以使用排名函数和 CTE 来实现此目的:
此 CTE 将按
hair
列的值“分区”您的数据,并且每个分区然后按分数排序(降序)并获取行号;每个分区的最高分数是 1,然后是 2,依此类推。因此,如果您想要每个组的前 3 名,请仅从 CTE 中选择那些
RowNum
等于或小于 3 的行(1、 2, 3) -->就这样吧!If you're using SQL Server 2005 or newer, you can use the ranking functions and a CTE to achieve this:
This CTE will "partition" your data by the value of the
hair
column, and each partition is then order by score (descending) and gets a row number; the highest score for each partition is 1, then 2 etc.So if you want to the TOP 3 of each group, select only those rows from the CTE that have a
RowNum
of 3 or less (1, 2, 3) --> there you go!该算法得出排名的方式是计算叉积中分数等于或大于相关女孩的行数,以生成排名。因此,在您讨论的问题案例中,莎拉的网格看起来
与黛博拉的网格相似,这就是为什么两个女孩在这里都获得 2 的排名。
问题是,当出现平局时,由于此计数,所有女孩都会采用平局范围内的最低值,而您希望她们取最高值。我认为一个简单的改变可以解决这个问题:
不要使用大于或等于比较,而是使用严格的大于比较来计算严格更好的女孩的数量。然后,再加一,你就得到了你的排名(这将酌情处理平局)。因此,内部选择将是:
任何人都可以看到这种方法中我没有注意到的任何问题吗?
The way the algorithm comes up with the rank, is to count the number of rows in the cross-product with a score equal to or greater than the girl in question, in order to generate rank. Hence in the problem case you're talking about, Sarah's grid would look like
and similarly for Deborah, which is why both girls get a rank of 2 here.
The problem is that when there's a tie, all girls take the lowest value in the tied range due to this count, when you'd want them to take the highest value instead. I think a simple change can fix this:
Instead of a greater-than-or-equal comparison, use a strict greater-than comparison to count the number of girls who are strictly better. Then, add one to that and you have your rank (which will deal with ties as appropriate). So the inner select would be:
Can anyone see any problems with this approach that have escaped my notice?
使用此复合选择可以正确处理 OP 问题
请注意,您需要在此处使用 IFNULL 来处理表 girls 对于某种类型的 hair 的行数较少的情况,然后我们希望看到在sql答案中(在OP情况下是3个项目)。
Use this compound select which handles OP problem properly
Note that you need to use IFNULL here to handle case when table girls has less rows for some type of hair then we want to see in sql answer (in OP case it is 3 items).