sql - 如何从每组中选择前 5%?

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

我有一个像这样的示例表:

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

现在,假设每个类别有超过 100 条记录(此处的示例表中未显示),如何从每个类别中选择前 5% 记录?例如,在我的实际表中,它应该从 A 中删除 John 记录,并从 B 中删除 Lily 记录,如下所示适当的(同样,我没有在这里显示完整的表格)得到:

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

我一直在尝试使用CTEPARTITION BY子句,但似乎无法实现我想要的。它从总体结果中删除了前 5%,但没有从每个类别中删除。有什么建议吗?

最佳答案

您可以使用 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 block ,每 block 代表数据分区的 5%。 NTile = 1 的切片是前 5% 的切片 - 从 CTE 中选择时忽略它即可。

参见:

了解更多信息

关于sql - 如何从每组中选择前 5%?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7579916/

相关文章:

sql - SCOPE_IDENTITY,多次插入,如果当前插入未插入,则携带先前插入的值

sql - 从不同的数据库postgresql复制表

php - SQL将多行转换为可变长度的单行

java - SQL 断言 - 在单元测试中比较两个 SQL 查询

sql - 选择重叠的时间范围

sql-server - 你能在你的 SQL if 语句的 block 中只添加一条注释吗?

SQL Server select where sql_variant equal 不起作用?

sql-server - 以不同的名称附加带有全文目录的数据库?

sql - 使用 sp_executesql 在不执行查询的情况下计算查询结果

sql-server - 选择SQL Server中的所有空表