PostgreSQLで符号なし整数が使用できないのはなぜですか?


113

私はこの投稿に遭遇し(MySQLのtinyint、smallint、mediumint、bigint、intの違いは何ですか?)、PostgreSQLが符号なし整数をサポートしていないことに気付きました。

誰もがなぜそうなのかを説明するのを手伝ってくれる?

ほとんどの場合、MySQLでは自動インクリメントの主キーとして符号なし整数を使用します。そのような設計では、データベースをMySQLからPostgreSQLに移植するときにこれをどのように克服できますか?

ありがとう。


まだですが、間もなく、PostgreSQLへの移行を検討しています。
Adrian Hoe

4
これは、なぜ特定の決定がなされたのかを尋ねるのに最適な場所ではないと思います。PostgreSQLメーリングリストの1つがより適しているかもしれません。自動インクリメントする値が必要な場合はserialbigserial(1〜2147483647)または(1〜9223372036854775807)を使用します。符号付き64ビット整数はおそらく十分な余地を提供します。
muが短すぎる

4
@muistooshortに感謝します。それが主要な問題に答えました。しかし、自動インクリメントも主キーもない符号なし整数型はどうでしょうか?0から2 ^ 32の範囲の符号なし整数を格納する列があります。
Adrian Hoe

4
PostgreSQLのドキュメント(postgresql.org/docs/current/interactive/index.html)をざっと見てみると、PostgreSQLの機能をよりよく理解するのに役立ちます。最近、MySQLを使用する唯一の理由は、すでに多くの投資を行っているかどうかです。PostgreSQLは高速で、便利な機能がロードされ、データにかなり偏執的な人々によって構築されています。IMOはもちろん:)
muが短すぎる

ポインタをありがとう@muistooshort。
Adrian Hoe

回答:


47

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 =#select(346346 :: uint2);

エラー:ドメインuint2の値がチェック制約「uint2_check」に違反しています


しかし、無署名の列が必要になるたびにこのドメインを使用すると、INSERT / UPDATEのオーバーヘッドが発生すると思います。本当に必要な場合(これはまれです)にこれを使用し、データ型が私たちが望む下限を設定しないという考えに慣れるだけです。結局、それはまた、論理的な観点からは通常無意味な上限を設定します。数値型は、アプリケーションの制約を強制するように設計されていません。
Federico Razzoli

このアプローチの唯一の問題は、使用されていない15ビットのデータストレージを「浪費」していることです。チェックは言うまでもありませんが、多少の効率が必要です。より良い解決策は、Postgresがunsignedをファーストクラスタイプとして追加することです。2,000万レコードのテーブルで、このようなインデックス付きフィールドを使用すると、未使用ビットに40MBのスペースが無駄になります。別の20個のテーブルでそれを悪用している場合、800MBのスペースを浪費しています。
tpartee

85

これはSQL標準に含まれていないため、実装する一般的な衝動は低くなります。

整数型が多すぎると、型解決システムが壊れやすくなるため、組み合わせに型を追加することに抵抗が生じます。

とはいえ、それができなかった理由はありません。大変な作業です。


35
この質問は、私がそれを修正するために着手したほど十分に人気があります:github.com/petere/pguint
Peter Eisentraut

符号なし整数リテラルの入出力変換があると非常に便利です。またはto_charパターンだけでも。
ベルギ

37

CHECK制約を使用できます。例:

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

また、PostgreSQLにはsmallserial、自動インクリメント用の、、serialおよびbigserialタイプがあります。


2
言及すべきことの1つは、CHECKを使用する列にNULLを含めることはできません。
Minutis 2017

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.htmlCHECKあなたが本当にその後、NULLを防ぐために使用したいのであれば、TRUEにNULLが評価さを返すNOT NULL代わりに(またはそれに加えてCHECK)。
flaviovs

CHECKを使用すると、私は中のIPv4アドレスを格納することはできませんinteger(彼らは少なくとも、ランダムに正または負行かなくてもいない...)
hanshenrikを

5

DOMAINSについての話は興味深いですが、その質問の考えられる唯一の原因には関係ありません。符号なし整数の要望は、同じビット数で整数の範囲を2倍にすることです。これは効率の引数であり、負の数を除外したいという意図ではなく、チェック制約を追加する方法を誰もが知っています。

それについて誰かから尋ねられたとき、トメレーンは述べました:

基本的に、多くの既存のアプリケーションを壊さない数値のプロモーション階層にそれらを適合させる方法を見つけられない限り、これが起こる可能性はゼロです。メモリが機能する場合、これを2回以上確認しましたが、POLAに違反していないように見える実用的なデザインを作成できませんでした。

「ポーラ」とは?グーグルは無意味な10の結果を私に与えました。それが政治的に正しくない考えであり、したがって検閲されているかどうかは不明です。この検索語句で結果が得られないのはなぜですか?なんでも。

unsigned intは、あまり問題なく拡張型として実装できます。C関数でそれを行う場合、パフォーマンスの低下はほとんどありません。PgSQLには文字列をリテラルとして解釈する簡単な方法があるので、パーサーを拡張してリテラルを処理する必要はありません。リテラルとして '4294966272' :: uint4と書くだけです。キャストも大したことではありません。範囲の例外を実行する必要すらありません。'4294966273 ':: uint4 :: intのセマンティクスを-1024として扱うことができます。または、エラーをスローすることもできます。

これが欲しかったなら、私はそれをしたでしょう。しかし、私はSQLの反対側でJavaを使用しているので、Javaにもこれらの符号なし整数がないので、私にとってそれはほとんど価値がありません。だから私は何も得ません。bigintカラムからBigIntegerを取得する場合、長く収まるはずですが、私はすでにイライラしています。

もう1つ、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_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は「最小の驚きの原則」だと思います。これは、変更が予期しない方法で既存の動作を変更する可能性があることを示唆しています。
医師評価

1

最新のドキュメントによると、表では符号付き整数はサポートされていますが、符号なし整数はサポートされていません。ただし、シリアル型は、0からではなく1から始まることを除いて、unsignedに似ています。しかし、上限は歌われると同じです。したがって、システムは本当に署名されていないサポートを持っていません。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タイプは、現在符号なしの4バイト整数として実装されます。[…]

oid型自体は、比較を超えて、いくつかの事業を展開しています。ただし、整数にキャストしてから、標準の整数演算子を使用して操作できます。(これを行う場合は、署名済みと未署名の混同に注意してください。)

ただし、数値型ではないため、算術演算(またはビット演算)を実行しようとしても失敗します。また、4バイト(INTEGER)であり、対応する8バイト(BIGINT)の符号なしタイプはありません。

これを自分で使用するために本当に良いアイデアではない、と私はPostgreSQLデータベースの設計には、必ず使用する必要があることを、すべての他の回答に同意するそれはそうINTEGERか、BIGINTあなたのために列をシリアル主キー-それは(負に開始持つMINVALUE)、またはそれを許しますCYCLE完全なドメインを使い果たしたい場合は、ラップアラウンド()します。

ただし、別のDBMSからの移行など、入出力変換には非常に役立ちます。2147483648整数列に値を挿入すると、「エラー:範囲外の整数」が表示されますが、式を使用しても2147483648::OID問題ありません。
同様に、整数列をでテキストとして選択するとmycolumn::TEXT、ある時点で負の値が得られますが、を使用mycolumn::OID::TEXTすると常に自然数になります。

dbfiddle.ukの例を参照してください。


操作が必要ない場合、OIDを使用することの唯一の価値は、ソート順が機能することです。それが必要な場合は、問題ありません。しかし、すぐに誰かがuint8を必要とし、それからそれらも失われます。要するに、32ビットまたは64ビットの値を格納するには、それぞれint4とint8を使用するだけでよいので、操作には注意が必要です。しかし、拡張機能を書くのは簡単です。
Gunther Schadow
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.