我有一个巴西公积金数字的数据库字段,想要检查它们的有效性。这些是11位数字符串,分别是9位和2位校验和数字。
我目前在MS Excel中实现校验和(见下文),但我想找到一种在SQL中执行此操作的方法。
Checksum的工作原理如下:(紧紧抓住,这是坚果。)
第一位数字(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)))
答案 0 :(得分:2)
我的测试表:
-- 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);
我的嵌套query:
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)))
;
答案 1 :(得分:1)
假设您的表格中包含id
主键列和cpf
列NUMBER(9,0)
数据类型的表格,请执行以下操作:
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
答案 2 :(得分:0)
@ SAR622:很好的问题和感谢算法。
以下是SQL Server的t-SQL解决方案,以防万一。请注意,Cadastro de Pessoas Físicas (CPF)数字只能有11个数字(预先为零),即它们不能超过10 ^ 12-1。如果您在数据集中记录了14位数字,则这些数字很可能是Cadastro Nacional da Pessoa Jurídica (CNPJ)号码发给企业(或拼写错误或其他内容)。伪造的CPF和CNPJ号码可以(批量生成)和验证(单独)here生成。此外,this site提供了有关其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
产生相同的输出。