F.19. isn

isn模块为下列国际产品编号标准提供数据类型:EAN13、UPC、ISBN(图书)、ISMN(音乐)以及 ISSN(期刊)。在输入时会按照一个硬编码的前缀列表对输入进行验证,这个前缀的列表也被用来在输出时连接号码。因为新的前缀总是不时地出现,这个前缀列表可能会过时。这个模块的一个未来版本有希望得到一个来自于一个或多个表的前缀列表,这样用户可以根据需要来方便地更新前缀列表。不过,在当前该列表只能通过修改源代码并且重新编译来更新。另外一种方案是,在这个模块的未来版本中可能会直接移除掉前缀验证和连接支持。

F.19.1. 数据类型

Table F-12展示了isn模块提供的数据类型。

Table F-12. isn 数据类型

数据类型描述
EAN13 欧洲文章号,总是以 EAN13 格式显示
ISBN13 国际标准图书号,以新的 EAN13 格式显示
ISMN13 国际标准音乐号,以新的 EAN13 格式显示
ISSN13 国际标准期刊号,以新的 EAN13 格式显示
ISBN 国际标准图书号,以旧的短格式显示
ISMN 国际标准音乐号,以旧的短格式显示
ISSN 国际标准期刊号,以旧的短格式显示
UPC 通用产品代码

一些注记:

  1. ISBN13、ISMN13、ISSN13 号码都是 EAN13 号码。

  2. EAN13 号码不总是 ISBN13、ISMN13 或 ISSN13(有些是)。

  3. 一些 ISBN13 号码能够作为 ISBN 显示。

  4. 一些 ISMN13 号码能够作为 ISMN 显示。

  5. 一些 ISSN13 号码能够作为 ISSN 显示。

  6. UPC 号码是 EAN13 号码的一个子集(它们基本上是去掉了第一个0位的 EAN13)。

  7. 所有 UPC、ISBN、ISMN 以及 ISSN 号码可以被表示为 EAN13 号码。

在内部,所有这些类型使用同一种表达(一个 64 位整数)并且所有内部表达是可以互换的。多种类型被提供来控制显示格式化并且对假定为表示一种特定类型号码的输入进行更严格的合法性检查。

在可能时,ISBNISMNISSN类型将显示号码的短版本(ISxN 10),并且在无法适应短版本时显示号码的 ISxN 13 格式。EAN13ISBN13ISMN13ISSN13类型总是显示长版本的 ISxN(EAN13)。

F.19.2. 造型

isn模块提供了下列类型之间的造型:

当从EAN13造型为另一种类型时, 会有对该值是否在另一种类型的域中的运行时检查,如果不在则抛出一个错误。其他的造型则是简单地重新贴个标签,因而总是会成功。

F.19.3. 函数和操作符

isn模块提供了标准的比较操作符,外加对所有这些数据类型的 B 树和哈希索引支持。此外还有一些特殊的函数,它们展示在Table F-13中。在这个表中,isn意味着该模块的数据类型中的任何一种。

Table F-13. isn 函数

函数返回描述
isn_weak(boolean)boolean设置弱输入模式(返回新设置)
isn_weak()boolean得到弱模式的当前状态
make_valid(isn)isn验证一个非法号码(清除非法标志)
is_valid(isn)boolean检查非法标志的存在

模式被用来允许插入非法数据到一个表中。非法意味着校验位错误,而不是有丢失号码。

为什么你会想要使用弱模式?你可能有一个巨大的 ISBN 号码集合并且出于某种奇怪的原因其中具有错误的校验位(可能这些号码是从印刷稿中扫描并且 OCR 而来,也可能是手工输入的......谁知道呢)。不管怎样,重点是你可能希望清理这些混乱,但是你仍然想要能够把这些号码放在你的数据库中并且可能会使用一个外部工具在数据库中定位非法号码,这样你能够更容易地验证信息。因此你可能会想要在表中选择所有非法的号码。

当你使用弱模式在一个表中插入非法号码时,被插入的号码将会被加上修正过的校验位,但是它的最后将会有一个感叹号(!),例如0-11-000322-5!。这种非法标志符可以用is_valid函数检查并且可以用make_valid函数清除。

即使不在弱模式中,你也能通过在号码某位追加!字符来强制非法号码的插入。

另一个特殊特性是在输入过程中,你可以写一个?代替校验位,然后正确的校验位将被自动插入。

F.19.4. 例子

--直接使用类型:
SELECT isbn('978-0-393-04002-9');
SELECT isbn13('0901690546');
SELECT issn('1436-4522');

--转换类型:
-- 注意只有在号码处于另一种类型的合法值之中时,才能从 EAN13 转换成另一种类型
-- 因此下面的用法将不会工作: select isbn(ean13('0220356483481'));
-- 但是下面的可以:
SELECT upc(ean13('0220356483481'));
SELECT ean13(upc('220356483481'));

--创建一个表,它有一个单一列来保存 ISBN 号码:
CREATE TABLE test (id isbn);
INSERT INTO test VALUES('9780393040029');

--自动计算校验位(观察 '?'):
INSERT INTO test VALUES('220500896?');
INSERT INTO test VALUES('978055215372?');

SELECT issn('3251231?');
SELECT ismn('979047213542?');

--使用弱模式:
SELECT isn_weak(true);
INSERT INTO test VALUES('978-0-11-000533-4');
INSERT INTO test VALUES('9780141219307');
INSERT INTO test VALUES('2-205-00876-X');
SELECT isn_weak(false);

SELECT id FROM test WHERE NOT is_valid(id);
UPDATE test SET id = make_valid(id) WHERE id = '2-205-00876-X!';

SELECT * FROM test;

SELECT isbn13(id) FROM test;

F.19.5. 参考文献

实现这个模块的信息可以从下列网站收集到:

用于连接的前缀:

创建算法时已经注意严谨地使用 ISBN、ISMN、ISSN 官方用户手册中的推荐算法进行了验证。

F.19.6. 作者

Germán Méndez Bravo (Kronuz), 2004 - 2006

这个模块受到了 Garrett A. Wollman 的isbn_issn代码的启发。