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


113

我碰到了这篇文章( MySQL中tinyint,smallint,mediumint,bigint和int有什么区别?),并且意识到PostgreSQL不支持无符号整数。

谁能帮忙解释为什么会这样吗?

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

谢谢。


尚未但很快,我们正在考虑迁移到PostgreSQL。
Adrian Hoe

4
我认为这不是问为什么做出某些决定的最佳地方,其中PostgreSQL邮件列表之一可能更合适。如果要自动递增值,请使用serial(1到2147483647)或bigserial(1到9223372036854775807)。有符号的64位整数可能提供了足够的空间。
亩太短了

4
谢谢@muistooshort。这回答了主要的关键问题。但是,无符号整数既不是自动递增的,也不是主键呢?我确实有存储无符号整数的列,其范围从0到2 ^ 32。
Adrian Hoe

4
快速浏览PostgreSQL文档(postgresql.org/docs/current/interactive/index.html)可能有助于您更好地了解PostgreSQL的功能。这些天我使用MySQL的唯一原因是,如果我已经对它进行了很多投资:PostgreSQL速度快,具有有用的功能,并且由对数据非常偏执的人构建。IMO当然是:)
mu太短

再次感谢@muistooshort提供的指针。
Adrian Hoe

Answers:


46

已经回答了为什么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”


但是我猜想,每次我们想要一个无符号列都使用该域时,会在INSERT / UPDATE上产生开销。最好在确实需要(很少见)的地方使用它,并习惯于这样的想法,即数据类型没有设置我们想要的下限。毕竟,它也提出了一个上限,从逻辑的角度来看,这通常是没有意义的。并非为了强制应用程序约束而设计数字类型。
Federico Razzoli

这种方法的唯一问题是您正在“浪费”未使用的15位数据存储。更不用说检查也要花费一些效率。更好的解决方案是Postgres将unsigned添加为第一类类型。在具有2000万条记录的表中,带有和这样的索引字段的表将浪费40MB的空间用于未使用的位。如果您要在另外20个表上滥用它,那么您现在浪费的是800MB空间。
tpartee

85

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

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

就是说,没有理由不能完成它。这只是很多工作。


35
这个问题很受欢迎,我已经着手解决这个问题:github.com/petere/pguint
Peter Eisentraut

但是,对无符号整数文字进行输入/输出转换将非常有用。甚至只是to_char模式。
Bergi

36

您可以使用CHECK约束,例如:

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

另外,PostgreSQL具有smallserialserialbigserial用于自动增量的类型。


2
值得一提的是,使用CHECK的列中不能包含任何NULL。
Minutis

1
@Minutis确定您不能拥有x是NULL或x在4与40之间
jgmjgm

这不能为您提供与unsigned int相同的分辨率。表示unsigned int可以达到2^32-1,而signed int可以达到2^31-1
JukesOnYou

2
NULL并且CHECK是完全正交的 您可以有NULL/ NOT NULL列,带或不带CHECK。请注意,根据postgresql.org/docs/9.4/ddl-constraints.html上的文档,CHECK返回NULL的结果为TRUE,因此,如果您确实想防止NULL,请使用NOT NULL代替(或除之外CHECK)。
flaviovs

使用CHECK不允许我在其中存储ipv4地址integer(至少不要让它们随机地
变为

5

关于DOMAINS的讨论很有趣,但与该问题的唯一可能来源无关。对无符号整数的需求是将相同位数的整数范围加倍,这是一个效率参数,而不是排除负数的需求,每个人都知道如何添加检查约束。

有人问到它时,Tome Lane说:

基本上,除非您能找到一种使它们适合数字促销层次结构且不会破坏许多现有应用程序的方法,否则这种情况发生的可能性为零。如果内存可用,我们已经不止一次地进行了研究,并且未能提出一种似乎没有违反POLA的可行设计。

什么是“ POLA”?Google给了我10条毫无意义的结果。不知道这是否是政治错误的想法,因此受到审查。为什么这个搜索词不会产生任何结果?随你。

您可以将无符号的int实现为扩展类型,而不会带来太多麻烦。如果使用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
)

最少的两个功能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_funcs.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);
}

嗯,或者您可以发现它已经完成


1
我猜POLA是“最少惊讶的原则”。它表明这种改变有可能以意想不到的方式改变现有的行为。
Eval医生

1

根据最新文档,支持单数整数,但表中没有无符号整数。但是,串行类型与unsigned相似,只是它从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

0

Postgres确实具有许多人都不知道的无符号整数类型OID

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

oid类型本身只有很少的操作无法比较。但是,可以将其强制转换为整数,然后使用标准整数运算符对其进行操作。(如果要这样做,请当心可能的签名与未签名的混淆。)

它不是数字类型,尝试对其进行任何算术(甚至是按位运算)都将失败。另外,它只有4个字节(INTEGER),没有相应的8个字节(BIGINT)无符号类型。

因此,自己使用它并不是一个好主意,我也同意所有其他答案,在Postgresql数据库设计中,您应始终对串行主键使用INTEGERor BIGINT列-使其以负号()开头或允许它如果要用尽整个域,请使用()换行。MINVALUECYCLE

但是,它对于输入/输出转换非常有用,例如从另一个DBMS进行迁移。将值2147483648插入整数列将导致“ 错误:整数超出范围 ”,而使用表达式则2147483648::OID可以正常工作。
同样,当使用来选择整数列作为文本时mycolumn::TEXT,您有时会得到负值,但mycolumn::OID::TEXT始终会得到一个自然数。

请参阅dbfiddle.uk上的示例


如果您不需要操作,那么使用OID的唯一价值就是排序顺序有效。如果那是您需要的,那很好。但是很快有人会想要一个uint8,然后他们也迷路了。最重要的是,要存储32位或64位值,您可以分别使用int4和int8,只需要小心操作即可。但是编写扩展很容易。
Gunther Schadow
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.