バーコード(JANコード)のチェックディジットをSQLとPL/pgSQLで計算してみた
バーコードを大量に生成する必要に迫られたので備忘録。
概要
バーコードといっても複数の種類があり、主に日本国内で見かけるのはJANコードと呼ばれる13桁または7桁の番号です。
そのどちらも、最後の1桁はチェックディジェットと呼ばれ、残りの1桁1桁からモジュラス10/ウェイト3といった方法で計算して求めます。
今回、その「モジュラス10/ウェイト3」をSQLとPL/pgSQLで再現しました。
SQL
select
a.jan,
case when a.ck = 10 then '0'::text else a.cd::text end as cd
from(
select
jan,
10 - mod(
case when length(jan) = 12 then
substr(jan, 12, 1)::integer * 3
+ substr(jan, 11, 1)::integer * 1
+ substr(jan, 10, 1)::integer * 3
+ substr(jan, 9, 1)::integer * 1
+ substr(jan, 8, 1)::integer * 3
+ substr(jan, 7, 1)::integer * 1
+ substr(jan, 6, 1)::integer * 3
+ substr(jan, 5, 1)::integer * 1
+ substr(jan, 4, 1)::integer * 3
+ substr(jan, 3, 1)::integer * 1
+ substr(jan, 2, 1)::integer * 3
+ substr(jan, 1, 1)::integer * 1
when length(jan) = 7 then
substr(jan, 7, 1)::integer * 3
+ substr(jan, 6, 1)::integer * 1
+ substr(jan, 5, 1)::integer * 3
+ substr(jan, 4, 1)::integer * 1
+ substr(jan, 3, 1)::integer * 3
+ substr(jan, 2, 1)::integer * 1
+ substr(jan, 1, 1)::integer * 3
else null end
, 10) as cd
from jans) as a;
PL/pgSQL
create or replace function calc_cd (text) returns text as $$
declare
x integer := 0;
len_code integer = length($1);
begin
if len_code <> 12 and len_code <> 7 then
return null::text;
end if;
for i in 1..len_code loop
case when (len_code - (i - 1)) % 2 = 1 then
x := x + substr($1, i, 1)::integer * 3;
else
x := x + substr($1, i, 1)::integer * 1;
end case;
end loop;
x := 10 - x % 10;
case when x = 10 then
return '0'::text;
else
return x::text;
end case;
end
$$ language plpgsql
returns null on null input
parallel safe;
SQLは恐ろしいほど力業だけども、PL/pgSQLはそれっぽく書けたかもしれない。