2011-09-28 16 views
11

que tienen una tabla de ejemplo de esta manera:¿Cómo selecciono TOP 5 PERCENT de cada grupo?

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 

me da el siguiente:

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

Ahora, ¿cómo seleccionar los TOP 5 PERCENT registros de cada categoría suponiendo que cada categoría tiene más de 100 registros (no se muestra en la tabla de muestra aquí)? Por ejemplo, en mi tabla real, se debe eliminar el registro John de A y Lily registro de B según el caso (de nuevo, no se presentó la tabla completa aquí) para obtener:

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

he estado tratando de use las cláusulas CTE sy PARTITION BY pero parece que no puede lograr lo que yo quiero. Quita el TOP 5 PERCENT del resultado general, pero no de cada categoría. ¿Alguna sugerencia?

+1

Puede ayudar de una manera pequeña: si tiene un conteo para un grupo, recuerde que el 5 por ciento sería "row_num <= (5 * count)/100" –

+0

@KierenJohnstone: +1 Gracias. Sé que podría tener que usar CROSS APPLY o algo similar, pero todavía tengo problemas. Se actualizará si lo resuelvo. – Legend

+1

¿Cuál es la salida deseada, por favor? Eliminar el 5 por ciento superior es muy poco comparado con un recuento de 6. Una fila (A, John) es 16%. – gbn

Respuesta

14

Puede usar un CTE (Common Table Expression) emparejado con la función de ventana NTILE - esto dividirá sus datos en tantos segmentos como necesite, p. Ej. en tu caso, en 20 rebanadas (cada 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 

Esto básicamente grupos de sus datos por Category,Name, órdenes por otra cosa (no estoy seguro si COUNT(Name) es realmente lo que usted quiere aquí), y luego rebanadas para arriba en 20 piezas, cada una representando el 5% de la partición de datos . La porción con NTile = 1 es la porción superior del 5%, simplemente ignore eso al seleccionar desde el CTE.

Ver:

para obtener más información

+1

Esto sirve para mi propósito. Un millón de gracias. Corregí tu publicación por algunas partes faltantes en la consulta para que salga de la caja. – Legend

+0

@Legend Creí que quería eliminar registros, no solo seleccionarlos. –

+0

@TimRogers: Claro. Acabo de hacer una lista de exclusión usando esta consulta para los nombres que quería eliminar. Trataré de arreglar mi pregunta. – Legend

1

Edición: He añadido la segunda solución

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 

Resultados:

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 

o

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 

Resultados:

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

+1 Gracias por su tiempo. No tengo una columna 'Id' pero supongo que puedo agregar eso fácilmente. – Legend

+0

Espero que estas nuevas soluciones sean mejores. –

+0

¡Dulce! Muchas gracias por tu tiempo :) Volveré a publicar la comparación de rendimiento si es posible en mi base de datos. – 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 

de salida:

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 

espero que esto ayude :)

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 no funcionará si el número de registros es menor que el número de baldosas.

Cuestiones relacionadas