Ответ 1
Попробуйте следующее:
SELECT
Letters = Char(64 + T.Num),
T.Col1,
T.Col2
FROM
dbo.YourTable T
;
Просто помните, что когда вы доберетесь до 27 (прошлое Z
), все станет интересным и не полезно.
Если вы хотите начать удвоение букв, как в ... X, Y, Z, AA, AB, AC, AD ...
, тогда это будет немного сложнее. Это работает во всех версиях SQL Server. Предложения SELECT
являются просто альтернативой оператору CASE (и на 2 символа короче, каждый).
SELECT
*,
LetterCode =
Coalesce((SELECT Char(65 + (N.Num - 475255) / 456976 % 26) WHERE N.Num >= 475255), '')
+ Coalesce((SELECT Char(65 + (N.Num - 18279) / 17576 % 26) WHERE N.Num >= 18279), '')
+ Coalesce((SELECT Char(65 + (N.Num - 703) / 676 % 26) WHERE N.Num >= 703), '')
+ Coalesce((SELECT Char(65 + (N.Num - 27) / 26 % 26) WHERE N.Num >= 27), '')
+ (SELECT Char(65 + (N.Num - 1) % 26))
FROM dbo.YourTable N
ORDER BY N.Num
;
Посмотрите демо-версию в SQL Fiddle
(Демо для SQL 2008 и выше, обратите внимание, что я использую Dense_Rank()
для имитации серии чисел)
Это будет работать от A
до ZZZZZ
, представляя значения 1
до 12356630
. Причина всего сумасшествия выше, чем простое выражение, потому что A
здесь не просто представляет 0
. Перед каждым порогом, когда последовательность переместится на следующую букву A
, добавленную к фронту, на самом деле есть скрытая, пустая цифра, но она не используется снова. Таким образом, длина 5 букв не составляет 26 ^ 5 комбинаций, это 26 + 26 ^ 2 + 26 ^ 3 + 26 ^ 4 + 26 ^ 5!
Потребовалось некоторое РЕАЛЬНОЕ мастеринг, чтобы заставить этот код работать правильно... Надеюсь, вы или кто-то это оценит! Это можно легко расширить до большего количества букв, просто добавив другое выражающее букву выражение с правильными значениями.
Так как кажется, что я сейчас квадрат посреди матча с доказательством мужественности, я провел некоторое тестирование производительности. Цикл WHILE
- это отличный способ сравнить производительность, потому что мой запрос предназначен для одновременного запуска всего набора строк. Для меня не имеет смысла запускать его миллион раз против одной строки (в основном, заставляя ее в виртуальную землю UDF), когда ее можно запустить один раз против миллиона строк, что является сценарием с использованием сценария, заданного OP для выполнения это против большого набора строк. Итак, здесь script для тестирования против 1 000 000 строк (для теста script требуется SQL Server 2005 и выше).
DECLARE
@Buffer varchar(16),
@Start datetime;
SET @Start = GetDate();
WITH A (N) AS (SELECT 1 FROM (VALUES (1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) A (N)),
B (N) AS (SELECT 1 FROM A, A X),
C (N) AS (SELECT 1 FROM B, B X),
D (N) AS (SELECT 1 FROM C, B X),
N (Num) AS (SELECT Row_Number() OVER (ORDER BY (SELECT 1)) FROM D)
SELECT @Buffer = dbo.HinkyBase26(N.Num)
FROM N
;
SELECT [HABO Elapsed Milliseconds] = DateDiff( ms, @Start, GetDate());
SET @Start = GetDate();
WITH A (N) AS (SELECT 1 FROM (VALUES (1), (1), (1), (1), (1), (1), (1), (1), (1), (1)) A (N)),
B (N) AS (SELECT 1 FROM A, A X),
C (N) AS (SELECT 1 FROM B, B X),
D (N) AS (SELECT 1 FROM C, B X),
N (Num) AS (SELECT Row_Number() OVER (ORDER BY (SELECT 1)) FROM D)
SELECT
@Buffer =
Coalesce((SELECT Char(65 + (N.Num - 475255) / 456976 % 26) WHERE N.Num >= 475255), '')
+ Coalesce((SELECT Char(65 + (N.Num - 18279) / 17576 % 26) WHERE N.Num >= 18279), '')
+ Coalesce((SELECT Char(65 + (N.Num - 703) / 676 % 26) WHERE N.Num >= 703), '')
+ Coalesce((SELECT Char(65 + (N.Num - 27) / 26 % 26) WHERE N.Num >= 27), '')
+ (SELECT Char(65 + (N.Num - 1) % 26))
FROM N
;
SELECT [ErikE Elapsed Milliseconds] = DateDiff( ms, @Start, GetDate());
И результаты:
UDF: 17093 ms
ErikE: 12056 ms
Исходный запрос
Я изначально сделал это "забавным" способом, создав 1 строку на каждую букву и сводную конкатенацию с использованием XML, но, хотя это было действительно весело, это оказалось медленным. Вот эта версия для потомков (SQL 2005 и выше требуется для Dense_Rank
, но будет работать в SQL 2000 для просто преобразования чисел в буквы):
WITH Ranks AS (
SELECT
Num = Dense_Rank() OVER (ORDER BY T.Sequence),
T.Col1,
T.Col2
FROM
dbo.YourTable T
)
SELECT
*,
LetterCode =
(
SELECT Char(65 + (R.Num - X.Low) / X.Div % 26)
FROM
(
SELECT 18279, 475254, 17576
UNION ALL SELECT 703, 18278, 676
UNION ALL SELECT 27, 702, 26
UNION ALL SELECT 1, 26, 1
) X (Low, High, Div)
WHERE R.Num >= X.Low
FOR XML PATH(''), TYPE
).value('.[1]', 'varchar(4)')
FROM Ranks R
ORDER BY R.Num
;