「因为要迁移到新系统,请确定商品编码的体系」──在构建业务系统或Excel台账迁移项目中,这道作业题必然会出现。而临时起意决定的编码,平均寿命往往比系统本体还要长。因为即使系统在10年后被替换,分发给客户的客户编号,以及印在过去单据上的商品编码,都会继续留存下去。
另一方面,编码设计领域也积累了令人惊讶的前人智慧。JAN码、信用卡号以及日本的个人番号(My Number)等「大规模运用中的号码」,从输入错误的检测方法到位数的分配,连同设计判断的理由都一并公开。本文将以这些为基础,整理业务系统中确定商品编码・客户编码・单据编号等时应遵循的规则,以及可以使用的机制(校验位)。
1. 先说结论
- 代码只负责识别,意义作为属性保存在数据库中。把部门・分类・年度嵌入代码位数的「有意义编码」,在组织改编・分类变更时必然会破绽。个人番号被设计为不带任何意义的号码。1
- 对由人输入・转录・口头传达的代码,加上校验位。实测数据表明,输入错误的大部分是「单字符打错」与「相邻两位互换」2,而校验位正是针对这两种情况精准检测的。
- 字符种类由运用方式决定。涉及电话・传真・手写时,只使用数字。想节省位数时,可使用去除易混淆字符(I・L・O等)的字母数字组合(如Crockford Base323)。
- 位数以「未来件数的10倍+1位」为标准来预留,若要使用开头的零,则在全系统范围内一律作为字符串处理。Excel一旦将其解读为数值,就会立即丢弃开头的零,并把第16位及以后的数字舍入为0。4
- 一旦使用过的代码,即使成为空号也不再重复利用。因为遗留在过去单据・日志・客户系统中的号码,会因此指向另一个不同的对象。
- 若要给商品附加条码流通,应采用JAN码(GS1标准),而不是自行设计的编码。5
2. 是否让代码承载意义 ── 最初也是最大的分岔点
在编码设计中,最先应该决定的既不是位数,也不是字符种类,而是「是否让代码承载意义」。
常见的设计是这样的:「商品编码为8位,前2位是部门,接下来3位是分类,剩余3位是流水号」。刚决定的那一刻看起来井然有序,但几年后就会变成这样。
- 因组织改编导致部门合并,带有旧部门代码的商品变得无所归属。重新编号会导致无法与过去单据核对;放任不管,「代码上的部门与实际部门不一致」的例外情况就会在主数据中不断增殖。
- 出现跨分类的商品(既是食品又是杂货),于是需要制定该分配哪个代码的公司内部规则。
- 某个分类的商品数量激增,导致3位流水号耗尽,于是开始出现「唯独这个分类例外地借用其他部门空号」的运用方式。
原因很明确:组织和分类本来就是会变化的,却把「不会变化」当作前提烧录进了代码。因此原则要反过来:把代码变成只用于唯一指向对象的符号,部门・分类等属性则保存在数据库的列中。作为属性,无论怎么变化,只需一条UPDATE语句就能解决,代码本身毫发无损。
国家的号码制度也是按这一原则设计的。个人番号是将住民票代码以「不施加任何人为意图的方法」转换生成的、不带任何意义的11位数字+1位校验数字。1 之所以不赋予意义,一方面是为了不让人从号码推测出个人属性,另一方面也是为了让号码不会因属性变更(搬家或改姓)而改变。
话虽如此,实务中「希望一看开头就能分辨是客户还是供应商」这样的需求依然根深蒂固,整理成判断表如下。
| 方式 | 示例 | 优势 | 破绽之处 | 适用场景 |
|---|---|---|---|---|
| 完全有意义编码 | 02-104-317(部门-分类-流水号) |
一看即可知晓属性 | 组织改编・分类变更・局部位数耗尽时需重新编号 | 仅适用于对象数量不增减、分类在制度上固定不变的情况 |
| 无意义流水号 | 10000317 |
无论什么发生变化都不会崩溃,编号方式简单 | 人无法从代码本身读取任何信息 | 以画面・单据始终并列显示属性为前提的系统运用 |
| 混合式 | C-0317-5(类型1个字符+流水号+校验数字) |
至少能防止类型上的误认,破绽因素最少 | 类型定义变更(罕见) | 拿不定主意时选这个。类型的粒度控制在「客户/供应商/商品」左右即可 |
推荐使用表格中下面两种。请这样理解:嵌入的意义越多,破绽的隐患就越多。
3. 字符种类与位数 ── 信息效率与可读性的权衡
接下来要决定的是字符种类与位数。这纯粹是算术问题:每一位可用的字符种类越多,同样的位数就能表示越多的对象。
| 字符种类 | 6位可表示的数量 | 口头传达・手写的耐受性 |
|---|---|---|
| 仅数字(10种) | 100万 | ◎ 对电话・传真・手写有很强的适应性 |
| 从英文大写字母+数字中去除易混淆字符后的32种(Crockford Base323) | 约10.7亿 | ○ 已做误读对策,但口头传达仍不及纯数字 |
| 英文大写字母+数字(36种) | 约21.8亿 | △ 必然会出现0与O、1与I的误读 |
判断基准有以下两点。
- 该代码是否会通过电话口头传达,或被手写・传真?只要符合其中一项,就推荐只使用数字。一旦混入英文字母,就会产生「是I还是1?」这样的确认成本与误读风险。
- 若只用数字,位数是否会变得过长?只有当对象数量超过数千万件等、需要压缩位数的情况下,才应考虑使用字母数字组合。即使在这种情况下,也不应使用未加处理的36种字符,而应使用像Crockford Base32这样、去除了I・L・O(以及为避免意外拼出粗俗词汇而去除的U)的专门设计过的字母表。这一规范对误读对策做得十分彻底,解码时同时接受小写字母,并将
i或l解释为1、o解释为0。3
位数不应从「当前件数」倒推,而应从「系统与单据存续的20〜30年间可能达到的件数」倒推,并在此基础上再加1位余量。换句话说,也就是要确保足以表示「可能达到的件数的10倍」的位数(若为10万件,则为6位)。如果要加校验位,这1位应与这份余量分开计算(第4章)。第1章和第9章所写的「未来件数的10倍+1位」,指的正是「本体=10倍那部分」与「校验数字1位」的总和。若预计有10万件,计算方式就是本体6位+校验数字1位,合计7位。
位数溢出的可怕之处将在第8章讨论,但为了不让自己公司发生像1998年邮政编码从5位变为7位那样的全面改造,所需付出的成本不过是多加1位而已。
还有一点看似不起眼却很有效,那就是分隔符。正如信用卡号每4位打印一组一样,人类只能准确处理3〜4位一组的长数字串。若要在单据或画面上显示8位以上的代码,应像 1234-5678 这样分隔显示。不过原则上,分隔符不应包含在数据中,而应在显示时附加(第6章)。
4. 校验位 ── 让代码自身检测输入错误
4.1 输入错误的真面目分为两种
校验位(校验数字)是附加在代码末尾(或开头)的一位数字,通过让它能够由其他各位计算得出,从而在输入代码的当场就能检测出错误的机制。个人番号的校验数字,其政令中也明确写明目的是「以确认输入电子计算机时没有错误为目的」。1
什么样的计算式好,取决于人会犯什么样的错误。根据分析了荷兰邮政转账系统等误记数据的Verhoeff经典调查,60〜95%的错误是仅1位打错(单一错误),这一类型占绝对多数。其次是2位错误,占10〜20%,其中大部分是相邻两位的错误,尤其是ab→ba型的互换(换位错误)。除此之外的类型(aa→bb型、隔一位的互换等),各自都只占全体的0.5〜1.5%左右。2
也就是说,校验位方式的性能,实质上可以用「能检测出多少1位错误」与「能检测出多少相邻换位错误」这两点来评价。
4.2 实际使用中的方式与检测能力(判断表)
在阅读表格之前,先约定一下方式名称的读法。「模〇〇」的意思是「使用除以〇〇所得余数的方式」(模11就是除以11的余数)。「权重」是各位所乘的倍率,「权重3-1」表示从右端起交替乘以3倍・1倍・3倍・1倍……。无论哪种方式,做的事情都只是「给各位乘以规定的倍率后求和,再从除以规定数所得的余数中生成校验用的1位数字」。
| 方式 | 应用场所 | 1位错误 | 相邻换位 | 特点 |
|---|---|---|---|---|
| 模10・权重3-1 | JAN码等GS1标准、ISBN-136 | 全部检出 | 漏检差值为5的组合(05↔50、16↔61等) |
若是条码运用,事实上只有这一个选择 |
| Luhn(模10) | 信用卡号7 | 全部检出 | 仅漏检09↔90这一组 |
实现最为简单,是自研代码的默认候选 |
| 模11(加权) | 个人番号8、旧版ISBN | 几乎全部检出 | 几乎全部检出 | 检出力高,但存在「余数收尾」问题(后述) |
| 模9(加权) | 法人番号9 | 漏检0↔9 |
漏检0↔9这一组 |
由于除以9的缘故,无法区分0与9 |
| Damm / Verhoeff | 学术性方式 | 全部检出 | 全部检出 | 通过查数表(Damm10)或群运算(Verhoeff2)来实现 |
补充几点说明。
- JAN方式(模10・权重3-1)从右端起依次交替乘以3倍・1倍后求和,将「10 −(合计除以10的余数)」的个位数作为校验位。6 换位不改变总和的情况,出现在3倍与1倍之差×(两位数字之差)恰好是10的倍数时,也就是两位数字之差恰好为5的时候,只有这种模式无法被检测出来。
- Luhn是1954年由IBM的H. P. Luhn申请专利的方式7,将每隔一位的数字乘以2、超过9就减去9,再求和。相邻换位错误中漏检的只有
09↔90这一组。它在实现简单性与检测力之间取得了良好平衡,若要为自研代码新引入方式,首选它基本不会有问题。 - 个人番号的模11,得益于11是质数,理论上的检测力很高,但存在一个折叠规则──当余数为0或1时,校验位一律设为08,因此只有在这两类之间移动的错误无法被检测出来。旧版ISBN(ISBN-10)用同样的模11、通过「把余数10写作
X」的方式解决了这个问题,但这次又带来了「本应是数字的位置上出现了X」这种运用上的麻烦。若要采用模11系列方式,应当知道「如何将11种余数塞进10种数字」这一问题必然如影随形。 - 法人番号的模9是一种把校验位放在开头的少见设计11,但由于使用除以9所得的余数,
0与9会合同,因此这两者的打错・互换会被直接放过。 - Damm与Verhoeff的方式能够同时检测出全部的1位错误与全部的相邻换位错误。最早构造出能检测全部1位错误与全部相邻换位错误的十进制代码的是Verhoeff2,而Damm则给出了一种更简单的构造方式,只需查阅一张准群(quasigroup)运算表即可10。若以检测力为最优先,应选择这些方式,但作为业务系统的输入错误对策,Luhn或JAN方式在实用上已经足够。
本表之所以用「1位错误」与「相邻换位」这两列来比较各方式,是因为4.1节中提到的Verhoeff调查──即对荷兰邮政转账系统等、人们实际转录・输入的号码误记记录按类型汇总的结果──显示这两种类型占据了错误的绝大多数。2 反过来说,如果贵公司代码中实际发生的错误倾向明显不同(例如手写的1与7读错占大多数),那么与其比较各种方式,不如重新审视字符种类或单据格式更为有效。
4.3 应该加校验位的代码、不需要加的代码
判断基准是「是否经过人的手与眼」。从纸质订单录入的商品编码、通过电话传达的会员编号、现场手写的单据编号,都有加上校验位的价值。反之,仅在系统间联动中流转的内部ID、只能通过画面选择输入的代码则不需要。因为如果没有产生错误的源头(人),检测也就失去了意义。
5. 实现示例 ── C#与Excel・VBA
主要方式无一例外,都可以用数行到十几行代码实现。只需注意一点:代码要始终以字符串形式接收(理由见第6章)。
C#
首先是JAN方式(GTIN-13)。从12位本体计算出校验位。6
public static int Gtin13CheckDigit(string body12)
{
if (body12.Length != 12 || !body12.All(char.IsAsciiDigit))
throw new ArgumentException("请指定12位数字", nameof(body12));
int sum = 0;
for (int i = 0; i < 12; i++)
{
int digit = body12[11 - i] - '0'; // 从右端开始数
sum += (i % 2 == 0) ? digit * 3 : digit; // 右端为×3,此后交替
}
return (10 - sum % 10) % 10;
}
接下来是Luhn。若要为自研的客户编码・会员编号加校验位,这两种方式就足够了。
public static int LuhnCheckDigit(string body)
{
int sum = 0;
for (int i = 0; i < body.Length; i++)
{
int digit = body[body.Length - 1 - i] - '0';
if (i % 2 == 0) // 从校验位的相邻位起,每隔一位乘以2
{
digit *= 2;
if (digit > 9) digit -= 9;
}
sum += digit;
}
return (10 - sum % 10) % 10;
}
public static bool IsValidLuhn(string code) =>
code.Length >= 2 && code.All(char.IsAsciiDigit) &&
LuhnCheckDigit(code[..^1]) == code[^1] - '0';
这段代码到底在做什么,只要手动跟着算一遍,就能记住。以客户编码本体 1000317 为例,来计算一下校验位。从本体右端数起第1、3、5……位乘以2,若乘以2后的结果超过9就减去9,仅此而已。
| 本体的位(从右起) | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
|---|---|---|---|---|---|---|---|
| 数字 | 7 | 1 | 3 | 0 | 0 | 0 | 1 |
| 乘以2 | ○ | ○ | ○ | ○ | |||
| 计算后 | 14→5 | 1 | 6 | 0 | 0 | 0 | 2 |
合计为 5+1+6+0+0+0+2 = 14。校验位是「10 −(合计除以10的余数)」的个位数,所以 10 − 4 = 6。完成后的代码为 10003176。这与上面LuhnCheckDigit("1000317")返回的值一致。
验证已生成的代码时,把校验位也包含在内,从右端数起,将第2、4……位乘以2后求和。6+5+1+6+0+0+0+2 = 20,能被10整除,因此是正确的代码。试着输入几个常见的打错情况看看。
- 将相邻两位互换、打成
10003716时:6+2+7+6+0+0+0+2 = 23。不能被10整除,因此可以检测出来。 - 只打错1位、打成
10008176时:6+5+1+7+0+0+0+2 = 21。这同样可以检测出来。
由于判定基准仅仅是「合计是否为10的倍数」,因此即使现场只有计算器,也能验算。正如第4章所述,Luhn唯一会漏过的,是09↔90的互换。
这里也附上可用于客户主数据输入检查的法人番号验证代码。它是将省令中的算式9原样写出的实现(开头1位是校验数字,接下来的12位是基础号码)。
public static bool IsValidCorporateNumber(string code)
{
if (code.Length != 13 || !code.All(char.IsAsciiDigit)) return false;
int sum = 0;
for (int n = 1; n <= 12; n++)
{
int p = code[13 - n] - '0'; // 以基础号码的最低位作为n=1位
sum += p * (n % 2 == 0 ? 2 : 1); // 奇数位×1,偶数位×2
}
return 9 - sum % 9 == code[0] - '0';
}
用国税厅资料中的示例(公司法人等号码 700110005901 → 法人番号 8700110005901)来验算:奇数位之和为11,偶数位之和13×2=37,37除以9的余数为1,9−1=8,两者一致。11
Excel公式与VBA
在用Excel盘点迁移前既有主数据的场景,或信息系统部门检查业务部门分发的名单的场景中,有时会想在不启动开发环境的情况下当场验证。若是Luhn验证,仅用一个公式就能写出来(假设代码存放在A2单元格。使用Microsoft 365或Excel 2021及以后版本的LET・SEQUENCE函数)。
=LET(s,A2&"", n,LEN(s), i,SEQUENCE(n),
d,MID(s,i,1)*1,
x,IF(MOD(n-i,2)=1, d*2, d),
y,IF(x>9, x-9, x),
MOD(SUM(y),10)=0)
该公式是把n-i为奇数的位,也就是从右数第2、4……位乘以2,超过9则减去9,求和后判断能否被10整除。这与前面的手算步骤完全一致。请注意,如果单元格中的代码是以数值形式存入的,开头的零会丢失(第6章)。应先把该列转换为文本,再进行验证。
如果希望连旧版Excel也能分发使用,或者想区分使用多种方式,可以做成VBA函数,这样就能像=IsValidLuhn(A2)一样从工作表中调用。
' Luhn方式的验证。为避免开头的零丢失,务必以字符串形式传入。
Public Function IsValidLuhn(ByVal code As String) As Boolean
Dim i As Long, n As Long, d As Long, total As Long
Dim c As String
n = Len(code)
If n < 2 Then Exit Function ' 返回默认值 False
For i = 1 To n
c = Mid$(code, n - i + 1, 1) ' 从右端开始数
If c < "0" Or c > "9" Then Exit Function
d = CLng(c)
If i Mod 2 = 0 Then ' 从右数第2、4……位乘以2
d = d * 2
If d > 9 Then d = d - 9
End If
total = total + d
Next i
IsValidLuhn = (total Mod 10 = 0)
End Function
以同样的结构,也能写出JAN方式(从右端起3倍・1倍)或法人番号(奇数位×1・偶数位×2)的验证。区别只在于倍率与除数不同。
在输入界面上的用法也有一个小窍门。当校验位不一致时,不要只显示「代码不正确」,而应查询主数据并复述显示名称(「10003176: ○○股份有限公司,确认无误吗?」),做到这一步,就连侥幸通过校验位检测的误输入(打成实际存在的另一个代码的情况),也能被人眼捕捉到。
6. 现场必踩的陷阱
即使编码体系本身设计得再好,也存在会被实现与运用毁掉的常见模式。
- Excel的开头零丢失与15位舍入。Excel一旦把单元格内容解读为数值,就会删除开头的零,而且由于数值的有效精度为15位,第16位及以后会被替换为0。4 也就是说,客户编码
00123会变成123,而信用卡号级别的长号码,末尾会莫名其妙地变成0。存在CSV联动的系统,其代码应从一开始就将「在所有路径上都作为字符串处理」(在Power Query中指定为文本类型、在导入端指定列类型)纳入运用规程。 - 在数据库中存成数值类型。开头的零会消失,这一点和Excel一样;而且原本想用
BETWEEN「按代码区间筛选」,结果却会把位数不同的代码也牵连进来。代码本来就不是计算对象,因此原则上应采用位数固定的字符串类型。排序顺序也应按字符串来设计(只要位数固定,字符串排序就等同于数值排序)。 - 把连字符包含进数据里。一旦
1234-5678与12345678作为不同记录混在一起,就无法收场了。保存时使用纯粹的代码,分隔符在显示时附加。输入时应先去除连字符与空格,再进行校验。 - 大小写的波动。若使用英文字母,应在保存前统一正规化为大写,而不是依赖数据库的排序规则(collation),而应自行统一。
- 空号的重复利用。「客户编码1000317已经解约,转给新客户用吧」这种做法是禁忌。过去的账单・日志・客户系统中都残留着旧的对应关系,在审计或故障调查时会指向错误的对象。原则上代码应永久空号。
7. 不应自行决定的代码 ── 采用现有标准的情形
仅在公司内部使用的代码可以自由设计,但如果要把条码印在商品上、流通到公司外部(零售・电商・物流),就应使用JAN码(GTIN),而不是自行设计的代码。JAN码需要获得GS1事业者代码的授权后才能设定,不能由自己公司随意决定。5 校验位的计算方法也由标准规定。6
此时设计上的要点在于,不要勉强将公司内部代码与JAN码统一为一体。由于即使是同一商品,也会因装箱数不同而对应不同的JAN码,或因规格变更而使JAN码改变,因此常规做法是在商品主数据中把「公司内部商品编码(相当于主键,由自己公司编号)」与「JAN码(属性,可以持有多个)」作为不同的列分别保存。这里同样适用「代码是识别,意义(与外部标准的对应关系)是属性」这一原则。
8. 编码体系的寿命与迁移
无论设计得多么细致周到,编码体系终究会迎来寿命的尽头。典型情况就是位数溢出。当流水号的上限逼近时,唯一的选择就是「增加位数」,但位数不仅烧录在主数据的列定义中,还烧录在单据版式、条码的打印宽度、与客户之间的联动文件规格,乃至客户一方的系统里。也就是说,需要在自己公司和所有客户之间,实施一次类似1998年邮政编码7位化那样的迁移。
正因如此,第3章中「多留1位余量」才显得重要,但即便如此,一旦真的需要迁移,原则也有以下3条。
- 建立新旧对照主数据,在迁移期间让两种代码都能被检索到。因为客户的咨询会用旧代码来提出。
- 若已将内部键与代码分离,迁移就能被封闭在显示层与主数据的问题范围内。如果数据库的主键直接使用业务代码本身,全部表的外键都会被牵连进去。新设计中建议将内部键(自动编号)与显示用代码分开。
- 架构变更应通过版本管理的迁移来分发。将代码列的位数变更确实地推送到全部客户・全部环境的方法,已在《DB架构的迁移文章》中讨论过。
9. 总结
- 编码设计的第一原则是「代码是识别,意义是属性」。把部门或分类烧录进代码,组织与分类的变化就会直接变成代码的破绽。个人番号是无意义号码,这并非偶然。1
- 字符种类与位数是信息效率与可读性之间的权衡。若涉及口头传达・手写,只用数字;若想压缩位数,则使用已做误读对策的字母表(Crockford Base323)。位数以未来件数的10倍+1位为准。
- 输入错误的实际情况是「1位错误占6〜9成,相邻换位占据剩余部分的大半」。2 对经过人手的代码加上校验位,拿不定主意时用Luhn,要载入条码则用GS1标准6。模11系列的「余数收尾」问题,以及法人番号的模9无法区分0与9,都是选定方式时应了解的特性。
- 实现时以始终使用字符串为原则。Excel的开头零丢失・15位舍入4、存成数值类型、混入连字符、重复利用空号,是现场的四大事故。
- 流通到外部的商品编码应采用JAN(GS1),并与公司内部代码用不同的列分别保存。为应对位数溢出的迁移,内部键与显示用代码应事先分离。
编码体系是一种「事实上的外部规格」──一旦分发出去,之后再修正的成本会呈数量级增长。在新系统的需求定义中一旦谈到编码问题,请在讨论画面或功能之前,先把本文的检查清单过一遍。
相关文章
- 业务应用数据库架构的版本管理 ── 防止「每个客户数据库都不一样」的迁移实践
- 把 Excel 台账迁移到 SharePoint 列表 ── 用共享・历史记录・流程联动告别「台账损坏」
- 用 PowerShell 自动化 Excel・CSV 业务处理 ── 汇总・核对・报表输出的实务方案
- Windows应用的数据存储位置怎么选 ── SQLite / JSON / 注册表 / Access 判断表
相关咨询领域
合同会社小村软件承接业务系统新建・替换时的编码体系・主数据设计,既有编码体系位数溢出・重复的调查与迁移计划,以及输入检查(校验位・主数据核对)的实现。像本文中校验位方式的比较一样,将「哪种方式能检测出什么样的错误、能检测多少」用公式评估并落地为设计判断的咨询,我们也作为数理咨询领域的业务受理。
参考链接
-
行政手续中用于识别特定个人的号码利用等相关法律施行令(平成26年政令第155号) 第六条. 关于应作为个人番号的号码,由转换住民票代码、以不施加任何人为意图的方法生成的11位号码,与其后附加的1位校验数字(以确认将个人番号输入电子计算机时没有错误为目的而算出的0至9的整数)构成。 ↩ ↩2 ↩3 ↩4
-
J. Verhoeff, Error Detecting Decimal Codes, Mathematical Centre Tracts 29, Mathematisch Centrum, Amsterdam. 关于基于实际系统误记样本分析得出的错误类型频率(1位错误占60〜95%、为最大的类型;2位错误占10〜20%,其中大部分是相邻位的换位;twin error等少数类型各占0.5〜1.5%),以及作者构造出能检测全部1位错误与全部相邻换位(该文献中的transposition指相邻位的互换)的十进制代码。 ↩ ↩2 ↩3 ↩4 ↩5 ↩6
-
Douglas Crockford, Base 32. 关于从32个字符的字母表中排除了与1容易混淆的I・L、与0容易混淆的O(以及为避免意外拼出粗俗词汇而排除的U),解码时同时接受大小写字母、把i・l当作1、把o当作0处理,以及基于mod 37的校验符号机制。 ↩ ↩2 ↩3 ↩4
-
Microsoft支持, 保留开头的零和较大数值. 关于Excel数值的有效精度最多为15位,对于信用卡号这类16位及以上的数值,超过15位的部分会被替换为0,开头的零会被删除,以及将列作为文本处理的规避方法。 ↩ ↩2 ↩3
-
GS1 Japan, GS1事业者代码・GTIN(JAN码). 关于使用JAN码需要办理获得GS1事业者代码授权的登记手续。 ↩ ↩2
-
GS1 Japan, 校验位的计算方法. 关于GTIN-13(JAN码标准类型)的校验位,是通过从右端起依次交替乘以3倍・1倍求和后,用「10减去(合计除以10的余数)」来计算得出的。 ↩ ↩2 ↩3 ↩4 ↩5
-
H. P. Luhn, US Patent 2,950,048 “Computer for Verifying Numbers” (1954年申请,1960年获批). 关于在原号码右端附加校验位,并使用替代数字(乘以2后的数字各位之和)通过交叉相加来验证号码的方式。 ↩ ↩2
-
行政手续中用于识别特定个人的号码利用等相关法律所规定的个人番号・个人番号卡・特定个人信息提供等相关命令(平成26年总务省令第85号) 第五条. 关于校验数字的算式(将除校验数字外的11位中、从最低位数起第n位的数字Pn,乘以权重Qn──当1≦n≦6时为n+1,当7≦n≦11时为n−5──后求和,除以11,用11减去余数;余数为1以下时取0)。 ↩ ↩2
-
关于法人番号指定等的省令(平成26年财务省令第70号) 第二条. 关于法人番号校验数字的算式(将基础号码中从最低位数起第n位的数字Pn,乘以权重Qn──n为奇数时为1,为偶数时为2──后求和,除以9,用9减去余数)。 ↩ ↩2
-
H. M. Damm, Totally anti-symmetric quasigroups for all orders n≠2,6, Discrete Mathematics, Vol. 307, 2007. 关于作为能检测全部1位错误与相邻换位的校验位方式基础的完全反对称准群,在除阶数2・6以外的所有阶数下都存在。 ↩ ↩2
-
国税厅, 校验位的计算. 关于法人番号由12位基础号码及附加在其前的1位校验数字构成,以及从公司法人等号码700110005901计算出校验位8的计算示例。 ↩ ↩2
相关文章
共享相同标签的最新文章。可以围绕相近的主题进一步加深理解。
业务应用数据库架构的版本管理 ── 防止「每个客户数据库都不一样」的迁移实践
一份为分散在各客户处的业务应用数据库架构做版本管理的实践指南。整理了 PRAGMA user_version 与前向迁移的 C# 实现、EF Core Migrations・DbUp・自行实现的判断表,以及两阶段发布策略。
WinForms / WPF 应用的 CI/CD 实践 ── 用 GitHub Actions 实现从构建到签名、发布的自动化
一份用 GitHub Actions 搭建 WinForms / WPF 应用 CI/CD 的实务指南。整理了在 windows-latest 上构建+测试的最小 YAML、标签驱动的版本编号、集成 signtool 完成签名,以及按 MSI/MSIX/ClickOnce/...
Windows 的进程间通信该怎么选 ── 命名管道 / TCP / gRPC / 共享内存 / COM 判断表
整理 Windows 应用程序之间应该如何选择通信方式。用判断表整理命名管道、本地 TCP、gRPC、共享内存、文件对接、COM 各自的优势与陷阱,并从实务角度说明 UI+服务分离・32bit/64bit 桥接・权限边界等常见架构,以及命名管道的实现示例。
Windows应用的数据存储位置怎么选 ── SQLite / JSON / 注册表 / Access 判断表
Windows桌面应用的数据该存在哪里、用什么格式保存?本文整理AppData/ProgramData的使用区分,以及SQLite、JSON文件、注册表、Access(.accdb)各自的优势与陷阱,并附判断表,从实务角度讲解防止数据损坏与位数问题等注意事项。
在桌面应用中使用 .NET Generic Host 与 BackgroundService 的理由
本文整理在 Windows 工具或常驻应用中,如何使用 Generic Host 与 BackgroundService 来梳理启动、定期处理、退出处理、日志、配置与 DI。
相关主题
与本文相近的主题页面。以本文为起点,可以进一步了解相关服务和其他文章。
Windows 技术主题
汇整 KomuraSoft LLC 关于 Windows 开发、故障调查与既有资产活用文章的主题中心。
与本主题相关的服务
本文与以下服务页面相关联,欢迎从最接近的入口查看。
Windows 应用程序开发
支持包含常驻处理、设备联动、运行日志与可维护结构的 Windows 桌面应用程序。
技术咨询 & 设计评审
协助梳理设计方向、架构边界、生命周期责任,以及既有 Windows 资产的处理方式。
常见问题
汇总了咨询这一主题时常见的问题。
- 校验位应该加在什么样的代码上?
- 应加在人工输入・转录・口头传达所涉及的代码上。典型例子包括从纸质单据录入的商品编码、通过电话传达的会员编号、现场手写的单据编号。反之,仅在系统之间流转的内部ID(如数据库主键)则不需要。因为不经过人手的代码不会发生打错的情况,校验位的目的正是检测输入错误。个人番号的校验数字,法令上也明确将其目的写作「确认输入电子计算机时没有错误」。
- 可以让商品编码承载部门或分类的意义吗?
- 原则是「代码只负责识别,意义作为属性保存在数据库中」。如果把部门・分类・年度等嵌入代码的位数中,每次组织改编或分类变更都需要重新编号,与过去单据・交付给客户的号码之间的核对也会因此崩溃。如果无论如何都想让人一眼分辨,实务上比较合适的落脚点是:仅用1个字符左右的前缀来表示类型,其余部分则采用流水号。
- 可以给既有的编码体系事后加上校验位吗?
- 技术上是可行的,但由于位数会增加1位,主数据・全部单据・与客户之间的数据联动・印刷品都会受到影响。实质上这会变成一次编码体系的迁移项目,因此现实的做法是把它安排在因应对位数溢出等原因而刷新体系的时机上。在那之前作为过渡措施,只要在输入画面上做主数据存在性检查(核对输入的代码是否实际存在)并复述显示名称,就能相当程度地减少误输入造成的实际损害。
- 可以把UUID或ULID用作业务代码吗?
- 作为数据库的内部键没有问题,但不适合用作供人朗读・转录的「显示用代码」。因为UUID长达36个字符,太长了,无法承受电话或传真的传达。若采用将内部键(UUID或自动流水号)与展示给人看的显示用代码(短流水号+校验位)分离的双层结构,就能同时满足两方面的需求。即使将来需要改变显示用代码的体系,只要内部键保持稳定,影响范围就能被封闭在显示层内。