【问题标题】:How do I select TOP 5 PERCENT from each group?如何从每个组中选择 TOP 5 PERCENT?
【发布时间】:2011-11-26 15:39:39
【问题描述】:

我有一个这样的示例表:

CREATE TABLE #TEMP(Category VARCHAR(100), Name VARCHAR(100))

INSERT INTO #TEMP VALUES('A', 'John')
INSERT INTO #TEMP VALUES('A', 'John')
INSERT INTO #TEMP VALUES('A', 'John')
INSERT INTO #TEMP VALUES('A', 'John')
INSERT INTO #TEMP VALUES('A', 'John')
INSERT INTO #TEMP VALUES('A', 'John')
INSERT INTO #TEMP VALUES('A', 'Adam')
INSERT INTO #TEMP VALUES('A', 'Adam')
INSERT INTO #TEMP VALUES('A', 'Adam')
INSERT INTO #TEMP VALUES('A', 'Adam')
INSERT INTO #TEMP VALUES('A', 'Lisa')
INSERT INTO #TEMP VALUES('A', 'Lisa')
INSERT INTO #TEMP VALUES('A', 'Bucky')
INSERT INTO #TEMP VALUES('B', 'Lily')
INSERT INTO #TEMP VALUES('B', 'Lily')
INSERT INTO #TEMP VALUES('B', 'Lily')
INSERT INTO #TEMP VALUES('B', 'Lily')
INSERT INTO #TEMP VALUES('B', 'Lily')
INSERT INTO #TEMP VALUES('B', 'Tom')
INSERT INTO #TEMP VALUES('B', 'Tom')
INSERT INTO #TEMP VALUES('B', 'Tom')
INSERT INTO #TEMP VALUES('B', 'Tom')
INSERT INTO #TEMP VALUES('B', 'Ross')
INSERT INTO #TEMP VALUES('B', 'Ross')
INSERT INTO #TEMP VALUES('B', 'Ross')

SELECT Category, Name, COUNT(Name) Total
FROM #TEMP
GROUP BY Category, Name
ORDER BY Category, Total DESC

DROP TABLE #TEMP

给我以下内容:

A   John    6
A   Adam    4
A   Lisa    2
A   Bucky   1
B   Lily    5
B   Tom     4
B   Ross    3

现在,我如何从每个类别中选择TOP 5 PERCENT 记录假设每个类别有超过 100 条记录(此处未显示在示例表中)?例如,在我的实际表中,它应该酌情从A 中删除John 记录和从B 中删除Lily 记录(同样,我没有在此处显示完整表)以获得:

A   Adam    4
A   Lisa    2
A   Bucky   1
B   Tom     4
B   Ross    3

我一直在尝试使用CTEs 和PARTITION BY 子句,但似乎无法达到我想要的效果。它从整体结果中删除了 TOP 5 PERCENT,但不是从每个类别中删除。有什么建议吗?

【问题讨论】:

  • 可能会有所帮助 - 如果您有一个组的计数,请记住 5% 将是“row_num
  • @KierenJohnstone:+1 谢谢。我知道我可能不得不使用 CROSS APPLY 或类似的东西,但仍然遇到一些麻烦。如果我弄明白了会更新。
  • 那么希望的输出是什么?与计数 6 相比,删除前 5% 的百分比非常少。一行(A,John)是 16%。
  • 看到这个类似的问题:stackoverflow.com/questions/4373451/…
  • @Legend - 我仍然不清楚你想要什么。请给出期望的结果并解释它们是如何得出的。不确定您希望在什么时候应用 TOP 5%

标签: sql sql-server tsql sql-server-2008 greatest-n-per-group


【解决方案1】:

您可以将 CTE(通用表表达式)与 NTILE 窗口函数配对使用 - 这会将您的数据切成您需要的任意数量的切片,例如在你的情况下,分成 20 片(每片 5%)。

;WITH SlicedData AS
(
   SELECT Category, Name, COUNT(Name) Total,
            NTILE(20) OVER(PARTITION BY Category ORDER BY COUNT(Name) DESC) AS  'NTile'
   FROM #TEMP
   GROUP BY Category, Name
)
SELECT *
FROM SlicedData
WHERE NTile > 1

这基本上将您的数据按Category,Name 分组,按其他排序(不确定COUNT(Name) 是否真的是您想要的东西),然后将其分成 20 块,每块代表 5% 的数据分区.带有NTile = 1 的切片是前 5% 切片 - 从 CTE 中选择时忽略它。

见:

更多信息

【讨论】:

  • 这符合我的目的。太感谢了。我为您的帖子修复了查询中一些缺失的部分,使其开箱即用。
  • @Legend 我以为你想删除记录,而不仅仅是选择它们?
  • @TimRogers:当然。我刚刚使用此查询为我想要删除的名称创建了一个排除列表。我会尝试解决我的问题。
【解决方案2】:
select Category,name,CountTotal,RankSeq,(50*CountTotal)/100 from (
select Category,name,COUNT(*)
over (partition by Category,name ) as CountTotal,
ROW_NUMBER()
over (partition by Category,name order by Category) RankSeq from #TEMP
--group by Category,Name 
) temp
where RankSeq <= ((50*CountTotal)/100)
order by Category,Name,RankSeq

输出:

Category    name     CountTotal RankSeq     50*CountTotal)/100
A           Adam     4          1           2
A           Adam     4          2           2
A           John     6          1           3
A           John     6          2           3
A           John     6          3           3
A           Lisa     2          1           1
B           Lily     5          1           2
B           Lily     5          2           2
B           Ross     3          1           1
B           Tom      4          1           2
B           Tom      4          2           2

我希望这会有所帮助:)

【讨论】:

    【解决方案3】:
    ;WITH SlicedData AS
    (
       SELECT Category, Name, COUNT(Name) Total,
                **PERCENT_RANK() OVER(PARTITION BY Category ORDER BY COUNT(Name) DESC) * 100** AS  'Percent'
       FROM #TEMP
       GROUP BY Category, Name
    )
    SELECT *
    FROM SlicedData
    WHERE Percent < 5
    

    如果记录数少于您的磁贴数,NTile 将不起作用。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-03-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多