Мне просто интересно, есть ли лучший способ сделать это в SQL Server 2005.
По сути, я беру originator_id (число от 0 до 99) и «next_element» (на самом деле это просто последовательный счетчик от 1 до 999 999).
Мы пытаемся создать из них 6-значный код.
Originator_id умножается на миллион, а затем добавляется счетчик, давая нам число от 0 до 99 999 999.
Затем мы конвертируем это в строку «base 32» - фальшивую базу 32, где мы на самом деле просто используем 0-9 и AZ, но с некоторыми из более запутанных алфавитов, удаленных для ясности (I, O, S , Z).
Чтобы сделать это, мы просто делим число на степени 32, на каждом этапе используя результат, который мы получаем для каждой степени, в качестве индекса для символа из нашего массива выбранного символа.
Thus, an originator ID of 61 and NextCodeElement of 9 gives a code of '1T5JA9'
(61 * 1,000,000) + 9 = 61,000,009
61,000,009 div (5^32 = 33,554,432) = 1 = '1'
27,445,577 div (4^32 = 1,048,576) = 26 = 'T'
182,601 div (3^32 = 32,768) = 5 = '5'
18,761 div (2^32 = 1,024) = 18 = 'J'
329 div (1^32 = 32) = 10 = 'A'
9 div (0^32 = 1) = 9 = '9'
so my code is 1T5JA9
Раньше у меня работал этот алгоритм (в Delphi), но теперь мне действительно нужно иметь возможность воссоздать его в SQL Server 2005. Очевидно, у меня не совсем те функции, которые у меня есть в Delphi, но это мой взгляд на рутину. Это работает, и я могу генерировать коды (или восстанавливать коды обратно в их компоненты) просто отлично.
Но это выглядит немного скучно, и я не уверен, что хитрость выбора результата деления на int (то есть приведение его на самом деле) обязательно «правильная» - есть ли лучший подход SQLS на такие вещи?
CREATE procedure dummy_RP_CREATE_CODE @NextCodeElement int, @OriginatorID int,
@code varchar(6) output
as
begin
declare @raw_num int;
declare @bcelems char(32);
declare @chr int;
select @bcelems='0123456789ABCDEFGHJKLMNPQRTUVWXY';
select @code='';
-- add in the originator_id, scaled into place
select @raw_num = (@OriginatorID * 1000000) + @NextCodeElement;
-- now to convert this to a 6-char code
-- 5^32
select @chr = @raw_num / 33554432;
select @raw_num = @raw_num - (@chr * 33554432);
select @code = @code + SUBSTRING(@bcelems, 1 + @chr, 1);
-- 4^32
select @chr = @raw_num / 1048576;
select @raw_num = @raw_num - (@chr * 1048576);
select @code = @code + SUBSTRING(@bcelems, 1 + @chr, 1);
-- 3^32
select @chr = @raw_num / 32768;
select @raw_num = @raw_num - (@chr * 32768);
select @code = @code + SUBSTRING(@bcelems, 1 + @chr, 1);
-- 2^32
select @chr = @raw_num / 1024;
select @raw_num = @raw_num - (@chr * 1024);
select @code = @code + SUBSTRING(@bcelems, 1 + @chr, 1);
-- 1^32
select @chr = @raw_num / 32;
select @raw_num = @raw_num - (@chr * 32);
select @code = @code + SUBSTRING(@bcelems, 1 + @chr, 1);
-- 0^32
select @code = @code + SUBSTRING(@bcelems, 1 + @raw_num, 1);
-- that's it!
end;
Я не отчаянно беспокоюсь об оптимизации, если этот метод на самом деле не на порядок (или около того) хуже, чем любая другая альтернатива.
Код выполняется только небольшими пакетами, возможно, генерируя 20 или 30 кодов каждые 10 минут или около того. Из-за большого диапазона чисел я не хочу предварительно вычислять коды в огромной таблице (в любой конкретной установке будут использоваться только небольшие карманы всего диапазона кодов).
Тем не менее, я уверен, что, возможно, есть более аккуратный способ достижения того же результата - особенно те деления и вычитания.
Любая конструктивная критика, замечания или предложения приветствуются.