#sql #tsql #ranking-functions
#sql #tsql #ранжирование-функции
Вопрос:
Учитывая набор данных типа:
id Name
1 Aaa
2 Aab
3 AAc
…
999 Zzz
Я хотел бы создать виртуальные папки, которые разделяют его по начальной букве. Например, я хотел бы передать 7
в функцию и получить 7 папок, например:
- A-C
- D-F
- G-H
- I-M
- N-Q
- R-S
- T-Z
… каждая из которых содержит соответствующее значение (например, T-Z
будет содержать Zzz
). Я выяснил, что могу использовать NTILE()
, чтобы приблизиться к желаемому результату:
WITH Ntiles(Name, Ntile) AS (
SELECT Name, NTILE(7) OVER(ORDER BY Name) FROM #Projects
)
SELECT MIN(LEFT(N.Name, 1)) '-' MAX(LEFT(N.Name, 1))
FROM Ntiles N
GROUP BY Ntile
Чтобы добавить нужные значения, я выполняю еще два объединения:
WITH Ntiles(Name, Ntile) AS (
SELECT Name, NTILE(7) OVER(ORDER BY Name) FROM #Projects
) SELECT P.Name, (
SELECT MIN(LEFT(N1.Name, 1)) '-' MAX(LEFT(N1.Name, 1)) FROM Ntiles N1
WHERE N1.Ntile = N2.Ntile
GROUP BY N1.Ntile
) FROM #Projects P INNER JOIN Ntiles N2 ON P.Name = N2.Name
… что кажется немного неэффективным.
Но это также неправильно: происходит перекрытие (например, буква P
появляется одновременно в J-P
и P-T
).
Я на правильном пути? Есть ли более эффективный способ? Как мне предотвратить перекрытие?
Ответ №1:
Если вам просто нужно разделить начальный список букв на довольно равные группы, то вам, вероятно, следует сделать это примерно так, как вы делали это до сих пор, но в несколько ином порядке:
-
Получить все начальные буквы, которые есть в таблице.
-
Завершите результирующий список.
-
Сгруппируйте результирующий набор NTILEd.
WITH letters AS (
SELECT
Letter = LEFT(Name, 1)
FROM #Projects
GROUP BY LEFT(Name, 1)
),
ntiled AS (
SELECT
Letter,
Folder = NTILE(7) OVER (ORDER BY Letter)
FROM letters
)
SELECT
Folder,
FolderCaption = MIN(Letter) '-' MAX(Letter)
FROM ntiled
GROUP BY Folder
Комментарии:
1. Ну, группы букв должны быть взвешены на основе исходных данных, а не на основе букв.
2. На мой взгляд, было бы справедливо, если бы вы отозвали принятие моего ответа и добавили свою последнюю часть спецификации к вашему вопросу. Я бы постарался найти правильное решение для вас, или, может быть, кому-то другому удалось бы сделать это раньше меня. В качестве альтернативы вы могли бы задать новый вопрос, конечно, если это вас устраивает.