周末看到SQL Server 大V瀟湘隱者的獲取下一個編碼字符串問題,本來作為以上博文的回復,也許回復內容長度超過其允許限制,無法提交。鑒於此,特記錄SQL Server實現過程,方便自己回顧和查閱。
我簡單總結編碼字符的規則如下:
1、5位長度,只能包含0-9數字字符和A-Z(大寫)字母字符,且第一位從A開始,最小編碼字符為A0000,最大編碼字符為ZZZZZ。
2、編碼字符是遞進增加的,例如:首個編碼是A0000,下一個是A0001,直到A9999,其下一個是B0000,直到B9999,其下一個是C0000,……,如果編碼是ZB999,下一個是ZC000,……,直到ZZZZZ。
具體的規則請參看
獲取下一個編碼字符串問題。
從規則入手分析規則2,編碼字符是遞進增加,間隔為1,也就是一個連續的整數序列,開始整數值和結束整數值固定的。如果編碼字符和整數數值之間實現某種相互轉換,那么這個問題也就解決啦。
從規則1來看,需要定義硬編碼實現0-9數字字符和A-Z(大寫)字母字符與相應10進制整數值的對應;還需要實現將一個編碼字符轉化為整數數值;當然也需要實現將一個整數數值轉化為一個編碼字符;目前僅是滿足5位長度,如果以后擴充到6位,更多位數的需求產生了呢,這個需要設置編碼字符的統一固定長度。以上四個方面我分別定義其對應的函數來實現:硬編碼字符映射表值函數、轉換編碼字符為整數數值的標量函數、轉換整數數值為編碼字符的標量函數和設置編碼字符固定長度的標量函數。
硬編碼字符映射表值函數
該函數的T-SQL代碼如下:
1 IF OBJECT_ID(N'dbo.ufn_GetCodeChars', 'TF') IS NOT NULL 2 BEGIN 3 DROP FUNCTION dbo.ufn_GetCodeChars; 4 END 5 GO 6 7 --================================== 8 -- 功能: 獲取編碼字符表函數 9 -- 說明: 編碼字符只包含0-9和A-Z這兩類字符 10 -- 將以上字符映射到對應十進制數值。 11 -- 作者: XXX 12 -- 創建: yyyy-MM-dd 13 -- 修改: yyyy-MM-dd XXX 修改內容描述 14 -- 調用: SELECT CodeChar, CodeValue FROM dbo.ufn_GetCodeChars(); 15 --================================== 16 CREATE FUNCTION dbo.ufn_GetCodeChars 17 ( 18 ) RETURNS @tblCodeChars TABLE ( 19 CodeChar CHAR(1) NOT NULL, 20 CodeValue TINYINT NOT NULL 21 ) 22 --$Encode$-- 23 AS 24 BEGIN 25 DECLARE 26 @intStartIndexID AS TINYINT, 27 @intEndIndexID AS TINYINT; 28 29 SELECT 30 @intStartIndexID = 0, 31 @intEndIndexID = 0; 32 33 -- 初始化0-9數字字符 34 SELECT 35 @intStartIndexID = ASCII('0'), 36 @intEndIndexID = ASCII('9'); 37 WHILE @intStartIndexID <= @intEndIndexID 38 BEGIN 39 INSERT INTO @tblCodeChars (CodeChar, CodeValue) 40 VALUES (CHAR(@intStartIndexID), 0); 41 42 SET @intStartIndexID = @intStartIndexID + 1; 43 END 44 45 -- 初始化A-Z字母字符 46 SELECT 47 @intStartIndexID = ASCII('A'), 48 @intEndIndexID = ASCII('Z'); 49 WHILE @intStartIndexID <= @intEndIndexID 50 BEGIN 51 INSERT INTO @tblCodeChars (CodeChar, CodeValue) 52 VALUES (CHAR(@intStartIndexID), 0); 53 54 SET @intStartIndexID = @intStartIndexID + 1; 55 END 56 57 -- 修改每個編碼字符對應的編碼值 58 ;WITH tCodeData AS ( 59 SELECT CodeChar, ROW_NUMBER() OVER (ORDER BY CodeChar ASC) AS RowNum 60 FROM @tblCodeChars 61 ) 62 63 UPDATE T2 64 SET T2.CodeValue = T.RowNum - 1 65 FROM tCodeData AS T 66 INNER JOIN @tblCodeChars AS T2 67 ON T.CodeChar = T2.CodeChar; 68 69 RETURN; 70 END 71 GO
設置編碼字符固定長度的標量函數
該函數的T-SQL代碼如下:
1 IF OBJECT_ID(N'dbo.ufn_GetCodeCharFixLength', 'FN') IS NOT NULL 2 BEGIN 3 DROP FUNCTION dbo.ufn_GetCodeCharFixLength; 4 END 5 GO 6 7 --================================== 8 -- 功能: 獲取編碼字符組合的固定長度 9 -- 說明: 如果轉化為int數據類,只能是8位整數,且字符串編碼的固定長度只能是8,僅支持5到8位編碼字符的組合 10 -- 作者: XXX 11 -- 創建: yyyy-MM-dd 12 -- 修改: yyyy-MM-dd XXX 修改內容描述 13 -- 調用: SELECT dbo.ufn_GetCodeCharFixLength(); 14 --================================== 15 CREATE FUNCTION ufn_GetCodeCharFixLength 16 ( 17 ) RETURNS TINYINT 18 --$Encode$-- 19 AS 20 BEGIN 21 RETURN CAST(5 AS TINYINT); 22 END 23 GO
轉換編碼字符為整數數值的標量函數
該函數的T-SQL代碼如下:
1 IF OBJECT_ID(N'dbo.ufn_GetCodeIntegerValue', 'FN') IS NOT NULL 2 BEGIN 3 DROP FUNCTION dbo.ufn_GetCodeIntegerValue; 4 END 5 GO 6 7 --================================== 8 -- 功能: 通過編碼字符獲取其對應的整數數值 9 -- 說明: 具體實現闡述 10 -- 作者: XXX 11 -- 創建: yyyy-MM-dd 12 -- 修改: yyyy-MM-dd XXX 修改內容描述 13 -- 調用: SELECT dbo.ufn_GetCodeIntegerValue('A0000') 14 --================================== 15 CREATE FUNCTION dbo.ufn_GetCodeIntegerValue 16 ( 17 @chCodeChar CHAR(5) 18 19 ) RETURNS INT 20 --$Encode$-- 21 AS 22 BEGIN 23 SET @chCodeChar = ISNULL(@chCodeChar, ''); 24 SET @chCodeChar = UPPER(@chCodeChar); 25 DECLARE @intCodeIntegerValue AS INT; 26 SET @intCodeIntegerValue = 0; 27 28 DECLARE @tintFixLength AS TINYINT; 29 SET @tintFixLength =dbo.ufn_GetCodeCharFixLength(); 30 31 DECLARE @tintLength AS TINYINT; 32 SET @tintLength = LEN(@chCodeChar); 33 34 IF @tintLength <= (@tintFixLength - 1) OR @tintLength >= (@tintFixLength + 1) 35 BEGIN 36 RETURN @intCodeIntegerValue; 37 END 38 39 DECLARE @tblCodeChars TABLE( 40 CodeChar CHAR(1) NOT NULL, 41 CodeValue TINYINT NOT NULL 42 ); 43 44 INSERT INTO @tblCodeChars (CodeChar, CodeValue) 45 SELECT CodeChar, CodeValue 46 FROM dbo.ufn_GetCodeChars(); 47 48 WHILE @tintLength >= 1 49 BEGIN 50 SELECT @intCodeIntegerValue = @intCodeIntegerValue + CodeValue * POWER(10, @tintFixLength - @tintLength) 51 FROM @tblCodeChars 52 WHERE CodeChar = SUBSTRING(@chCodeChar, @tintLength, 1); 53 54 SET @tintLength = @tintLength - 1; 55 END 56 57 RETURN @intCodeIntegerValue; 58 END 59 GO
轉換為整數數值為編碼字符的標量函數
該函數的T-SQL代碼如下:
1 IF OBJECT_ID(N'dbo.ufn_GetCodeChar', 'FN') IS NOT NULL 2 BEGIN 3 DROP FUNCTION dbo.ufn_GetCodeChar; 4 END 5 GO 6 7 --================================== 8 -- 功能: 通過編碼整數值獲取對應的編碼字符 9 -- 說明: 具體實現闡述 10 -- 作者: XXX 11 -- 創建: yyyy-MM-dd 12 -- 修改: yyyy-MM-dd XXX 修改內容描述 13 --================================== 14 CREATE FUNCTION dbo.ufn_GetCodeChar 15 ( 16 @intCodeIntegerValue INT 17 ) RETURNS CHAR(5) 18 --$Encode$-- 19 AS 20 BEGIN 21 SET @intCodeIntegerValue = ISNULL(@intCodeIntegerValue, 0); 22 DECLARE @chCodeChar AS VARCHAR(9); 23 SET @chCodeChar = ''; 24 25 DECLARE @tintFixLength AS TINYINT; 26 SET @tintFixLength =dbo.ufn_GetCodeCharFixLength(); 27 28 IF @intCodeIntegerValue NOT BETWEEN dbo.ufn_GetCodeIntegerValue('A' + REPLICATE('0', @tintFixLength - 1)) AND dbo.ufn_GetCodeIntegerValue(REPLICATE('Z', @tintFixLength)) 29 BEGIN 30 RETURN @chCodeChar; 31 END 32 33 DECLARE @tblCodeChars TABLE( 34 CodeChar CHAR(1) NOT NULL, 35 CodeValue TINYINT NOT NULL 36 ); 37 38 INSERT INTO @tblCodeChars (CodeChar, CodeValue) 39 SELECT CodeChar , CodeValue 40 FROM dbo.ufn_GetCodeChars(); 41 42 DECLARE @tintPerCodeValue TINYINT; 43 SET @tintPerCodeValue = 0; 44 45 WHILE @tintFixLength >= 1 46 BEGIN 47 SET @tintPerCodeValue = @intCodeIntegerValue / POWER(10, @tintFixLength - 1); 48 49 SELECT TOP 1 @chCodeChar = @chCodeChar + CodeChar, @tintPerCodeValue = CodeValue 50 FROM @tblCodeChars 51 WHERE CodeValue <= @tintPerCodeValue 52 ORDER BY CodeValue DESC; 53 54 SET @intCodeIntegerValue = @intCodeIntegerValue - @tintPerCodeValue * POWER(10, @tintFixLength - 1); 55 56 SET @tintFixLength = @tintFixLength - 1; 57 END 58 59 RETURN @chCodeChar; 60 END 61 GO 62
測試實現效果
測試的T-SQL代碼如下:
1 DECLARE @chCodeChar AS CHAR(8); 2 SET @chCodeChar = 'A0000'; 3 DECLARE @intValue AS INT; 4 SET @intValue = dbo.ufn_GetCodeIntegerValue(@chCodeChar); 5 6 SELECT @chCodeChar AS CurrentCodeChar, @intValue AS CurrentCodeIntegerValue, dbo.ufn_GetCodeChar(@intValue + 1) AS NextCodeChar; 7 GO 8 9 DECLARE @chCodeChar AS CHAR(8); 10 SET @chCodeChar = 'ZZZZY'; 11 DECLARE @intValue AS INT; 12 SET @intValue = dbo.ufn_GetCodeIntegerValue(@chCodeChar); 13 14 SELECT @chCodeChar AS CurrentCodeChar, @intValue AS CurrentCodeIntegerValue, dbo.ufn_GetCodeChar(@intValue + 1) AS NextCodeChar; 15 GO
執行后的查詢結果如下:
實現方案的限制
該實現方案只能實現編碼字符長度最多為8位的編碼字符與整數數值的相互轉換。如果要要實現編碼字符固定長度更長的(比如編碼字符固定長度為6位、7位或8位)功能,必須要修改三個函數,具體的修改處如下圖:
以上圖紅色矩形框標注的地方,務必要一致才可以的。如果全部更改為6,那就滿足編碼字符固定長度為6位的實現;也可以修改為7或8,最多只能修改為8。
博友如有其他更好的解決方案,也請不吝賜教,萬分感謝。
