2008-10-24 16 views
148

Supongamos que tengo una tabla con una columna numérica (vamos a llamarlo "puntuación").En SQL, ¿cómo se puede "agrupar por" en rangos?

Me gustaría generar una tabla de recuentos, que muestre cuántas veces aparecieron puntajes en cada rango.

Por ejemplo:

 
score range | number of occurrences 
------------------------------------- 
    0-9  |  11 
    10-19  |  14 
    20-29  |   3 
    ...  |  ... 

En este ejemplo había 11 filas con puntajes en el rango de 0 a 9, 14 filas con puntajes en el rango de 10 a 19, y 3 filas con puntuaciones en el rango 20-29.

¿Hay una manera fácil de configurar esto? ¿Que recomiendas?

Respuesta

114

Ninguna de las las respuestas mejor votadas son correctas en SQLServer 2000. Quizás estaban usando una versión diferente.

Estas son las versiones correctas de los dos en SQL Server 2000.

select t.range as [score range], count(*) as [number of occurences] 
from (
    select case 
    when score between 0 and 9 then ' 0- 9' 
    when score between 10 and 19 then '10-19' 
    else '20-99' end as range 
    from scores) t 
group by t.range 

o

select t.range as [score range], count(*) as [number of occurences] 
from (
     select user_id, 
     case when score >= 0 and score< 10 then '0-9' 
     when score >= 10 and score< 20 then '10-19' 
     else '20-99' end as range 
    from scores) t 
group by t.range 
+0

¿Puedo agregar otra columna también (como recuentos de grupos). Supongamos que wana agrega la columna de becas para cada rango de puntaje. Intenté, pero no hacerlo bien –

+0

Buena respuesta @Ron Tuffin, sin embargo, cuando tienes dos rangos como 10-20, 100-200, entonces el orden no funciona. tendrías que pedir como 10-20, 100-200,20-30 etc. ¿Algún consejo para el pedido? –

+2

@ZoHas es un poco un truco pero esto funciona: orden por len (t.range), t.range –

5
create table scores (
    user_id int, 
    score int 
) 

select t.range as [score range], count(*) as [number of occurences] 
from (
     select user_id, 
     case when score >= 0 and score < 10 then '0-9' 
     case when score >= 10 and score < 20 then '10-19' 
     ... 
     else '90-99' as range 
    from scores) t 
group by t.range 
+0

Gracias! Intenté esto y la idea básica funciona bien, aunque la sintaxis que tuve que usar es ligeramente diferente. Solo se necesita la primera palabra clave "caso" y después de la última condición, antes del "rango", necesita la palabra clave "fin". Aparte de eso, funcionó muy bien, ¡gracias! – Hugh

5
select cast(score/10 as varchar) + '-' + cast(score/10+9 as varchar), 
     count(*) 
from scores 
group by score/10 
+0

Me gusta esto, pero debe arreglar los rangos fuera de la consulta si va a mostrarla. – tvanfosson

+0

En caso de que decida corregir su respuesta, necesita cambiar su puntaje/10 en la primera línea para ser (puntaje/10) * 10 para ambos, de lo contrario, obtendrá 3 - 12 en vez de 30-39, etc. De acuerdo con mi A continuación, puede agregar un pedido para obtener los resultados en el orden correcto. –

19

en Postgres (donde || es el operador de concatenación de cadenas):

select (score/10)*10 || '-' || (score/10)*10+9 as scorerange, count(*) 
from scores 
group by score/10 
order by 1 

da:

scorerange | count 
------------+------- 
0-9  | 11 
10-19  | 14 
20-29  |  3 
30-39  |  2 
-1

Tal vez usted está preguntando acerca de mantener este tipo de cosas que suceden ..

De la co Si llama mucho a una tabla completa para las consultas y si la tabla que contiene los puntajes que deben ser contados (agregaciones) es grande, es posible que desee una solución de mejor rendimiento, puede crear una tabla secundaria y usar reglas, como on insert - podrías mirarlo.

¡No todos los motores RDBMS tienen reglas!

1
declare @RangeWidth int 

set @RangeWidth = 10 

select 
    Floor(Score/@RangeWidth) as LowerBound, 
    Floor(Score/@RangeWidth)[email protected] as UpperBound, 
    Count(*) 
From 
    ScoreTable 
group by 
    Floor(Score/@RangeWidth) 
28

Veo aquí las respuestas que no funcionarán en la sintaxis de SQL Server. Me gustaría utilizar:

select t.range as [score range], count(*) as [number of occurences] 
from (
    select case 
    when score between 0 and 9 then ' 0-9 ' 
    when score between 10 and 19 then '10-19' 
    when score between 20 and 29 then '20-29' 
    ... 
    else '90-99' end as range 
    from scores) t 
group by t.range 

EDIT: ver los comentarios

+0

Posiblemente debido a la versión de SQLServer que estoy usando, pero para que funcione su ejemplo (pruebo las cosas antes de votarlas) tuve que mover 'puntuación' de después del 'caso' a después de cada 'cuándo'. –

+3

Tienes razón, y gracias por la corrección. Aparentemente cuando pones la variable después de la palabra clave 'caso', solo puedes hacer coincidencias exactas, no expresiones. Aprendo tanto de contestar preguntas como de preguntarlas. :-) –

9

respuesta de James Curran fue la más concisa en mi opinión, pero la salida no era correcta. Para SQL Server la declaración más simple es el siguiente:

SELECT 
    [score range] = CAST((Score/10)*10 AS VARCHAR) + ' - ' + CAST((Score/10)*10+9 AS VARCHAR), 
    [number of occurrences] = COUNT(*) 
FROM #Scores 
GROUP BY Score/10 
ORDER BY Score/10 

Esto supone una tabla temporal #Scores utilicé para probarlo, sólo pobladas 100 filas con un número aleatorio entre 0 y 99.

+1

Ah ... Existe la ventaja de realmente tomarse el tiempo para crear la mesa. (Utilicé una tabla existente con muy pocas filas en un rango demasiado pequeño) –

25

Un enfoque alternativo implicaría almacenar los rangos en una tabla, en lugar de incrustarlos en el consulta.Se podría terminar con una mesa, lo llaman Ranges, que se ve así:

LowerLimit UpperLimit Range 
0    9   '0-9' 
10   19   '10-19' 
20   29   '20-29' 
30   39   '30-39' 

Y una consulta que tiene el siguiente aspecto:

Select 
    Range as [Score Range], 
    Count(*) as [Number of Occurences] 
from 
    Ranges r inner join Scores s on s.Score between r.LowerLimit and r.UpperLimit 
group by Range 

Esto significa la creación de una mesa, pero sería ser fácil de mantener cuando cambien los rangos deseados. ¡No es necesario cambiar el código!

+0

Hice una pregunta sobre Administradores de bases de datos [Diseño de tabla para datos con dibujos utilizando rangos de cubos variables] (http://dba.stackexchange.com/questions/94275/table-design-for-patterned-data-using-variable -bucket-ranges) que no obtuvo una respuesta, pero terminé diseñando un sistema que tiene los rangos que mencionaste. Amo esta respuesta. – OmegaMan

0
select t.blah as [score range], count(*) as [number of occurences] 
from (
    select case 
    when score between 0 and 9 then ' 0-9 ' 
    when score between 10 and 19 then '10-19' 
    when score between 20 and 29 then '20-29' 
    ... 
    else '90-99' end as blah 
    from scores) t 
group by t.blah 

Asegúrese de que utiliza una palabra que no sea 'rango' si se encuentra en MySQL, o se obtendrá un error para ejecutar el ejemplo anterior.

1

Como la columna que se ordena (Range) es una cadena, se utiliza la clasificación de cadena/palabra en lugar de la clasificación numérica.

Mientras las cadenas tienen ceros para rellenar el número de longitudes de la clasificación todavía debe ser semánticamente correcta:

SELECT t.range AS ScoreRange, 
     COUNT(*) AS NumberOfOccurrences 
    FROM (SELECT CASE 
        WHEN score BETWEEN 0 AND 9 THEN '00-09' 
        WHEN score BETWEEN 10 AND 19 THEN '10-19' 
        ELSE '20-99' 
       END AS Range 
      FROM Scores) t 
GROUP BY t.Range 

Si el rango se mezcla, simplemente almohadilla extra cero:

SELECT t.range AS ScoreRange, 
     COUNT(*) AS NumberOfOccurrences 
    FROM (SELECT CASE 
        WHEN score BETWEEN 0 AND 9 THEN '000-009' 
        WHEN score BETWEEN 10 AND 19 THEN '010-019' 
        WHEN score BETWEEN 20 AND 99 THEN '020-099' 
        ELSE '100-999' 
       END AS Range 
      FROM Scores) t 
GROUP BY t.Range 
3

Haría esto un poco diferente para que se pueda escalar sin tener que definir cada caso:

select t.range as [score range], count(*) as [number of occurences] 
from (
    select FLOOR(score/10) as range 
    from scores) t 
group by t.range 

No probado, pero se obtiene la idea ...

2

Esto le permitirá no tener que especificar rangos, y debe ser SQL Server independiente. Matemáticas FTW!

SELECT CONCAT(range,'-',range+9), COUNT(range) 
FROM (
    SELECT 
    score - (score % 10) as range 
    FROM scores 
) 
1

Trate

SELECT (str(range) + "-" + str(range + 9)) AS [Score range], COUNT(score) AS [number of occurances] 
FROM (SELECT score, int(score/10) * 10 AS range FROM scoredata) 
GROUP BY range; 
+3

sería útil si pudiera agregar alguna explicación sobre cómo su consulta resuelve el problema. –