2011-09-28 59 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將無法正常工作。