web-dev-qa-db-ja.com

GST識別番号(GSTIN)の検証

次の図は、GST識別番号の形式を示しています。

enter image description here

  • 最初の2桁は、2011年インド国勢調査による一意の州コードを示します。たとえば、州コードニューデリーのは '07'で、カルナタカのそれは '29'です。

  • 次の10文字は[〜#〜] pan [〜#〜]Permanent Account Number)納税者の。

  • 13桁目は、登録番号(またはエンティティ番号)を示します同じ[〜#〜] pan [〜#〜]の納税者。

  • 14桁目は、デフォルトではすべて「Z」です。現在、何も意図していません。

  • 15桁目はチェックサムディジットです–数字またはアルファベット文字。

おそらく、PATINDEXまたはRegexを使用して検証を実行できますか?

1
Sunil Patil

これにはPATINDEXやRegExは必要ありません。

CREATE TABLE #floob
(
  GSTINColumn char(15),
  CONSTRAINT CheckGSTINColumn CHECK 
  (
    GSTINColumn  LIKE '[0-9][0-9][0-9A-Z]'
      + '[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]'
      + '[0-9A-Z][0-9A-Z][0-9A-Z][0-9A-Z]'
      + '[0-9A-Z][0-9]Z[0-9A-Z]'
  )
);

-- succeeds
INSERT #floob(GSTINColumn) VALUES('22AAAAA0000A1Z5');
GO
-- succeeds
INSERT #floob(GSTINColumn) VALUES('675T3E5600AZ7Z9');
GO
-- fails
INSERT #floob(GSTINColumn) VALUES('22AAAAA0000A1X5');
GO

DROP TABLE #floob;
4
Aaron Bertrand

アーロンの完全に有効な回答を補足するために、あなたmightは、チェックディジットの正確さを検証することにより、GSTIN番号のより詳細な検証を行うことに決めました、番号の最後の桁として表示されます。

以下のコードは、新しい行を検証するためにCHECK CONSTRAINTによって使用される多数の関数を作成し、GSTIN番号への変更を加えます。

最初の2つの関数は、GSTIN番号に含まれるASCII文字のbase-36への任意のマッピングを提供します。つまり、009にマップします9にマップし、A10にマップし、Z35にマップします。

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

インド政府がこれと同じエンコーディングを使用しているかどうかを確実に判断することはできませんでしたが、提供された値に対しては機能するようです。

このコードは、上記の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

このコードにはエラーチェックはまったくありません。読者の練習問題として残しておきます。 GSTINに有効なチェックデジットが最後の桁として含まれている場合、関数は1を返します。チェックディジットが無効な場合、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)
);

ここでは、いくつかの「テスト」GSTIN番号を挿入します。

INSERT INTO dbo.t (i) VALUES ('123456789012345');
INSERT INTO dbo.t (i) VALUES ('27AAFFM5744C1ZE');
INSERT INTO dbo.t (i) VALUES ('27AAACE7932L1ZC');

最初の挿入は失敗します。 2番目と3番目の挿入は成功します。無効な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

無効なGSTIN番号を挿入:

INSERT INTO dbo.t_with_trigger (i) 
VALUES ('123456789012345')
    , ('27AAFFM5744C1ZE')
    , ('27AAACE7932L1ZC');

このエラーが発生します:

メッセージ50000、レベル14、状態1、手順t_validate、行37 [バッチ開始行129]
無効なGSTIN番号を使用して挿入/更新しようとしました。
メッセージ3609、レベル16、状態1、行130
トランザクションはトリガーで終了しました。バッチは中止されました。

私は LUHNアルゴリズム を中心に実装を行いました。

5
Max Vernon