在SQL中计算校验和

在SQL中计算校验和,sql,oracle,algorithm,checksum,Sql,Oracle,Algorithm,Checksum,我有一个巴西CPF编号的数据库字段,希望检查其有效性。这些是11位字符串,分别是9位和2位校验和数字 我目前在MS Excel中实现了校验和,请参见下文,但我想找出一种在SQL中实现校验和的方法 校验和的工作原理如下:抓紧,这是螺母 CPF编号以ABCDEFGHI/JK的形式书写,或直接作为 ABCDEFGHIJK,其中数字不能全部相同。 J称为CPF编号的第一位校验。 K称为CPF编号的第二个校验位。 第一位数字J: 将前9的每个数字乘以一个常数: 10*A+9*B+8*C+7*D+6*E+5

我有一个巴西CPF编号的数据库字段,希望检查其有效性。这些是11位字符串,分别是9位和2位校验和数字

我目前在MS Excel中实现了校验和,请参见下文,但我想找出一种在SQL中实现校验和的方法

校验和的工作原理如下:抓紧,这是螺母

CPF编号以ABCDEFGHI/JK的形式书写,或直接作为 ABCDEFGHIJK,其中数字不能全部相同。 J称为CPF编号的第一位校验。 K称为CPF编号的第二个校验位。 第一位数字J:

将前9的每个数字乘以一个常数: 10*A+9*B+8*C+7*D+6*E+5*F+4*G+3*H+2*I

将该总和除以11,如果余数为0或1,则J将为0。如果余数大于等于2,则J将为11-余数

第二位K:计算相同,但包括数字J

将前10位的每个数字乘以一个常数: 11A+10B+9C+8D+7E+6F+5G+4H+3I+2J

将该和除以11,如果余数为0或1,K将为0。如果余数大于等于2,则K将为11-余数

-在msexcel中的实现- 假设CPF位于A2。 这里的优化是受欢迎的,但并不是这个问题的重点。
数字J:=IFMODSUMMID$A2,1,1*10,MID$A2,2,1*9,MID$A2,3,1*8,MID$A2,4,1*7,MID$A2,5,1*6,MID$A2,6,1*5,MID$A2,7,1*4,MID$A2,8,1*3,MID$A2,9,1*2,11假设您有一个带有id主键列和数字9,0数据类型的cpf列的表,则类似于:

WITH digits ( id, a, b, c, d, e, f, g, h, i ) AS (
  SELECT id,
         MOD( TRUNC( cpf / 1e8 ), 10 ),
         MOD( TRUNC( cpf / 1e7 ), 10 ),
         MOD( TRUNC( cpf / 1e6 ), 10 ),
         MOD( TRUNC( cpf / 1e5 ), 10 ),
         MOD( TRUNC( cpf / 1e4 ), 10 ),
         MOD( TRUNC( cpf / 1e3 ), 10 ),
         MOD( TRUNC( cpf / 1e2 ), 10 ),
         MOD( TRUNC( cpf / 1e1 ), 10 ),
         MOD( TRUNC( cpf / 1e0 ), 10 )
  FROM   your_table
),
values1 ( id, j, k ) AS (
  SELECT id,
         MOD( 10*A +  9*B +  8*C +  7*D +  6*E +  5*F +  4*G +  3*H + 2*I, 11 ),
         11*A + 10*B +  9*C +  8*D +  7*E +  6*F +  5*G +  4*H + 3*I
  FROM   digits
),
values2 ( id, j, k ) AS (
  SELECT id,
         CASE WHEN j <= 1 THEN 0 ELSE 11 - j END,
         MOD( k + 2 * CASE WHEN j <= 1 THEN 0 ELSE 11 - j END, 11 )
  FROM   values1
)
SELECT id,
       j,
       CASE WHEN k <= 1 THEN 0 ELSE 11 - k END AS k
FROM   values2
我的测试表:

-- Create a table called CPF
CREATE TABLE CPF(Id integer PRIMARY KEY, No integer);

-- Create few records in this table 
INSERT INTO CPF VALUES(1, 12345678901);
我的嵌套:


@问得好,谢谢你的算法

下面是一个针对SQL Server的t-SQL解决方案,以防万一。请注意,数字只能有11位预展开为零的数字,也就是说,它们不能超过10^12-1。如果您注意到数据集中有14位数字,那么这些数字很可能是发布给企业的数字或打字错误或其他内容。伪造的CPF和CNPJ编号可以批量生成并单独验证。还提供了有关CNPJ所在企业的更多信息,可以将其视为隐含的CNPJ验证。验证CPF编号时,请记住检查编号是否在[0,10^12-1]范围内。您可能需要删除任何标点符号和其他无效字符作为用户,我们倾向于打字

此输入表有前5个无效CPF编号和后4个有效CPF编号:

IF OBJECT_ID('tempdb..#x') IS NOT NULL DROP TABLE #x;
CREATE TABLE #x  (CPF BIGINT default NULL);
INSERT INTO #x (CPF) VALUES (12345678900);
INSERT INTO #x (CPF) VALUES (11);
INSERT INTO #x (CPF) VALUES (1010101010101010);
INSERT INTO #x (CPF) VALUES (11111179011525590);
INSERT INTO #x (CPF) VALUES (-32081397641);
INSERT INTO #x (CPF) VALUES (00000008726210061);
INSERT INTO #x (CPF) VALUES (56000608314);
INSERT INTO #x (CPF) VALUES (73570630706);
INSERT INTO #x (CPF) VALUES (93957133564);
下面的t-SQL函数模块化了实现,但可能比后面的原始t-SQL慢。或者,可以使用表输入/输出或存储过程创建t-SQL函数

ALTER FUNCTION fnIsCPF(@n BIGINT) RETURNS INT AS
BEGIN
    DECLARE @isValid BIT = 0;
    IF (@n > 0 AND @n < 100000000000)
    BEGIN
        --Parse out numbers
        DECLARE @a TINYINT = FLOOR( @n / 10000000000)% 10;
        DECLARE @b TINYINT = FLOOR( @n / 1000000000)% 10;
        DECLARE @c TINYINT = FLOOR( @n / 100000000)% 10;
        DECLARE @d TINYINT = FLOOR( @n / 10000000)% 10;
        DECLARE @e TINYINT = FLOOR( @n / 1000000)% 10;
        DECLARE @f TINYINT = FLOOR( @n / 100000)% 10;
        DECLARE @g TINYINT = FLOOR( @n / 10000)% 10;
        DECLARE @h TINYINT = FLOOR( @n / 1000)% 10;
        DECLARE @i TINYINT = FLOOR( @n / 100)% 10;

        DECLARE @j TINYINT =  ISNULL(NULLIF(NULLIF(11-( 10*@a + 9*@b + 8*@c + 7*@d + 6*@e + 5*@f + 4*@g + 3*@h + 2*@i) % 11, 11), 10), 0);
        DECLARE @k TINYINT =  ISNULL(NULLIF(NULLIF(11 - (11*@a +10*@b + 9*@c + 8*@d + 7*@e + 6*@f + 5*@g + 4*@h + 3*@i + 2 * @j)% 11, 11), 10), 0);
        RETURN CASE WHEN @j=FLOOR(@n / 10)% 10 AND @k=FLOOR(@n)% 10 THEN 1 ELSE 0 END
    END;
    RETURN @isValid;
END;
表的t-SQL:

WITH digits ( CPF, a, b, c, d, e, f, g, h, i ) AS (
  SELECT CPF,
    FLOOR( CPF / 10000000000)% 10,
    FLOOR( CPF / 1000000000)% 10,
    FLOOR( CPF / 100000000)% 10,
    FLOOR( CPF / 10000000)% 10,
    FLOOR( CPF / 1000000)% 10,
    FLOOR( CPF / 100000)% 10,
    FLOOR( CPF / 10000)% 10,
    FLOOR( CPF / 1000)% 10,
    FLOOR( CPF / 100)% 10
  FROM   #x
),
jk ( CPF, j, k ) AS (
  SELECT CPF, ISNULL(NULLIF(NULLIF(11-( 10*A + 9*B + 8*C + 7*D + 6*E + 5*F + 4*G + 3*H + 2*I) % 11, 11), 10), 0),
    11*A +10*B + 9*C + 8*D + 7*E + 6*F + 5*G + 4*H + 3*I
  FROM digits
),
jk2 ( CPF, j, k ) AS (
  SELECT CPF, j, ISNULL(NULLIF(NULLIF(11 - (k + 2 * j)% 11, 11), 10), 0)
  FROM jk
)
SELECT CPF, isValid=CASE WHEN CPF>0 AND CPF<99999999999 AND j=FLOOR( CPF / 10)% 10 AND k=FLOOR( CPF)% 10 THEN 1 ELSE 0 END
FROM jk2

产生相同的输出。

我想您需要知道的是Oracle中有一个substr函数。首先,谢谢你试一试。看看这是假设我从一个9位数的字符串开始,并添加校验位。我从一个11位的字符串开始比较。添加了一个正则表达式来检查所有数字是否相同,并添加了一个长度不超过11个字符的检查。当长度CPF>11或类似于CPF的正则表达式“^\d\1*$”或J=J2或K=K2然后“无效”或“有效”结束为CPF\U有效
SELECT CPF, isValid=dbo.fnIsCPF(CPF) FROM #x

CPF                 isValid
12345678900         0
11                  0
1010101010101010    0
11111179011525590   0
-32081397641        0
8726210061          1
56000608314         1
73570630706         1
93957133564         1
WITH digits ( CPF, a, b, c, d, e, f, g, h, i ) AS (
  SELECT CPF,
    FLOOR( CPF / 10000000000)% 10,
    FLOOR( CPF / 1000000000)% 10,
    FLOOR( CPF / 100000000)% 10,
    FLOOR( CPF / 10000000)% 10,
    FLOOR( CPF / 1000000)% 10,
    FLOOR( CPF / 100000)% 10,
    FLOOR( CPF / 10000)% 10,
    FLOOR( CPF / 1000)% 10,
    FLOOR( CPF / 100)% 10
  FROM   #x
),
jk ( CPF, j, k ) AS (
  SELECT CPF, ISNULL(NULLIF(NULLIF(11-( 10*A + 9*B + 8*C + 7*D + 6*E + 5*F + 4*G + 3*H + 2*I) % 11, 11), 10), 0),
    11*A +10*B + 9*C + 8*D + 7*E + 6*F + 5*G + 4*H + 3*I
  FROM digits
),
jk2 ( CPF, j, k ) AS (
  SELECT CPF, j, ISNULL(NULLIF(NULLIF(11 - (k + 2 * j)% 11, 11), 10), 0)
  FROM jk
)
SELECT CPF, isValid=CASE WHEN CPF>0 AND CPF<99999999999 AND j=FLOOR( CPF / 10)% 10 AND k=FLOOR( CPF)% 10 THEN 1 ELSE 0 END
FROM jk2