簡體   English   中英

在SQL中計算校驗和

[英]Calculating a checksum in SQL

我有一個巴西公積金數字的數據庫字段,想要檢查它們的有效性。 這些是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 - remainder

第二位數字(K):(相同的計算,但包括數字J)

  • 將前10個的每個數字乘以常數:
    11A + 10B + 9C + 8D + 7E + 6F + 5G + 4H + 3I + 2J

  • 將此總和除以11,如果余數為0或1,則K為0.如果余數> = 2,則K為11 - remainder

- 在MS Excel中實現 -
假設CPF在A2中。
這里的優化是受歡迎的,但不是這個問題的重點。
數字J: =IF(MOD(SUM(MID($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)<=1,NUMBERVALUE(LEFT(RIGHT($A2,2),1))=0,NUMBERVALUE(LEFT(RIGHT($A2,2),1))=(11-MOD(SUM(MID($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)))
數字K: =IF(MOD(SUM(MID($A2,1,1)*11,MID($A2,2,1)*10,MID($A2,3,1)*9,MID($A2,4,1)*8,MID($A2,5,1)*7,MID($A2,6,1)*6,MID($A2,7,1)*5,MID($A2,8,1)*4,MID($A2,9,1)*3,MID($A2,10,1)*2),11)<=1,NUMBERVALUE(LEFT(RIGHT($A2,1),1))=0,NUMBERVALUE(LEFT(RIGHT($A2,1),1))=(11-MOD(SUM(MID($A2,1,1)*11,MID($A2,2,1)*10,MID($A2,3,1)*9,MID($A2,4,1)*8,MID($A2,5,1)*7,MID($A2,6,1)*6,MID($A2,7,1)*5,MID($A2,8,1)*4,MID($A2,9,1)*3,MID($A2,10,1)*2),11)))

我的測試表:

-- 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);

我的嵌套查詢

SELECT No, 
(CASE WHEN (J != J2) THEN 'J wrong!' ELSE 'J ok!' END) as Jchk,
(CASE WHEN (K != K2) THEN 'K wrong!' ELSE 'K ok!' END) as Kchk
FROM 
(SELECT No, J, K,
(CASE WHEN MJ < 2 THEN 0 ELSE 11 - MJ END) as J2,
(CASE WHEN MK < 2 THEN 0 ELSE 11 - MK END) as K2
FROM 
(SELECT No, J, K,
MOD(10*A + 9*B + 8*C + 7*D + 6*E + 5*F + 4*G + 3*H + 2*I, 11) as MJ,
MOD(11*A + 10*B + 9*C + 8*D + 7*E + 6*F + 5*G + 4*H + 3*I + 2*J, 11) as MK 
FROM 
 (SELECT
  No,
  substr(to_char(No), 1, 1) as A,
  substr(to_char(No), 2, 1) as B,
  substr(to_char(No), 3, 1) as C,
  substr(to_char(No), 4, 1) as D,
  substr(to_char(No), 5, 1) as E,
  substr(to_char(No), 6, 1) as F,
  substr(to_char(No), 7, 1) as G,
  substr(to_char(No), 8, 1) as H,
  substr(to_char(No), 9, 1) as I,
  substr(to_char(No), 10, 1) as J,
  substr(to_char(No), 11, 1) as K
  FROM CPF)))
  ;

假設您有一個具有id主鍵列的表和一個NUMBER(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

@ SAR622:很好的問題和感謝算法。

這是一個針對SQL Server的t-SQL解決方案,以防萬一。 請注意, Cadastro dePessoasFísicas(CPF)數字只能有11位數(預先為零),即它們不能超過10 ^ 12-1。 如果您在數據集中記錄了14位數字,這些數字很可能是發給企業的Cadastro Nacional daPessoaJurídica(CNPJ)號碼(或拼寫錯誤或其他內容)。 假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慢。 或者,您可以使用TABLE輸入/輸出或存儲過程創建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;

輸出是:

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

表的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

產生相同的輸出。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM