MSSQL选择前10名获胜分数,包括领带和每个类别中的至少一个

时间:2012-08-19 01:01:19

标签: sql-server-2008

我得到了一些帮助,使用以下声明

找到前10个分数,包括并列条目
select T.EntryID, T.CategoryID, T.Score
from (
   select EntryID, CategoryID, Score,
          dense_rank() over(order by Score) as rn
   from YourTable
 ) T
where T.rn <= 10

(谢谢[mikael-eriksson]:https://stackoverflow.com/users/569436/mikael-eriksson

[问题]:MSSQL Selecting top 10 but include columns with duplicate values以下是示例数据:

EntryID CategoryID  Score
3036    1           85
3159    1           85
3039    1           84
3146    1           83
3225    1           82
3045    1           82
3047    1           80
3048    1           80
3049    1           80
3193    1           80
3098    1           80
3025    1           72
3082    1           70
3167    1           70
3122    1           67
3220    1           65
3080    1           65
3168    1           64
______________________
Total Entries >= 18

要求前10名中的每个类别至少有一个条目(或者不论它是什么,即前100名),在这种情况下有3个类别。

现在我需要做的就是在前10名中每个类别至少包含一个条目。即,如果前10个分数都来自第1类,并且有3个类别,我需要从中删除2个最低分数第1类,包括第2类和第3类的最高分数。

从结果中可以看出,所有条目都来自类别1,因此我需要从结果集中删除EntryID的3220,3080和3168,因为它们是得分最低的,并且还包括类别2中得分最高的条目作为第3类中得分最高的条目,结果看起来像这样:

EntryID CategoryID  Score
3036    1           85
3159    1           85
3039    1           84
3146    1           83
3225    1           82
3045    1           82
3047    1           80
3048    1           80
3049    1           80
3193    1           80
3098    1           80
3025    1           72
3082    1           70
3167    1           70
3122    1           67
3019    3           60
3800    2           54
______________________
Total Entries >= 17

同样的情况适用于以下场景,让我们看一下前5个而不是前10个,以便让它更容易一些,正如您在本例中所见,前5个分数排除了类别2中的条目

EntryID CategoryID  Score
3036    1           85
3159    1           85
3039    1           84
3146    1           83
3225    1           82
3045    1           82
3019    3           60
______________________
Total Entries >= 7

在这种情况下,条目3225和3045需要删除,因为它们是得分最低的条目(需要包括3047,即使它是得分最低的条目我需要结果中所有类别的条目)我需要包括来自第2类的得分最高的参赛作品,我希望这样:

EntryID CategoryID  Score
3036    1           85
3159    1           85
3039    1           84
3146    1           83
3019    3           60
3800    2           54
______________________
Total Entries >= 6

然后可能存在可能没有进入特定类别的情况,例如没有类别2条目,因此结果应该仍然具有前5个与前5个的原始结果集一样(包括在下面作为参考)

EntryID CategoryID  Score
3036    1           85
3159    1           85
3039    1           84
3146    1           83
3225    1           82
3045    1           82
3019    3           60
______________________
Total Entries >= 7

请原谅我是否在重复自己,我只是想说清楚理解;)

我非常感谢您的帮助!

1 个答案:

答案 0 :(得分:8)

正如我所看到的,您需要以更复杂的方式对行进行排名,以便包含每个类别中最重要的条目而不管它们的值如何,并且根据条目包含不是顶级条目的条目他们的整体排名。

我要提出的建议可能不是最有效的解决方案,但它应该有效,如果没有别的办法,可能会激励其他人提出更好的解决方案:

WITH ranked1 AS (
  SELECT
    *,
    RankByCategory = DENSE_RANK() OVER (
      PARTITION BY CategoryID
      ORDER BY Score DESC
    )
  FROM YourTable
),
ranked2 AS (
  SELECT
    *,
    FinalRank = DENSE_RANK() OVER (
      ORDER BY
        CASE RankByCategory WHEN 1 THEN 1 ELSE 2 END,
        Score DESC
    )
  FROM ranked1
)
SELECT
  EntryID,
  CategoryID,
  Score
FROM ranked2
WHERE FinalRank <= @top_n
;

第一个CTE按类别对行进行排名,从而让我们找出哪些条目成为各自类别中的顶级条目。下一步(第二次CTE)是关于获得全球排名,这次考虑一个条目是否是其类别中的最高级别。类别最高值获得较低排名,因此确保包含在最终结果中。 (当然,您需要确保类别数量不大于您希望在输出中接收的不同值的数量。)

这是一个live example at SQL Fiddle来玩。