如何在SQL中构建辅助VLOOKUP函数

时间:2018-10-22 14:13:16

标签: sql sql-server tsql

我在excel中有一个小的校验位算法,它基本上是一个VLOOKUP函数。现在,我想在sql中使函数具有相同的结果。

要检查数字,我为此定义了一个静态表。

enter image description here 我从0(橙色)开始,然后我的vlookup函数是:

=SVERWEIS(C16;$A$4:$K$13;B17+2;FALSCH)

英语翻译

=VLOOKUP(C16;$A$4:$K$13;B17+2;FALSE)

enter image description here

我如何检查“校验位” enter image description here

首先,我正在看0(C16)列(橙色单元格) 比转移列第9行(b17)中的两位数的组合= 5(我的新转移) 下一个 现在我正沉迷于 传输第5列,并且(B18)= 6->即该列 组合现在是第5行(传输)和第6列->我的新传输是9 下一个 组合现在是第9行(传输)和9列->我的新传输是3 等等... 在第一个示例中,最后一位应为2(C42)...此数字计算为负10-> 10-2 = 8
到目前为止我所做的:

我在sql中创建了一个新表,其中包含CheckDigit Tbl。

CREATE TABLE CheckTbl(
   transfer    INTEGER  NOT NULL PRIMARY KEY 
  ,0           INTEGER  NOT NULL
  ,1           INTEGER  NOT NULL
  ,2           INTEGER  NOT NULL
  ,3           INTEGER  NOT NULL
  ,4           INTEGER  NOT NULL
  ,5           INTEGER  NOT NULL
  ,6           INTEGER  NOT NULL
  ,7           INTEGER  NOT NULL
  ,8           INTEGER  NOT NULL
  ,9           INTEGER  NOT NULL
  ,check_digit INTEGER  NOT NULL
);
INSERT INTO CheckTbl(transfer,0,1,2,3,4,5,6,7,8,9,check_digit) VALUES (0,0,9,4,6,8,2,7,1,3,5,0);
INSERT INTO CheckTbl(transfer,0,1,2,3,4,5,6,7,8,9,check_digit) VALUES (1,9,4,6,8,2,7,1,3,5,0,9);
INSERT INTO CheckTbl(transfer,0,1,2,3,4,5,6,7,8,9,check_digit) VALUES (2,4,6,8,2,7,1,3,5,0,9,8);
INSERT INTO CheckTbl(transfer,0,1,2,3,4,5,6,7,8,9,check_digit) VALUES (3,6,8,2,7,1,3,5,0,9,4,7);
INSERT INTO CheckTbl(transfer,0,1,2,3,4,5,6,7,8,9,check_digit) VALUES (4,8,2,7,1,3,5,0,9,4,6,6);
INSERT INTO CheckTbl(transfer,0,1,2,3,4,5,6,7,8,9,check_digit) VALUES (5,2,7,1,3,5,0,9,4,6,8,5);
INSERT INTO CheckTbl(transfer,0,1,2,3,4,5,6,7,8,9,check_digit) VALUES (6,7,1,3,5,0,9,4,6,8,2,4);
INSERT INTO CheckTbl(transfer,0,1,2,3,4,5,6,7,8,9,check_digit) VALUES (7,1,3,5,0,9,4,6,8,2,7,3);
INSERT INTO CheckTbl(transfer,0,1,2,3,4,5,6,7,8,9,check_digit) VALUES (8,3,5,0,9,4,6,8,2,7,1,2);
INSERT INTO CheckTbl(transfer,0,1,2,3,4,5,6,7,8,9,check_digit) VALUES (9,5,0,9,4,6,8,2,7,1,3,1);

我的SQL:

DECLARE @refNr nvarchar(30) = '9699100000030000201830'
DECLARE @str VARCHAR(50), @Inc INT, @len INT, @char VARCHAR(50)


SET @str = @refNr
SET @Inc = 1
SET @len = LEN(@str)

WHILE @Inc<= @len
BEGIN
  SET @char = COALESCE(@char+',' ,'') + SUBSTRING(@str, @Inc, 1)  
  SET @Inc=@Inc+1
END


SELECT [value] FROM string_split(@char, ',') WHERE RTRIM(value) <> ''; 

SELECT [transfer]
      ,[0],[1],[2],[3],[4],[5],[6],[7],[8],[9]
      ,[check_digit]
  FROM [CCHelper].[dbo].[CheckTbl]


  SELECT 
    [value] 
  FROM 
    string_split(@char, ',') as SS
    LEFT JOIN CheckTbl CT on (SS.[value] = CT. .....)

我还将长整数分成几行。

enter image description here

不幸的是,我不知道该怎么写on条件才能加入表格。 我只需要一个号码...支票号码。

解决方案: 终于,我有一些时间重新考虑解决方案:

Create FUNCTION [dbo].[CheckDigit] (
    @long_number VARCHAR(80))
RETURNS INT
AS
BEGIN

DECLARE @check_digit INT;

DECLARE @numbers VARCHAR(50) = '0946827135';
    DECLARE @check_digits TABLE (
        id INT IDENTITY(1,1),
        alg INT
    );
    DECLARE @numbers_len INT;
    SELECT @numbers_len = LEN(@numbers);
    DECLARE @item INT = 1;

   WHILE @item <= @numbers_len
    BEGIN
        INSERT INTO @check_digits (alg) SELECT CONVERT(INT, SUBSTRING(@numbers, @item, 1));
        SELECT @item = @item + 1;
    END;


DECLARE @offset TABLE (
        id INT IDENTITY(1,1),
        offset INT,
        r2 INT
        );
DECLARE @len INT;
SELECT @len = LEN(@long_number);

DECLARE @pos INT = 1;
DECLARE @rpr INT;
DECLARE @r2 INT = 0;

WHILE @pos <= @len
    BEGIN
        INSERT INTO @offset (offset) SELECT CONVERT(INT, SUBSTRING(@long_number, @pos, 1));
        SELECT @rpr = @r2 + (SELECT offset FROM @offset WHERE id = @pos)
        SELECT @r2 = (SELECT alg FROM @check_digits WHERE id= ((@rpr % 10)+1))
        UPDATE @offset SET r2 = @r2 WHERE id = @pos;
        SELECT @pos = @pos + 1;
    END;



SELECT @check_digit = (SELECT (10-r2) AS Result FROM @offset WHERE id = @len)%10
RETURN @check_digit;
END;

现在可以使用了。

1 个答案:

答案 0 :(得分:1)

我认为我并不是100%地了解这是如何工作的,但是我通过Excel VLOOKUP进行了工作,我可以得到第一个示例的答案,但是看起来并不特别愉快!

SET NOCOUNT ON;
DECLARE @check_digits TABLE (
    [transfer] INT,
    d0 INT,
    d1 INT,
    d2 INT,
    d3 INT,
    d4 INT,
    d5 INT,
    d6 INT,
    d7 INT,
    d8 INT,
    d9 INT,
    check_digit INT);
INSERT INTO @check_digits SELECT 0,0,9,4,6,8,2,7,1,3,5,0;
INSERT INTO @check_digits SELECT 1,9,4,6,8,2,7,1,3,5,0,9;
INSERT INTO @check_digits SELECT 2,4,6,8,2,7,1,3,5,0,9,8;
INSERT INTO @check_digits SELECT 3,6,8,2,7,1,3,5,0,9,4,7;
INSERT INTO @check_digits SELECT 4,8,2,7,1,3,5,0,9,4,6,6;
INSERT INTO @check_digits SELECT 5,2,7,1,3,5,0,9,4,6,8,5;
INSERT INTO @check_digits SELECT 6,7,1,3,5,0,9,4,6,8,2,4;
INSERT INTO @check_digits SELECT 7,1,3,5,0,9,4,6,8,2,7,3;
INSERT INTO @check_digits SELECT 8,3,5,0,9,4,6,8,2,7,1,2;
INSERT INTO @check_digits SELECT 9,5,0,9,4,6,8,2,7,1,3,1;

DECLARE @offset TABLE (
    id INT,
    offset INT);
INSERT INTO @offset
SELECT 1, 9
UNION ALL
SELECT 2, 6
UNION ALL
SELECT 3, 9
UNION ALL
SELECT 4, 9
UNION ALL
SELECT 5, 1
UNION ALL
SELECT 6, 0
UNION ALL
SELECT 7, 0
UNION ALL
SELECT 8, 0
UNION ALL
SELECT 9, 0
UNION ALL
SELECT 10, 0
UNION ALL
SELECT 11, 0
UNION ALL
SELECT 12, 3
UNION ALL
SELECT 13, 0
UNION ALL
SELECT 14, 0
UNION ALL
SELECT 15, 0
UNION ALL
SELECT 16, 0
UNION ALL
SELECT 17, 2
UNION ALL
SELECT 18, 0
UNION ALL
SELECT 19, 1
UNION ALL
SELECT 20, 8
UNION ALL
SELECT 21, 3
UNION ALL
SELECT 22, 0
UNION ALL
SELECT 23, 0
UNION ALL
SELECT 24, 0
UNION ALL
SELECT 25, 8
UNION ALL
SELECT 26, 7;

DECLARE @transfer INT = 0;
DECLARE @offset_value INT;
DECLARE @iterations INT = 1;
WHILE @iterations <= 26
BEGIN
    SELECT @offset_value = offset + 2 FROM @offset WHERE id = @iterations;
    SELECT @transfer = 
        CASE
            WHEN @offset_value = 2 THEN d0
            WHEN @offset_value = 3 THEN d1
            WHEN @offset_value = 4 THEN d2
            WHEN @offset_value = 5 THEN d3
            WHEN @offset_value = 6 THEN d4
            WHEN @offset_value = 7 THEN d5
            WHEN @offset_value = 8 THEN d6
            WHEN @offset_value = 9 THEN d7
            WHEN @offset_value = 10 THEN d8
            WHEN @offset_value = 11 THEN d9
        END
    FROM 
        @check_digits
    WHERE
        [transfer] = @transfer;
    SELECT @iterations = @iterations + 1;
END;
PRINT 'Check Digit is ' + CONVERT(CHAR(1), 10 - @transfer);
SET NOCOUNT OFF;

这真的需要基于集合,可能使用递归吗?但是,我并没有真正看到它的商业用途。我假设存在一个长数字96991000000300002018300087,并且您想要此校验和吗?我真的不知道您的(O)橙色数字起什么作用,所以我只是将数字设置为零(如您的示例所示),而忽略了这一部分。

无论如何,如果您实际运行它(它是非破坏性的,因为它没有实现任何东西),那么您将得到正确的答案2。从这里到哪里去还不清楚。可能是获得长号的更好方法?可能需要为您的长号中的每个数字添加递归等?


我想我可能花了太长时间,但是我将它作为UDF进行了工作:

CREATE FUNCTION dbo.CheckDigit (
    @long_number VARCHAR(50))
RETURNS INT
AS
BEGIN
    --Hardcoded check digits
    DECLARE @check_digit INT;
    DECLARE @check_digits TABLE (
        [transfer] INT,
        d0 INT,
        d1 INT,
        d2 INT,
        d3 INT,
        d4 INT,
        d5 INT,
        d6 INT,
        d7 INT,
        d8 INT,
        d9 INT,
        check_digit INT);
    INSERT INTO @check_digits SELECT 0,0,9,4,6,8,2,7,1,3,5,0;
    INSERT INTO @check_digits SELECT 1,9,4,6,8,2,7,1,3,5,0,9;
    INSERT INTO @check_digits SELECT 2,4,6,8,2,7,1,3,5,0,9,8;
    INSERT INTO @check_digits SELECT 3,6,8,2,7,1,3,5,0,9,4,7;
    INSERT INTO @check_digits SELECT 4,8,2,7,1,3,5,0,9,4,6,6;
    INSERT INTO @check_digits SELECT 5,2,7,1,3,5,0,9,4,6,8,5;
    INSERT INTO @check_digits SELECT 6,7,1,3,5,0,9,4,6,8,2,4;
    INSERT INTO @check_digits SELECT 7,1,3,5,0,9,4,6,8,2,7,3;
    INSERT INTO @check_digits SELECT 8,3,5,0,9,4,6,8,2,7,1,2;
    INSERT INTO @check_digits SELECT 9,5,0,9,4,6,8,2,7,1,3,1;

    --Make the long number into an indexed list
    DECLARE @offset TABLE (
        id INT IDENTITY(1,1),
        offset INT);
    DECLARE @len INT;
    SELECT @len = LEN(@long_number);
    DECLARE @pos INT = 1;
    WHILE @pos <= @len
    BEGIN
        INSERT INTO @offset (offset) SELECT CONVERT(INT, SUBSTRING(@long_number, @pos, 1));
        SELECT @pos = @pos + 1;
    END;

    --Use recursive CTE
    WITH cte1 AS (
        SELECT
            1 AS iterations,
            offset,
            offset + 2 AS new_offset
        FROM
            @offset
        WHERE
            id = 1
        UNION ALL
        SELECT
            iterations + 1 AS iterations,
            o.offset,
            o.offset + 2 AS new_offset
        FROM
            cte1 c
            INNER JOIN @offset o ON o.id = c.iterations + 1
        WHERE
            c.iterations <= @len),
    cte2 AS (
        SELECT
            1 AS iterations,
            c.new_offset,
            CASE
                WHEN c.new_offset = 2 THEN d0
                WHEN c.new_offset = 3 THEN d1
                WHEN c.new_offset = 4 THEN d2
                WHEN c.new_offset = 5 THEN d3
                WHEN c.new_offset = 6 THEN d4
                WHEN c.new_offset = 7 THEN d5
                WHEN c.new_offset = 8 THEN d6
                WHEN c.new_offset = 9 THEN d7
                WHEN c.new_offset = 10 THEN d8
                WHEN c.new_offset = 11 THEN d9
            END AS [transfer]
        FROM 
            cte1 c
            INNER JOIN @check_digits cd ON cd.[transfer] = 0
        WHERE
            iterations = 1
        UNION ALL
        SELECT
            c.iterations + 1 AS iterations,
            c1.new_offset,
            CASE
                WHEN c1.new_offset = 2 THEN d0
                WHEN c1.new_offset = 3 THEN d1
                WHEN c1.new_offset = 4 THEN d2
                WHEN c1.new_offset = 5 THEN d3
                WHEN c1.new_offset = 6 THEN d4
                WHEN c1.new_offset = 7 THEN d5
                WHEN c1.new_offset = 8 THEN d6
                WHEN c1.new_offset = 9 THEN d7
                WHEN c1.new_offset = 10 THEN d8
                WHEN c1.new_offset = 11 THEN d9
            END AS [transfer]
        FROM 
            cte2 c
            INNER JOIN @check_digits cd ON cd.[transfer] = c.[transfer]
            INNER JOIN cte1 c1 ON c1.iterations = c.iterations + 1
        WHERE
            c1.iterations <= @len)
    SELECT @check_digit = 10 - [transfer] FROM cte2 WHERE iterations = @len;
    RETURN @check_digit;
END;
GO
SELECT dbo.CheckDigit('96991000000300002018300087');
SELECT dbo.CheckDigit('96991000000300002018300086');
SELECT dbo.CheckDigit('96991000000300002018300085');

...我今天学到了一些东西;我不知道您可以在同一查询中两次使用递归:D

如果运行该命令,则应该得到与Excel匹配的8、2和4...。