在项目中,大家可能都遇到过,需要把十进制转换为其他进制的情况,google上一搜,已经有很多2进制、8进制、16进制和十进制的转换方法。但是在一些项目中,这些可能无法满足要求,可能需要17、18甚至是32、36进制和十进制的转换,那么我们应该怎么办呢?不可能为每一种进制都去写一个函数,那样可不是明智之举。所以我这里提供一个十进制与N进制之间的互转函数(N<=32)。
N进制函数
1、准备工作
在写N进制函数之前,需要有一个用于存储表示N进制字符的基础表,这里我用一个表函数表示:
CREATE FUNCTION xavi . fn_NSystemTable () RETURNS @temp TABLE ( id SMALLINT IDENTITY , [Char] CHAR ( 1 ), [Ascii] SMALLINT ) AS BEGIN DECLARE @ignoreAscii TABLE ( [Ascii] SMALLINT ) DECLARE @i INT SET @i = 58 WHILE ( @i <= 64 ) BEGIN INSERT INTO @ignoreAscii VALUES ( @i ) SET @i = @i + 1 END SET @i = 0 WHILE ( @i < 43 ) BEGIN IF NOT EXISTS ( SELECT 1 FROM @ignoreAscii WHERE [Ascii] = @i + 48 ) BEGIN INSERT INTO @temp VALUES ( CHAR ( @i + 48 ), @i + 48 ) END SET @i = @i + 1 END RETURN END
2、十进制转换为N进制
CREATE FUNCTION xavi . fn_DecimalToNSystem ( @bigInt BIGINT , @n TINYINT ) RETURNS VARCHAR ( 100 ) AS BEGIN Declare @result VARCHAR ( 100 ), @mode INT , @remainder INT , @iRet CHAR ( 1 ) SELECT @mode = @bigInt , @result = '' WHILE ( 1 = 1 ) BEGIN IF ( @bigInt = 0 OR @n = 0 OR @n = 1 ) BEGIN SET @result = CONVERT ( VARCHAR ( 100 ), @bigInt ) BREAK END IF ( @mode = 0 ) BEGIN BREAK END SET @remainder = @mode % @n SET @mode = @mode / @n SELECT @iRet = [Char] FROM xavi . fn_NSystemTable () ns WHERE ns . id = @remainder + 1 SET @result = @iRet + @result END RETURN @result END
3、N进制转换为十进制
CREATE FUNCTION xavi . fn_NSystemToDecimal ( @nSys VARCHAR ( 100 ), @n TINYINT ) RETURNS BIGINT AS BEGIN Declare @result int , @iPos int , @iTmp int Select @result = 0 , @iPos = 0 While ( @iPos < Len ( @nSys )) BEGIN SELECT @iTmp = ns . id - 1 FROM xavi . fn_NSystemTable () ns WHERE ns . [Char] = SUBSTRING ( @nSys , LEN ( @nSys ) - @iPos , 1 ) Set @result = @result + @iTmp * POWER ( CAST ( @n AS BIGINT ), cast ( @iPos AS BIGINT )) Set @iPos = @iPos + 1 END RETURN @result END
注意:目前测试下来对于最高进制(36进制),最多支持13位,但是我想这也足够了,因为36进制所能表示的范围远比10进制的13位数大得多,0<=y<=36 * 36 12 + 36 * 36 11 +......+ 36 * 36 1 + 36。所以一个N进制来说能表示的范围应该为:0<=y<=N * N x + N * N x-1 +......+ N * N 1 + N。
如何使用
那么我们应该怎么使用这些函数呢,这里举一个自增36进制字段的表的例子。
首先创建一个表:
CREATE TABLE xavi . tb_Test ( ID CHAR ( 10 ) PRIMARY KEY , Account VARCHAR ( 20 ), [Name] NVARCHAR ( 10 ) )
然后创建一个触发器:
CREATE TRIGGER xavi . tr_TestInsert ON xavi . tb_Test INSTEAD OF INSERT AS SET NOCOUNT ON DECLARE @maxID BIGINT , @n TINYINT , @nSystemChar VARCHAR ( 10 ) SET @n = 36 SELECT @maxID = ISNULL ( MAX ( xavi . fn_NSystemToDecimal ( ID , @n )), 0 ) FROM xavi . tb_Test SET @nSystemChar = xavi . fn_DecimalToNSystem ( @maxID + 1 , @n ) INSERT INTO xavi . tb_Test ( ID , Account , [Name] ) SELECT REPLICATE ( '0' , 10 - LEN ( @nSystemChar )) + @nSystemChar , Account , [Name] FROM INSERTED
接着往这个表里插入100条册数数据:
DECLARE @i INT SET @i = 1 WHILE ( @i <= 100 ) BEGIN INSERT INTO xavi . tb_Test VALUES ( @i ,LEFT( REPLACE ( CONVERT ( VARCHAR ( 100 ), NEWID ()), '-' , '' ), 10 ),LEFT( REPLACE ( CONVERT ( VARCHAR ( 100 ), NEWID ()), '-' , '' ), 10 )) SET @i = @i + 1 END
执行看下表里的数据,可以得到如下图的结果:
从这个结果应该可以观察到,ID这一列已经是36进制的表示形式了。
扩展用法
有了这个N进制函数,那么我们再生产一些唯一编码、订单号等一些编码时,就可以用更少的位数,表示更大的范围。