我面临一个非常常见的问题,即“为表格中的每个组选择前N行”。
考虑一个包含id, name, hair_colour, score
列的表格。
我想要一个结果集,这样,对于每种头发颜色,让我获得前三名得分手的名字。
为了解决这个问题,我完全得到了Rick Osborne's blogpost "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
。如果这个得分(10)与Sarah相同,那么“褐发女郎”型头发的排名将为2,2,3。
这是什么解决方案?
答案 0 :(得分:17)
如果您使用的是SQL Server 2005或更高版本,则可以使用排名功能和CTE来实现此目的:
;WITH HairColors AS
(SELECT id, name, hair, score,
ROW_NUMBER() OVER(PARTITION BY hair ORDER BY score DESC) as 'RowNum'
)
SELECT id, name, hair, score
FROM HairColors
WHERE RowNum <= 3
此CTE将按hair
列的值“分区”您的数据,然后每个分区按分数(降序)排序并获取行号;每个分区的最高分为1,然后是2等。
因此,如果您想要每组的TOP 3,请仅选择CTE中RowNum
为3或更少(1,2,3)的那些行 - &gt;你去吧!
答案 1 :(得分:0)
算法提出等级的方式是计算交叉积中的行数,其得分等于或大于所讨论的女孩,以便生成等级。因此,在您谈论的问题案例中,Sarah的网格看起来像
a.name | a.score | b.name | b.score
-------+---------+---------+--------
Sarah | 9 | Sarah | 9
Sarah | 9 | Deborah | 9
对于Deborah来说也是如此,这就是为什么这两个女孩在这里的等级为2的原因。
问题在于,当有一个平局时,所有女孩都会因为这个计数而在绑定范围内取最低值,而你希望他们取代最高值。我认为一个简单的改变可以解决这个问题:
使用严格的大于比较来计算严格更好的女孩数量,而不是大于或等于比较。然后,添加一个,你有你的等级(将适当处理关系)。因此内部选择将是:
SELECT a.id, COUNT(*) + 1 AS ranknum
FROM girl AS a
INNER JOIN girl AS b ON (a.hair = b.hair) AND (a.score < b.score)
GROUP BY a.id
HAVING COUNT(*) <= 3
任何人都可以看到这种方法的任何问题都没有引起我的注意吗?
答案 2 :(得分:0)
使用此复合选择正确处理OP问题
SELECT g.* FROM girls as g
WHERE g.score > IFNULL( (SELECT g2.score FROM girls as g2
WHERE g.hair=g2.hair ORDER BY g2.score DESC LIMIT 3,1), 0)
注意你需要在这里使用IFNULL处理表女孩对于某些类型的发的行数较少的情况然后我们想在sql中看到答案(在OP中)案例是3项)。