Предположим, у меня есть таблица с числовым столбцом (назовем ее «оценка»).
Я хотел бы создать таблицу подсчетов, которая показывает, сколько раз баллы появлялись в каждом диапазоне.
Например:
score range | number of occurrences ------------------------------------- 0-9 | 11 10-19 | 14 20-29 | 3 ... | ...
В этом примере было 11 строк с оценками от 0 до 9, 14 строк с оценками от 10 до 19 и 3 строки с оценками от 20 до 29.
Есть ли простой способ настроить это? Что вы порекомендуете?


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
select cast(score/10 as varchar) + '-' + cast(score/10+9 as varchar),
count(*)
from scores
group by score/10
Мне это нравится, но вам нужно исправить диапазоны вне запроса, если вы собираетесь его отображать.
Если вы решите исправить свой ответ, вам нужно изменить свой счет / 10 в первой строке на (балл / 10) * 10 для них обоих, иначе вы получите 3-12 вместо 30-39 и т. д. Согласно моему сообщению ниже вы можете добавить заказ, чтобы получить результаты в правильном порядке.
В 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
Возможно, вы спрашиваете о том, чтобы такие вещи продолжались ...
Конечно, вы вызовете полное сканирование таблицы для запросов, и если таблица, содержащая оценки, которые необходимо подсчитать (агрегации), велика, вам может потребоваться более эффективное решение, вы можете создать дополнительную таблицу и использовать правила, такие как on insert - можно посмотреть.
Однако не все СУБД имеют правила!
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)
Я вижу здесь ответы, которые не будут работать в синтаксисе 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
Обновлено: см. Комментарии
Возможно, это из-за версии SQLServer, которую я использую, но для того, чтобы ваш пример заработал (я тестирую вещи, прежде чем проголосовать за них), мне пришлось переместить «счет» после «case» на после каждого «когда».
Вы правы, и спасибо за исправление. Очевидно, когда вы помещаете переменную после ключевого слова case, вы можете делать только точные совпадения, а не выражения. Я узнаю столько же, отвечая на вопросы, как и задавая их. :-)
На мой взгляд, ответ Джеймса Каррана был самым кратким, но результат был неправильным. Для 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.
Ах ... Есть преимущество в том, чтобы потратить время на создание таблицы. (Я использовал существующую таблицу со слишком небольшим количеством строк в слишком маленьком диапазоне)
Ни один из ответов с наибольшим количеством голосов не является правильным для SQL Server 2000. Возможно, они использовали другую версию.
Вот их правильные версии на 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
или же
select t.range as [score range], count(*) as [number of occurrences]
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
Могу ли я объединить и другой столбец (например, количество групп). скажем, я хочу объединить столбец стипендий для каждого диапазона баллов. Я пробовал, но не понял
Хороший ответ @Ron Tuffin, однако, когда у вас есть два диапазона, например 10-20, 100-200, порядок не работает. у вас был бы заказ как 10-20, 100-200,20-30 и т. д. Любые подсказки для заказа?
@ZoHas это что-то вроде взлома, но это работает: order by len (t.range), t.range
Лучший ответ на stackoverflow.com/questions/14730380/…
что, если у вас больше тысячи групп? просто добавит 1000 когда заявления?
Если у вас все еще есть проблемы с синтаксисом, проверьте этот ответ: dba.stackexchange.com/questions/22491/…
как насчет 0 счетчиков для любого определенного диапазона?
Альтернативный подход предполагает сохранение диапазонов в таблице вместо их встраивания в запрос. У вас получится таблица, назовите ее «Диапазоны», которая выглядит так:
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
Это означает создание таблицы, но ее будет легко поддерживать при изменении желаемых диапазонов. Изменения кода не требуется!
Я задал вопрос об администраторах базы данных Дизайн таблиц для шаблонных данных с использованием переменных диапазонов сегментов, на который не получил ответа, но в итоге я разработал систему, которая имеет указанные вами диапазоны. Люблю этот ответ.
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
Убедитесь, что вы используете слово, отличное от «диапазон», если вы работаете в MySQL, иначе вы получите ошибку при выполнении приведенного выше примера.
Поскольку сортируемый столбец (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
Я бы сделал это немного по-другому, чтобы он масштабировался без необходимости определять каждый случай:
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
Не проверял, но идею вы поняли ...
Это позволит вам не указывать диапазоны и не зависит от SQL-сервера. Математика FTW!
SELECT CONCAT(range,'-',range+9), COUNT(range)
FROM (
SELECT
score - (score % 10) as range
FROM scores
)
Пытаться
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;
Было бы полезно, если бы вы могли добавить некоторые пояснения о том, как ваш запрос решает проблему.
select t.range as score, count(*) as Count
from (
select UserId,
case when isnull(score ,0) >= 0 and isnull(score ,0)< 5 then '0-5'
when isnull(score ,0) >= 5 and isnull(score ,0)< 10 then '5-10'
when isnull(score ,0) >= 10 and isnull(score ,0)< 15 then '10-15'
when isnull(score ,0) >= 15 and isnull(score ,0)< 20 then '15-20'
else ' 20+' end as range
,case when isnull(score ,0) >= 0 and isnull(score ,0)< 5 then 1
when isnull(score ,0) >= 5 and isnull(score ,0)< 10 then 2
when isnull(score ,0) >= 10 and isnull(score ,0)< 15 then 3
when isnull(score ,0) >= 15 and isnull(score ,0)< 20 then 4
else 5 end as pd
from score table
) t
group by t.range,pd order by pd
Я здесь, потому что у меня есть аналогичный вопрос, но я нахожу короткие ответы неправильными, а тот, в котором постоянно повторяется «случай, когда» требует много работы, и то, что я вижу что-то повторяющееся в моем коде, режет мне глаза. Итак, вот решение
SELECT --MIN(score), MAX(score),
[score range] = CAST(ROUND(score-5,-1)AS VARCHAR) + ' - ' + CAST((ROUND(score-5,-1)+10)AS VARCHAR),
[number of occurrences] = COUNT(*)
FROM order
GROUP BY CAST(ROUND(score-5,-1)AS VARCHAR) + ' - ' + CAST((ROUND(score-5,-1)+10)AS VARCHAR)
ORDER BY MIN(score)
Спасибо! Я попробовал это, и основная идея отлично работает, хотя синтаксис, который мне пришлось использовать, немного отличается. Требуется только первое ключевое слово "case", а затем после последнего условия перед "as range" необходимо ключевое слово "end". Помимо этого, отлично поработал - спасибо!