transact - En SQL, ¿cómo se puede "agrupar por" en rangos?
transact sql pdf (14)
Supongamos que tengo una tabla con una columna numérica (digamos "puntuación").
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, hubo 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 puntajes en el rango de 20-29.
¿Hay alguna manera fácil de configurar esto? ¿Que recomiendas?
Como la columna que se está ordenando ( Range
) es una cadena, se utiliza la clasificación de cadena / palabra en lugar de la clasificación numérica.
Siempre que las cadenas tengan ceros para rellenar las longitudes de los números, la clasificación debería 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 es mixto, simplemente rellene un cero adicional:
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
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
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
)
Haría esto un poco diferente para que se escalara 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 entiendes la idea ...
La respuesta de James Curran fue la más concisa en mi opinión, pero la salida no fue correcta. Para SQL Server, la declaración más simple es la 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 que utilicé para probarla, acabo de completar 100 filas con un número aleatorio entre 0 y 99.
Ninguna de las respuestas más votadas es correcta en SQLServer 2000. Quizás estaban usando una versión diferente.
Aquí están las versiones correctas de ambos en SQLServer 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
Tal vez estás preguntando por mantener estas cosas en marcha ...
Por supuesto, invocará una exploración de 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
, puedes mirarlo.
Sin embargo, no todos los motores RDBMS tienen reglas.
Tratar
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;
Un enfoque alternativo implicaría almacenar los rangos en una tabla, en lugar de incrustarlos en la consulta. Usted terminaría con una tabla, llámala Rangos, 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 se ve así:
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 configurar una tabla, pero sería fácil de mantener cuando cambien los rangos deseados. ¡No es necesario cambiar el código!
Veo respuestas aquí que no funcionarán en la sintaxis de SQL Server. Yo usaría:
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
EDITAR: ver comentarios
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
declare @RangeWidth int
set @RangeWidth = 10
select
Floor(Score/@RangeWidth) as LowerBound,
Floor(Score/@RangeWidth)+@RangeWidth as UpperBound,
Count(*)
From
ScoreTable
group by
Floor(Score/@RangeWidth)
select cast(score/10 as varchar) + ''-'' + cast(score/10+9 as varchar),
count(*)
from scores
group by score/10
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 utilizar una palabra que no sea ''rango'' si está en MySQL, o obtendrá un error al ejecutar el ejemplo anterior.