我看到这篇文章 (What is the difference between tinyint, smallint, mediumint, bigint and int in MySQL?) 并意识到 PostgreSQL 不支持无符号整数。
谁能帮忙解释为什么会这样?
大多数时候,我使用无符号整数作为 MySQL 中的自动递增主键。在这样的设计中,当我将数据库从 MySQL 移植到 PostgreSQL 时,我该如何克服这个问题?
谢谢。
serial
(1 到 2147483647)或 bigserial
(1 到 9223372036854775807)。一个有符号的 64 位整数可能提供了足够多的空间。
它不在 SQL 标准中,因此实施它的一般冲动较低。
拥有太多不同的整数类型会使类型解析系统更加脆弱,因此在混合中添加更多类型存在一些阻力。
也就是说,没有理由不能做到。这只是很多工作。
已经回答了为什么 postgresql 缺少无符号类型。但是,我建议将域用于无符号类型。
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=#选择(346346 :: uint2);错误:域 uint2 的值违反了检查约束“uint2_check”
您可以使用 CHECK 约束,例如:
CREATE TABLE products (
id integer,
name text,
price numeric CHECK (price > 0)
);
此外,PostgreSQL 有 serial
、smallserial
和 bigserial
类型用于自动增量。
2^32-1
,同时有符号 int 可以上升到 2^31-1
。
NULL
和 CHECK
完全正交。您可以拥有带有或不带有 CHECK
的 NULL
/NOT NULL
列。请注意,根据 postgresql.org/docs/9.4/ddl-constraints.html 中的文档,返回 NULL 的 CHECK
计算结果为 TRUE,因此如果您真的想防止 NULL,请改用 NOT NULL
(或除了 CHECK
)。
integer
中(至少要让它们随机正或负,至少......)
关于域的讨论很有趣,但与该问题的唯一可能来源无关。对无符号整数的期望是将具有相同位数的整数范围加倍,这是一个效率参数,而不是排除负数的愿望,每个人都知道如何添加检查约束。
当asked by someone about it时,Tome Lane 说:
基本上,这种情况发生的可能性为零,除非您能找到一种方法将它们放入不会破坏许多现有应用程序的数字提升层次结构中。如果没记错的话,我们已经不止一次地研究过这个问题,但未能提出一个似乎不违反 POLA 的可行设计。
什么是“POLA”? Google gave me 10 results that are meaningless。不确定这是否是政治不正确的想法,因此受到审查。为什么这个搜索词不会产生任何结果?任何。
您可以将无符号整数实现为扩展类型,而不会有太多麻烦。如果您使用 C 函数执行此操作,那么根本不会有性能损失。您不需要扩展解析器来处理文字,因为 PgSQL 有一种将字符串解释为文字的简单方法,只需编写 '4294966272'::uint4 作为文字。演员阵容也不应该是一个大问题。您甚至不需要进行范围异常,您只需将 '4294966273'::uint4::int 的语义视为 -1024。或者你可以抛出一个错误。
如果我想要这个,我会做到的。但是由于我在 SQL 的另一端使用 Java,对我来说它没有什么价值,因为 Java 也没有那些无符号整数。所以我一无所获。如果我从一个 bigint 列中得到一个 BigInteger,我已经很恼火了,而它应该适合 long。
另一件事,如果我确实需要存储 32 位或 64 位类型,我可以分别使用 PostgreSQL int4 或 int8,只要记住自然顺序或算术不会可靠地工作。但是存储和检索不受此影响。
以下是我如何实现一个简单的 unsigned 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_in
和 uint8_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_funcs.c 中实现它。所以我去使用 the complex example from here 并使其变得简单:
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);
}
嗯,或者你可以just find it done already。
根据最新文档,支持有符号整数,但表中没有无符号整数。然而,串行类型有点类似于无符号,除了它从 1 开始而不是从 0 开始。但上限与签名相同。所以系统确实没有未签名的支持。正如彼得所指出的,实现未签名版本的大门是敞开的。代码可能需要更新很多,从我使用 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 类型本身几乎没有可比性的操作。但是,它可以转换为整数,然后使用标准整数运算符进行操作。 (如果您这样做,请注意可能的有符号与无符号混淆。)
但它不是 numeric type,尝试用它做任何算术(甚至按位运算)都会失败。而且,它只有 4 个字节(INTEGER
),没有对应的 8 字节(BIGINT
)无符号类型。
因此,自己使用它并不是一个好主意,我同意所有其他答案,即在 Postgresql 数据库设计中,您应该始终使用 INTEGER
或 BIGINT
列作为 serial 主键 - 让它开始如果您想用尽整个域,则在否定 (MINVALUE
) 中或允许它环绕 (CYCLE
)。
但是,它对于输入/输出转换非常有用,例如从另一个 DBMS 迁移。将值2147483648
插入整数列中,将导致“ 错误:整数超出范围” ,而使用表达式2147483648::OID
则可以正常工作。
类似地,选择整数时列作为带有 mycolumn::TEXT
的文本,您将在某些时候获得负值,但对于 mycolumn::OID::TEXT
,您将始终获得一个自然数。
to_char
模式。