Sun*_*til 1 sql-server constraint sql-server-2008-r2 data-validation
下图描述了 GST 标识号的格式:
根据 2011 年印度人口普查,前 2 位数字表示唯一的州代码。例如,新德里的州代码是“07”,卡纳塔克邦的州代码是“29”。
接下来的 10 个字符表示纳税人的PAN(永久帐号)。
第 13 位数字表示具有相同PAN 的纳税人的注册号(或实体号)。
默认情况下,第 14 位数字是“Z”——目前没有任何意图。
第 15 位数字是校验和数字- 可以是数字或字母字符。
也许我可以通过 PATINDEX 或 Regex 进行验证?
为了补充 Aaron 完全有效的答案,您可能决定通过验证校验位的正确性来更深入地验证 GSTIN 号码,校验位显示为号码的最后一位。
下面的代码创建了许多函数,a 将使用这些函数CHECK CONSTRAINT
来验证新行以及对 GSTIN 编号的任何修改。
前两个函数提供将 GSTIN 编号中包含的 ASCII 字符任意映射到 base-36;即0
映射到0
、9
映射到9
、A
映射到10
和Z
映射到35
:
IF OBJECT_ID(N'dbo.map_char', N'FN') IS NOT NULL
DROP FUNCTION dbo.map_char;
GO
CREATE FUNCTION dbo.map_char(@c char(1))
RETURNS int
AS
BEGIN
DECLARE @val int;
SET @c = UPPER(@c);
IF ASCII(@c) >= 48 AND ASCII(@c) <= 57
SET @val = ASCII(@c) - 48;
IF ASCII(@c) >= 65 AND ASCII(@c) <= 90
SET @val = (ASCII(@c) - 65) + 10;
RETURN @val;
END
GO
IF OBJECT_ID(N'dbo.unmap_char', N'FN') IS NOT NULL
DROP FUNCTION dbo.unmap_char;
GO
CREATE FUNCTION dbo.unmap_char(@v int)
RETURNS char(1)
AS
BEGIN
DECLARE @c char(1);
IF @v >= 0 AND @v <=9
SET @c = CHAR(@v + 48);
IF @v >= 10 AND @v <= 90
SET @c = CHAR((@v + 65) - 10);
RETURN @c;
END
GO
Run Code Online (Sandbox Code Playgroud)
我一直无法确定印度政府是否使用相同的编码,但它似乎适用于提供的值。
此代码使用上述dbo.map_char
函数来验证给定 GSTIN 号码中的校验位。
CREATE FUNCTION dbo.fn_validate_gstin
(
@inp char(15)
)
RETURNS tinyint
AS
BEGIN
DECLARE @return tinyint;
DECLARE @i int = LEN(@inp);
DECLARE @factor int = 1;
DECLARE @char char(1);
DECLARE @codepoint int;
DECLARE @addend int;
DECLARE @sum int = 0;
IF @i <> 15 /* GSTIN MUST be 15 characters to be valid */
BEGIN
SET @return = 0;
END
ELSE
BEGIN
WHILE @i > 0
BEGIN
SET @codepoint = dbo.map_char(SUBSTRING(@inp, @i, 1));
SET @addend = @factor * @codepoint;
SET @addend = (@addend / 36) + (@addend % 36);
SET @sum += @addend;
IF @factor = 2 SET @factor = 1 ELSE SET @factor = 2;
SET @i -= 1;
END
END
DECLARE @remainder int = @sum % 36;
IF @remainder = 0 SET @return = 1 ELSE SET @return = 0;
RETURN @return;
END
GO
Run Code Online (Sandbox Code Playgroud)
请注意,此代码中绝对没有错误检查;我把它留给读者作为练习。1
如果 GSTIN 包含有效校验位作为最后一位,则该函数返回。如果校验位无效,则返回 0。
这里我创建了一个实现dbo.fn_validate_gstin
函数的表:
CREATE TABLE dbo.t
(
i char(15) NOT NULL
CONSTRAINT CK_t_valid_gstin
CHECK (dbo.fn_validate_gstin(i) = 1)
);
Run Code Online (Sandbox Code Playgroud)
我们在这里插入几个“测试”GSTIN 编号:
INSERT INTO dbo.t (i) VALUES ('123456789012345');
INSERT INTO dbo.t (i) VALUES ('27AAFFM5744C1ZE');
INSERT INTO dbo.t (i) VALUES ('27AAACE7932L1ZC');
Run Code Online (Sandbox Code Playgroud)
第一次插入失败。第二次和第三次插入成功。尝试插入无效的 GSTIN 编号会导致此错误:
消息 547,级别 16,状态 0,第 80 行
INSERT 语句与 CHECK 约束“CK_t_valid_gstin”冲突。
冲突发生在数据库“tempdb”、表“dbo.t”、列“i”中。
请注意,作为约束一部分的标量函数的存在会阻止使用并行性的查询。这对您的系统可能有问题,也可能没有问题。如果您需要并行性,您可以考虑INSTEAD OF
在表上使用触发器来检查插入或更新时的 GSTIN 编号。例如:
IF OBJECT_ID(N'dbo.t_with_trigger', N'U') IS NOT NULL
DROP TABLE dbo.t_with_trigger;
GO
CREATE TABLE dbo.t_with_trigger
(
i char(15) NOT NULL
);
GO
CREATE TRIGGER t_validate
ON dbo.t_with_trigger
INSTEAD OF INSERT, UPDATE
AS
BEGIN
SET NOCOUNT ON;
BEGIN TRANSACTION
DECLARE @bOk bit = 1;
IF EXISTS (
SELECT TOP(1) 1
FROM inserted i
WHERE dbo.fn_validate_gstin(i.i) = 0
UNION ALL
SELECT TOP(1) 1
FROM deleted d
WHERE dbo.fn_validate_gstin(d.i) = 0
)
BEGIN
SET @bOk = 0;
END
IF @bOk = 1
BEGIN
DELETE
FROM dbo.t_with_trigger
FROM dbo.t_with_trigger t
INNER JOIN deleted d ON t.i = d.i;
INSERT INTO dbo.t_with_trigger(i)
SELECT i.i
FROM inserted i;
COMMIT TRANSACTION;
END
ELSE
BEGIN
ROLLBACK TRANSACTION;
DECLARE @msg varchar(1000);
SET @msg = 'Attempted to insert/update using an invalid GSTIN number.';
RAISERROR (@msg, 14, 1);
END
END
GO
Run Code Online (Sandbox Code Playgroud)
插入无效的 GSTIN 号码:
INSERT INTO dbo.t_with_trigger (i)
VALUES ('123456789012345')
, ('27AAFFM5744C1ZE')
, ('27AAACE7932L1ZC');
Run Code Online (Sandbox Code Playgroud)
导致此错误:
消息 50000,级别 14,状态 1,过程 t_validate,第 37 行 [批处理起始行 129]
尝试使用无效的 GSTIN 编号插入/更新。
消息 3609,级别 16,状态 1,第 130 行
事务在触发器中结束。该批次已中止。
我的实现基于LUHN 算法。
归档时间: |
|
查看次数: |
6127 次 |
最近记录: |