В SQL, как вы можете "группировать себя" в диапазонах?
Предположим, что у меня есть таблица с числовым столбцом (давайте назовем ее "оценка" ).
Я бы хотел создать таблицу подсчетов, которая показывает, сколько раз в каждом диапазоне появлялись баллы.
Например:
score range | number of occurrences
-------------------------------------
0-9 | 11
10-19 | 14
20-29 | 3
... | ...
В этом примере было 11 строк с оценками в диапазоне от 0 до 9, 14 строк с оценками в диапазоне от 10 до 19 и 3 строки с оценками в диапазоне 20-29.
Есть ли простой способ установить это? Что вы рекомендуете?
Ответы
Ответ 1
Ни один из самых высоких голосовых ответов не является правильным на SQLServer 2000. Возможно, они использовали другую версию.
Вот правильные версии обоих из них на 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
или
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
Ответ 2
Здесь я вижу ответы, которые не будут работать в синтаксисе SQL Server. Я бы использовал:
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
ИЗМЕНИТЬ: см. комментарии
Ответ 3
Альтернативный подход предполагает хранение диапазонов в таблице вместо их внедрения в запрос. В итоге вы получите таблицу, назовите ее "Диапазоны", которая выглядит так:
LowerLimit UpperLimit Range
0 9 '0-9'
10 19 '10-19'
20 29 '20-29'
30 39 '30-39'
И запрос выглядит следующим образом:
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
Это означает настройку таблицы, но ее можно будет легко сохранить при изменении требуемых диапазонов. Никаких изменений кода не требуется!
Ответ 4
В postgres (где ||
- оператор конкатенации строк):
select (score/10)*10 || '-' || (score/10)*10+9 as scorerange, count(*)
from scores
group by score/10
order by 1
дает:
scorerange | count
------------+-------
0-9 | 11
10-19 | 14
20-29 | 3
30-39 | 2
Ответ 5
Ответ Джеймса Каррана был самым кратким, на мой взгляд, но результат был неправильным. Для SQL Server простейшее утверждение выглядит следующим образом:
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
Это предполагает временную таблицу #Scores, которую я использовал для ее проверки, я просто заполнил 100 строк со случайным числом от 0 до 99.
Ответ 6
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
Ответ 7
select cast(score/10 as varchar) + '-' + cast(score/10+9 as varchar),
count(*)
from scores
group by score/10
Ответ 8
Это позволит вам не указывать диапазоны и быть агностиком SQL-сервера. Математика FTW!
SELECT CONCAT(range,'-',range+9), COUNT(range)
FROM (
SELECT
score - (score % 10) as range
FROM scores
)
Ответ 9
Я бы сделал это несколько иначе, чтобы он масштабировался без необходимости определять каждый случай:
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
Не тестировалось, но вы поняли...
Ответ 10
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)
Ответ 11
Поскольку сортируемый столбец (Range
) является строкой, вместо числовой сортировки используется сортировка строки/слова.
Пока строки имеют нули, чтобы отбросить длину номера, сортировка должна быть семантически корректной:
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
Если диапазон смешан, просто нанесите дополнительный ноль:
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
Ответ 12
Try
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;
Ответ 13
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
Убедитесь, что вы используете слово, отличное от "range", если вы находитесь в MySQL, или вы получите сообщение об ошибке для выполнения приведенного выше примера.
Ответ 14
Возможно, вы просите о том, чтобы такие вещи шли...
Конечно, вы вызовете полное сканирование таблицы для запросов, и если таблица, содержащая оценки, которые должны быть подсчитаны (скопления), велика, вам может понадобиться более эффективное решение, вы можете создать вторичную таблицу и использовать правила, например on insert
- вы можете изучить его.
Однако у всех двигателей РСУБД есть правила!