PostgreSQL GTIN校验函数异常:计算校验码与实际不符求助
14位GTIN校验函数计算错误问题
我使用以下PostgreSQL函数验证14位GTIN编码,但未得到预期结果:
CREATE OR REPLACE FUNCTION bulk_loader.is_valid_gtin_check_digit(gtin text) RETURNS boolean LANGUAGE plpgsql IMMUTABLE STRICT LEAKPROOF AS $function$ DECLARE gtin_length integer; check_digit integer; gtin_without_check_digit text; gtin_reverse text; gtin_digits integer[]; i integer; sum integer := 0; computed_check_digit integer := 0; BEGIN gtin := regexp_replace(gtin, '\D', '', 'g'); gtin_length := length(gtin); -- If the GTIN code doesn't have 14 digits, return false IF gtin_length <> 14 THEN RETURN FALSE; END IF; gtin_without_check_digit := left(gtin, gtin_length - 2); check_digit := cast(right(gtin, 1) as integer); -- Check if gtin_without_check_digit consists of exactly 13 numeric digits IF gtin_without_check_digit ~ '^[0-9]{13}$' THEN gtin_reverse := reverse(gtin_without_check_digit); gtin_digits := string_to_array(gtin_reverse, NULL)::int[]; -- Compute the check digit FOR i IN 1..array_upper(gtin_digits, 1) LOOP sum := sum + gtin_digits[i] * CASE i % 2 WHEN 0 THEN 3 ELSE 1 END; END LOOP; computed_check_digit := (10 - sum % 10) % 10; -- Compare the computed check digit with the last digit of the GTIN code RETURN computed_check_digit = check_digit; END IF; RETURN FALSE; EXCEPTION WHEN others THEN RETURN FALSE; END; $function$;
执行语句:
select is_valid_gtin_check_digit('10628176957012');
此时计算出的校验码computed_check_digit为9,而实际传入的GTIN最后一位校验码为2,我预期计算值应为2。使用的PostgreSQL版本为9.6.3 on x86_64-pc-linux-gnu。
错误原因及修正方案
原函数存在两个关键错误:
数据位截取错误
原代码中gtin_without_check_digit := left(gtin, gtin_length - 2);错误地截取了前12位,而14位GTIN的校验计算需要基于前13位(去掉最后1位校验码),应改为:gtin_without_check_digit := left(gtin, gtin_length - 1);权重计算逻辑错误
根据GS1的GTIN校验规则,需从右往左(不含校验码)对奇数位数字乘3、偶数位数字乘1。原函数反转数据位后,对偶数位乘3,与规则相反,应调整CASE条件:sum := sum + gtin_digits[i] * CASE i % 2 WHEN 1 THEN 3 ELSE 1 END;
修正后的完整函数
CREATE OR REPLACE FUNCTION bulk_loader.is_valid_gtin_check_digit(gtin text) RETURNS boolean LANGUAGE plpgsql IMMUTABLE STRICT LEAKPROOF AS $function$ DECLARE gtin_length integer; check_digit integer; gtin_without_check_digit text; gtin_reverse text; gtin_digits integer[]; i integer; sum integer := 0; computed_check_digit integer := 0; BEGIN gtin := regexp_replace(gtin, '\D', '', 'g'); gtin_length := length(gtin); -- 校验GTIN长度是否为14位 IF gtin_length <> 14 THEN RETURN FALSE; END IF; -- 截取前13位(去掉最后1位校验码) gtin_without_check_digit := left(gtin, gtin_length - 1); check_digit := cast(right(gtin, 1) as integer); -- 验证前13位是否全为数字 IF gtin_without_check_digit ~ '^[0-9]{13}$' THEN gtin_reverse := reverse(gtin_without_check_digit); gtin_digits := string_to_array(gtin_reverse, NULL)::int[]; -- 计算校验码:反转后奇数位乘3,偶数位乘1(对应原数据位从右往左的奇数位乘3) FOR i IN 1..array_upper(gtin_digits, 1) LOOP sum := sum + gtin_digits[i] * CASE i % 2 WHEN 1 THEN 3 ELSE 1 END; END LOOP; computed_check_digit := (10 - sum % 10) % 10; RETURN computed_check_digit = check_digit; END IF; RETURN FALSE; EXCEPTION WHEN others THEN RETURN FALSE; END; $function$;
说明
通过GS1官方规则验证,1062817695701对应的正确校验码为9,因此原传入的GTIN10628176957012本身是无效的,修正后的函数返回false是符合规则的结果。若需验证有效GTIN,可使用10628176957019,函数会返回true。
内容的提问来源于stack exchange,提问作者Matias
相关产品推荐
相关产品推荐

