我在SQL Server获取下一个编码字符实现的博文中,虽然实现了这个问题,但是感觉维护起来比较麻烦,例如如果调整编码字符串的固定长度,就需要变更三个函数,这样的为何成本确实比较大。面向对象编程很重视讲究开放封闭原则,我认为数据库对象特别函数、存储等对象也要尽量封装成实现单一功能,维护起来简单,也方便后续人员的维护,便利别人也是便利自己。
针对编码字符串的规则,继续延伸总结如下:
1、第一个字符必须是字母A-Z中任意一个字符,其长度可以为1位、2位、3位,……,6位、7位、8位、……;
2、编码字符串满足递进增加。如果编码字符串长度为5位,则编码字符串是A0000,下一个是A0001,直到A9999,其下一个是B0000,直到B9999,其下一个是C0000,……,如果编码是ZB999,下一个是ZC000,……,直到ZZZZZ;无论其长度为1位到N位中的任何一个长度,均要满足递进增加。
我的实现方案是将编码字符串中每一位的字符对应的整数值加工转换为一个10进制整数值,再实现将一个将整数值转换为一个编码字符串,也就是将编码字符串和整数值相互转换。由于方案中是使用了POWER函数,受限其结果值的长度,此方案最多支持8位长度的编码字符串和整数值间的相互转换。
通过以上规则分析,不同长度的编码字符均对应不同的最小和最大编码字符串,我的方案是编码字符串和整数相互转换,也就是不同长度的编码字符对应的不同的相对应的最小和最大整数值,也就是不同的长度对应的不同的整数范围,且这些不同长度对应的整数范围之间是不连续,比如长度1位对应的最大整数值和长度2位对应的最小整数不是相等。
方案的具体实现思路如下:
1、由于使用到数字0-9和字母[A-Z](大写)字母这两类字符,将以上字符和10进制整数值之间的硬编码对应,将其封装为一个表值函数。
2、实现将一个编码字符串转换为一个整数值,此功能封装为一个标量函数。
3、针对不同长度的编码字符串对应的不同最小和最大整数,这个功能也可以检查一个整数值是否在满足转换为一个编码字符串,也封装为一个表值函数。
4、将一个整数值转换为一个编码字符串,此功能也封装在一个标量函数中。
针对上实现方案主要重构的部分包括函数命名,针对表(表值或内联表)函数和标量函数尽量通过命名标识符来区分。还解决了编码字符串穿不同长度的实现来强制修改所涉及三个函数的缺陷。为了讲解方便,我根据该方案的实现思路的4个步骤,将其对应的部分分为:字符映射表值函数、获取编码字符串整数值标量函数、获取编码字符串长度和整数值范围表函数和获取整数值对应的编码字符串变量函数。
字符映射表值函数
该函数的T-SQL代码如下:
IF OBJECT_ID(N'dbo.ufn_GetCharTable', 'TF') IS NOT NULL
BEGIN
DROP FUNCTION dbo.ufn_GetCharTable;
END
GO --==================================
-- 功能: 获取字符映射表表值函数
-- 说明: 编码字符只包含0-9和A-Z这两类字符
-- 将以上字符映射到对应十进制数值。
-- 作者: XXX
-- 创建: yyyy-MM-dd
-- 修改: yyyy-MM-dd XXX 修改内容描述
-- 调用: SELECT CodeChar, CodeValue FROM dbo.ufn_GetCharTable();
--==================================
CREATE FUNCTION dbo.ufn_GetCharTable
(
) RETURNS @tblChar TABLE (
[Char] CHAR() NOT NULL,
Value TINYINT NOT NULL
)
--$Encode$--
AS
BEGIN
DECLARE
@intStartIndexID AS TINYINT,
@intEndIndexID AS TINYINT; SELECT
@intStartIndexID = ,
@intEndIndexID = ; -- 初始化0-9数字字符
SELECT
@intStartIndexID = ASCII(''),
@intEndIndexID = ASCII('');
WHILE @intStartIndexID <= @intEndIndexID
BEGIN
INSERT INTO @tblChar ([Char], Value)
VALUES (CHAR(@intStartIndexID), ); SET @intStartIndexID = @intStartIndexID + ;
END -- 初始化A-Z字母字符
SELECT
@intStartIndexID = ASCII('A'),
@intEndIndexID = ASCII('Z');
WHILE @intStartIndexID <= @intEndIndexID
BEGIN
INSERT INTO @tblChar ([Char], Value)
VALUES (CHAR(@intStartIndexID), ); SET @intStartIndexID = @intStartIndexID + ;
END -- 修改每个字符对应的10进制整数值
;WITH tCodeData AS (
SELECT [Char], ROW_NUMBER() OVER (ORDER BY [Char] ASC) AS RowNum
FROM @tblChar
) UPDATE T2
SET T2.Value = T.RowNum -
FROM tCodeData AS T
INNER JOIN @tblChar AS T2
ON T.[Char] = T2.[Char]; RETURN;
END
GO
获取编码字符串整数值标量函数
该函数的T-SQL代码如下:
IF OBJECT_ID(N'dbo.ufn_GetCodeCharsValue', 'FN') IS NOT NULL
BEGIN
DROP FUNCTION dbo.ufn_GetCodeCharsValue;
END
GO --==================================
-- 功能: 获取编码字符串对应的10进制整数数值
-- 说明: 具体实现阐述
-- 作者: XXX
-- 创建: yyyy-MM-dd
-- 修改: yyyy-MM-dd XXX 修改内容描述
-- 调用: SELECT dbo.ufn_GetCodeIntegerValue('A0000')
--==================================
CREATE FUNCTION dbo.ufn_GetCodeCharsValue
(
@chvCodeChars VARCHAR() -- 编码字符串 ) RETURNS INT
--$Encode$--
AS
BEGIN
SET @chvCodeChars = ISNULL(@chvCodeChars, '');
SET @chvCodeChars = UPPER(@chvCodeChars); DECLARE @intCodeCharsValue AS BIGINT;
SET @intCodeCharsValue = ; DECLARE @tintLength AS TINYINT;
SET @tintLength = LEN(@chvCodeChars); IF @tintLength =
BEGIN
RETURN @intCodeCharsValue;
END DECLARE @tblChar TABLE(
[Char] CHAR() NOT NULL,
Value TINYINT NOT NULL
); INSERT INTO @tblChar ([Char], Value)
SELECT [Char], Value
FROM dbo.ufn_GetCharTable(); -- 编码字符串的首字母必须是A-Z字母字符的逻辑检查
IF NOT EXISTS (SELECT FROM @tblChar WHERE [Char] = SUBSTRING(@chvCodeChars, , ) AND Value >= )
BEGIN
RETURN @intCodeCharsValue;
END WHILE @tintLength >=
BEGIN
SELECT @intCodeCharsValue = @intCodeCharsValue + CAST(Value * POWER(, @tintLength - ) AS BIGINT)
FROM @tblChar
WHERE [Char] = SUBSTRING(@chvCodeChars, , ); SET @chvCodeChars = STUFF(@chvCodeChars, , , ''); SET @tintLength = @tintLength - ;
END RETURN @intCodeCharsValue;
END
GO
获取编码字符串长度和整数值范围表函数
该函数的T-SQL代码如下:
IF OBJECT_ID(N'dbo.ufn_GetCodeCharsValueTable', 'TF') IS NOT NULL
BEGIN
DROP FUNCTION dbo.ufn_GetCodeCharsValueTable;
END
GO --==================================
-- 功能: 获取编码字符串不同长度对应的整数值范围
-- 说明: 具体实现阐述
-- 作者: XXX
-- 创建: yyyy-MM-dd
-- 修改: yyyy-MM-dd XXX 修改内容描述
-- 调用: SELECT StartValue, EndValue, FixLength FROM dbo.ufn_GetCodeCharsValueTable();
--==================================
CREATE FUNCTION dbo.ufn_GetCodeCharsValueTable
( ) RETURNS @tblCodeCharValue TABLE (
StartValue INT NOT NULL,
EndValue INT NOT NULL,
FixLength TINYINT NOT NULL
)
AS
BEGIN
DECLARE
@tintLength AS TINYINT,
@tintMaxLength AS TINYINT;
SELECT
@tintLength = ,
@tintMaxLength = ; WHILE @tintLength <= @tintMaxLength
BEGIN
INSERT INTO @tblCodeCharValue (StartValue, EndValue, FixLength)
VALUES (dbo.ufn_GetCodeCharsValue(CONCAT('A', REPLICATE('', @tintLength - ))), dbo.ufn_GetCodeCharsValue(CONCAT('Z', REPLICATE('Z', @tintLength - ))), @tintLength); SET @tintLength = @tintLength + ;
END RETURN;
END
GO
获取整数值对应的编码字符串变量函数。
该函数的T-SQL代码如下:
IF OBJECT_ID(N'dbo.ufn_GetCodeChars', 'FN') IS NOT NULL
BEGIN
DROP FUNCTION dbo.ufn_GetCodeChars;
END
GO --==================================
-- 功能: 获取一个整数值对应的编码字符串
-- 说明: 具体实现阐述
-- 作者: XXX
-- 创建: yyyy-MM-dd
-- 修改: yyyy-MM-dd XXX 修改内容描述
--==================================
CREATE FUNCTION dbo.ufn_GetCodeChars
(
@intCodeCharsValue INT -- 编码字符串整数值
) RETURNS VARCHAR()
--$Encode$--
AS
BEGIN
SET @intCodeCharsValue = ISNULL(@intCodeCharsValue, );
DECLARE @chvCodeChars AS VARCHAR();
SET @chvCodeChars = ''; -- 整数值长度变量
DECLARE @tintFixLength AS TINYINT;
SET @tintFixLength = ; SELECT @tintFixLength = FixLength
FROM dbo.ufn_GetCodeCharsValueTable()
WHERE @intCodeCharsValue BETWEEN StartValue AND EndValue; -- 整数值范围的逻辑检查
IF @tintFixLength =
BEGIN
RETURN @chvCodeChars;
END DECLARE @tblChar TABLE(
[Char] CHAR() NOT NULL,
Value TINYINT NOT NULL
); INSERT INTO @tblChar ([Char], Value)
SELECT [Char], Value
FROM dbo.ufn_GetCharTable(); DECLARE @tintPerCodeValue TINYINT;
SET @tintPerCodeValue = ; WHILE @tintFixLength >=
BEGIN
SET @tintPerCodeValue = @intCodeCharsValue / POWER(, @tintFixLength - ); SELECT TOP @chvCodeChars = @chvCodeChars + [Char], @tintPerCodeValue = Value
FROM @tblChar
WHERE Value <= @tintPerCodeValue
ORDER BY Value DESC; SET @intCodeCharsValue = @intCodeCharsValue - @tintPerCodeValue * POWER(, @tintFixLength - ); SET @tintFixLength = @tintFixLength - ;
END RETURN @chvCodeChars;
END
GO
测试实现效果
测试T-SQL代码如下:
DECLARE @chvCodeChars AS VARCHAR();
SET @chvCodeChars = CONCAT('A', REPLICATE('', - ));
DECLARE @intCodeCharsValue AS INT;
SET @intCodeCharsValue = dbo.ufn_GetCodeCharsValue(@chvCodeChars); SELECT @chvCodeChars AS CurrentCodeChars, @intCodeCharsValue AS CurrentCodeCharsValue, dbo.ufn_GetCodeChars(@intCodeCharsValue + ) AS NextCodeChars
GO
执行后的查询结果如下:
博友如有其他更好的解决方案,也请不吝赐教,万分感谢。