2011-09-28 68 views
11

我有这样一个示例表:如何从每组中选择TOP 5 PERCENT?

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多条记录(这里没有在样品表中显示)?举例来说,在我实际的表,它应该(再次,我在这里没有显示完整的表)中删除从AJohn记录和Lily记录从B酌情获得:

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

我一直在努力使用CTE s和PARTITION BY子句,但似乎无法实现我想要的。它从整体结果中排除TOP 5 PERCENT,但不是从每个类别中排除。有什么建议么?

+1

可能对你有帮助 - 如果你有一个组的计数,请记住5%将是“row_num <=(5 * count)/ 100” –

+0

@KierenJohnstone:+1谢谢。我知道我可能不得不使用CROSS APPLY或类似的东西,但仍然有一些麻烦。如果我知道它会更新。 – Legend

+1

请问您需要输出什么?删除前5%的比例与6的比例非常小。一行(A,John)为16%。 – gbn

回答

14

您可以使用与NTILE窗口函数配对的CTE(公用表表达式) - 这会将您的数据切片成所需的切片数量,例如,在你的情况下,分成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中选择时忽略。

参见:

更多信息

+1

这服务于我的目的。太感谢了。我在查询中修复了一些缺失部分的帖子,以使其不再使用。 – Legend

+0

@Legend我以为你想删除记录,不只是选择它们? –

+0

@TimRogers:当然。我只是使用这个查询为我想删除的名称做了一个排除列表。我会尽力解决我的问题。 – Legend

1

编辑:我已经添加了第二个解决方案

SELECT b.Id 
     ,b.Category 
     ,b.Name 
     ,b.CategoryNameCount 
FROM 
(
     SELECT a.Id 
       ,a.Category 
       ,a.Name 
       ,COUNT(*)OVER(PARTITION BY a.Category, a.Name) CategoryNameCount 
       ,COUNT(*)OVER(PARTITION BY a.Category) CategoryCount 
     FROM #TEMP a 
) b 
WHERE b.CategoryCount*5.0/100 > b.CategoryCount*b.CategoryNameCount*1.0/100 
ORDER BY b.Category, b.CategoryNameCount DESC, b.Name 

结果:

Id   Category Name  CategoryNameCount 
----------- -------- ---------- ----------------- 
7   A  Adam  4 
8   A  Adam  4 
9   A  Adam  4 
10   A  Adam  4 
11   A  Lisa  2 
12   A  Lisa  2 
13   A  Bucky  1 
19   B  Tom  4 
20   B  Tom  4 
21   B  Tom  4 
22   B  Tom  4 
23   B  Ross  3 
24   B  Ross  3 
25   B  Ross  3 

SELECT b.Category, b.Name, b.CategoryNameCount 
FROM 
(
     SELECT 
       a.Category 
       ,a.Name 
       ,COUNT(*)OVER(PARTITION BY a.Category, a.Name) CategoryNameCount 
       ,COUNT(*)OVER(PARTITION BY a.Category) CategoryCount 
     FROM #TEMP a 
) b 
WHERE b.CategoryCount*5.0/100 > b.CategoryCount*b.CategoryNameCount*1.0/100 
GROUP BY b.Category, b.Name, b.CategoryNameCount 
ORDER BY b.Category, b.CategoryNameCount DESC, b.Name 

结果:

Category Name  CategoryNameCount 
-------- ---------- ----------------- 
A  Adam  4 
A  Lisa  2 
A  Bucky  1 
B  Tom  4 
B  Ross  3 
+0

+1谢谢你的时间。我没有'Id'专栏,但我猜想我可以很容易地添加。 – Legend

+0

我希望这个新的解决方案更好。 –

+0

甜!非常感谢您的时间:)如果可能的话,我会回发性能比较数据库。 – Legend

1
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 

我希望这有助于:)如果记录数少于你的瓦数

0
;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将无法正常工作。