[英]Calculating a checksum in SQL
我有一個巴西公積金數字的數據庫字段,想要檢查它們的有效性。 這些是11位數字符串,分別是9位和2個校驗和數字。
我目前在MS Excel中實現了校驗和(見下文),但我想找到一種在SQL中實現它的方法。
校驗和的工作原理如下:(保持緊,這是堅果。)
第一位數(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.