为什么无符号整数在 PostgreSQL 中不可用?

我偶然看到这篇文章(在 MySQL 中 tinyint、 smallint、 mediumint、 bigint 和 int 有什么区别?) ,意识到 PostgreSQL 不支持无符号整数。

有人能解释一下为什么会这样吗?

大多数时候,我在 MySQL 中使用无符号整数作为自动递增的主键。在这种设计中,当我将数据库从 MySQL 移植到 PostgreSQL 时,如何克服这个问题?

谢谢。

110536 次浏览

它不在 SQL 标准中,因此实现它的一般要求较低。

拥有太多不同的整数类型会使类型解析系统更加脆弱,因此在混合类型中添加更多类型会遇到一些阻力。

也就是说,没有理由做不到,只是有很多工作要做。

可以使用 CHECK 约束,例如:

CREATE TABLE products (
id integer,
name text,
price numeric CHECK (price > 0)
);

而且,PostgreSQL 具有 serialsmallserialbigserial类型用于自动增量。

我们已经回答了 postgreql 缺少无符号类型的原因,但是我建议对无符号类型使用域。

Http://www.postgresql.org/docs/9.4/static/sql-createdomain.html

 CREATE DOMAIN name [ AS ] data_type
[ COLLATE collation ]
[ DEFAULT expression ]
[ constraint [ ... ] ]
where constraint is:
[ CONSTRAINT constraint_name ]
{ NOT NULL | NULL | CHECK (expression) }

域类似于类型,但带有额外的约束。

对于一个具体的例子,您可以使用

CREATE DOMAIN uint2 AS int4
CHECK(VALUE >= 0 AND VALUE < 65536);

下面是 psql 在尝试滥用该类型时给出的结果。

DS1 = # select (346346: : uint2) ;

错误: 域 uint2的值违反了检查约束“ uint2 _ check”

根据最新的文档,表中支持有符号整数,但不支持无符号整数。但是,串行类型类似于无符号类型,只不过它从1开始而不是从0开始。但是上限和已签名的是一样的。因此,系统确实没有无符号支持。正如 Peter 所指出的,实现未签名版本的大门是敞开的。代码可能需要更新很多,只是因为我在 C 编程方面的经验太多了。

Https://www.postgresql.org/docs/10/datatype-numeric.html

integer     4 bytes     typical choice for integer  -2147483648 to +2147483647
serial      4 bytes     autoincrementing integer    1 to 2147483647

Postgres 确实有一个许多人都不知道的无符号整数类型: 强 > OID

oid类型当前实现为无符号四字节整数。[ ... ]

oid类型本身很少有无法比较的操作 然而,转换为整数,然后使用标准的 整数运算符。(小心可能出现的有符号与无符号混淆 如果你这么做的话。)

但是它不是 数字类型,并且尝试使用它进行任何算术操作(甚至按位操作)都将失败。而且,它只有4个字节(INTEGER) ,没有相应的8字节(BIGINT)无符号类型。

因此,自己使用这个并不是一个好主意,我同意所有其他的答案,在 Postgreql 数据库设计中,你应该总是使用 INTEGER或者 BIGINT列作为你的 连环杀手主键——如果你想用尽整个域名,就让它以负数开始(MINVALUE)或者允许它包围(CYCLE)。

但是,它对于输入/输出转换非常有用,就像从另一个 DBMS 迁移一样。将值 2147483648插入一个整数列将导致一个“ 错误: 整数超出范围”,而使用表达式 2147483648::OID就可以了。
类似地,当使用 mycolumn::TEXT选择一个整数列作为文本时,您将在某个时刻得到负值,但是使用 mycolumn::OID::TEXT您将始终得到一个自然数。

参见 例如 dbfiddle.uk

关于域的讨论是有趣的,但与该问题的唯一可能起源无关。对无符号整数的期望是用相同数量的比特将整数的范围加倍,这是一个效率参数,而不是排除负数的期望,每个人都知道如何添加检查约束。

被人问起的时候,Tome Lane 说:

基本上,除非你能找到 一种将它们与数字晋升等级相匹配的方法 破坏了许多现有的应用程序 如果我没记错的话,曾经有过一次,但没能想出一个可行的设计 看起来并没有违反波兰法律。

什么是“ POLA”?谷歌给了我10个毫无意义的结果.不确定这是否是政治上不正确的想法,因此受到审查。为什么这个搜索词不会产生任何结果?随便啦。

您可以将无符号整型实现为扩展类型,而不会有太多麻烦。如果使用 C 函数,那么将不会有任何性能损失。您不需要扩展解析器来处理字面值,因为 PgSQL 有一种将字符串解释为字面值的简单方法,只需将’4294966272’: : uint4写为字面值即可。石膏也不是什么大问题。您甚至不需要执行范围异常,只需将“4294966273”: : uint4: : int 的语义处理为 -1024即可。或者你可以抛出一个错误。

如果我想这么做,我早就这么做了。但是因为我在 SQL 的另一端使用 Java,所以对我来说它没有什么价值,因为 Java 也没有那些无符号整数。所以我一无所获。如果我从 bigint 列得到一个 BigInteger,而它应该适合长列,那么我已经很烦恼了。

另外,如果我确实需要存储32位或64位类型,我可以分别使用 PostgreSQL int4或 int8,只要记住自然顺序或算法不会可靠地工作。但是存储和检索不受此影响。


下面是我如何实现一个简单的无符号 int8:

首先我要用

CREATE TYPE name (
INPUT = uint8_in,
OUTPUT = uint8_out
[, RECEIVE = uint8_receive ]
[, SEND = uint8_send ]
[, ANALYZE = uint8_analyze ]
, INTERNALLENGTH = 8
, PASSEDBYVALUE ]
, ALIGNMENT = 8
, STORAGE = plain
, CATEGORY = N
, PREFERRED = false
, DEFAULT = null
)

我必须首先定义的最小的2个函数 uint8_inuint8_out

CREATE FUNCTION uint8_in(cstring)
RETURNS uint8
AS 'uint8_funcs'
LANGUAGE C IMMUTABLE STRICT;


CREATE FUNCTION uint64_out(complex)
RETURNS cstring
AS 'uint8_funcs'
LANGUAGE C IMMUTABLE STRICT;

需要在 C uint8 _ funs.c 中实现它,因此我使用 这个复杂的例子并使它变得简单:

PG_FUNCTION_INFO_V1(complex_in);


Datum complex_in(PG_FUNCTION_ARGS) {
char       *str = PG_GETARG_CSTRING(0);
uint64_t   result;


if(sscanf(str, "%llx" , &result) != 1)
ereport(ERROR,
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
errmsg("invalid input syntax for uint8: \"%s\"", str)));


return (Datum)SET_8_BYTES(result);
}

啊,好吧,或者你可以 只是发现它已经完成了