Warum ist in PostgreSQL keine vorzeichenlose Ganzzahl verfügbar?

113

Ich bin auf diesen Beitrag gestoßen ( Was ist der Unterschied zwischen tinyint, smallint, mediumint, bigint und int in MySQL? ) Und habe festgestellt, dass PostgreSQL keine vorzeichenlose Ganzzahl unterstützt.

Kann jemand helfen zu erklären, warum es so ist?

Meistens verwende ich in MySQL eine vorzeichenlose Ganzzahl als automatisch inkrementierten Primärschlüssel. Wie kann ich dies in einem solchen Design überwinden, wenn ich meine Datenbank von MySQL nach PostgreSQL portiere?

Vielen Dank.

Adrian Hoe
quelle
Noch nicht, aber bald, und wir überlegen, auf PostgreSQL umzusteigen.
Adrian Hoe
4
Ich denke nicht, dass dies der beste Ort ist, um zu fragen, warum bestimmte Entscheidungen getroffen wurden. Eine der PostgreSQL-Mailinglisten ist möglicherweise besser geeignet. Wenn Sie Werte automatisch inkrementieren möchten, verwenden Sie serial(1 bis 2147483647) oder bigserial(1 bis 9223372036854775807). Eine vorzeichenbehaftete 64-Bit-Ganzzahl bietet wahrscheinlich mehr als genug Platz.
Mu ist zu kurz
4
Danke @muistooshort. Damit war das Hauptproblem gelöst. Aber wie wäre es mit einem vorzeichenlosen Integer-Typ, der weder automatisch inkrementiert noch ein Primärschlüssel ist? Ich habe Spalten, die vorzeichenlose Ganzzahlen speichern, die einen Bereich von 0 bis 2 ^ 32 haben.
Adrian Hoe
4
Ein kurzer Überblick über die PostgreSQL-Dokumente ( postgresql.org/docs/current/interactive/index.html ) kann hilfreich sein, um eine bessere Vorstellung davon zu erhalten, wozu PostgreSQL in der Lage ist. Der einzige Grund, warum ich MySQL heutzutage verwenden würde, ist, wenn ich bereits viel in MySQL investiert hätte: PostgreSQL ist schnell, voller nützlicher Funktionen und wurde von Leuten erstellt, die in Bezug auf ihre Daten ziemlich paranoid sind. IMO natürlich :)
mu ist zu kurz
Nochmals vielen Dank @muistooshort für die Hinweise.
Adrian Hoe

Antworten:

46

Es ist bereits beantwortet, warum in postgresql nicht signierte Typen fehlen. Ich würde jedoch vorschlagen, Domänen für nicht signierte Typen zu verwenden.

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) }

Domain ist wie ein Typ, jedoch mit einer zusätzlichen Einschränkung.

Für ein konkretes Beispiel könnten Sie verwenden

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

Hier ist, was psql gibt, wenn ich versuche, den Typ zu missbrauchen.

DS1 = # select (346346 :: uint2);

FEHLER: Wert für Domäne uint2 verstößt gegen die Prüfbedingung "uint2_check"

Karl Tarbe
quelle
Aber ich denke, dass die Verwendung dieser Domain jedes Mal, wenn wir eine nicht signierte Spalte wünschen, einen Overhead für INSERT / UPDATE bedeuten würde. Verwenden Sie dies besser dort, wo es wirklich notwendig ist (was selten vorkommt) und gewöhnen Sie sich einfach an die Idee, dass der Datentyp nicht die gewünschte Untergrenze setzt. Schließlich gibt es auch eine Obergrenze, die aus logischer Sicht normalerweise bedeutungslos ist. Numerische Typen sind nicht dazu gedacht, unsere Anwendungsbeschränkungen durchzusetzen.
Federico Razzoli
Das einzige Problem bei diesem Ansatz besteht darin, dass Sie 15 nicht verwendete Datenspeicherbits "verschwenden". Ganz zu schweigen davon, dass der Scheck auch ein wenig Effizienz kostet. Die bessere Lösung wäre, Postgres als erstklassigen Typ ohne Vorzeichen hinzuzufügen. In einer Tabelle mit 20 Millionen Datensätzen mit einem solchen indizierten Feld verschwenden Sie 40 MB Speicherplatz für nicht verwendete Bits. Wenn Sie dies in weiteren 20 Tabellen missbrauchen, verschwenden Sie jetzt 800 MB Speicherplatz.
tpartee
85

Es ist nicht im SQL-Standard enthalten, daher ist der allgemeine Drang, es zu implementieren, geringer.

Wenn zu viele verschiedene Ganzzahltypen vorhanden sind, ist das Typauflösungssystem anfälliger, sodass ein gewisser Widerstand gegen das Hinzufügen weiterer Typen zum Mix besteht.

Es gibt jedoch keinen Grund, warum dies nicht möglich ist. Es ist nur viel Arbeit.

Peter Eisentraut
quelle
35
Diese Frage ist populär genug, dass ich mich vorgenommen habe, sie zu beheben
Peter Eisentraut
Eingabe / Ausgabe-Konvertierungen für vorzeichenlose Ganzzahlliterale wären jedoch sehr nützlich. Oder auch nur ein to_charMuster.
Bergi
36

Sie können eine CHECK-Einschränkung verwenden, z.

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

Auch PostgreSQL hat smallserial, serialund bigserialTypen für Auto-Inkrement.

TriAnMan
quelle
2
Eine Sache zu erwähnen, können Sie keine NULL-Werte in Spalten haben, die CHECK verwenden.
Minutis
1
@ Minutis sind Sie sicher, dass Sie nicht x IS NULL ODER x ZWISCHEN 4 UND 40 haben können
jgmjgm
Und dies gibt Ihnen nicht die gleiche Auflösung wie wenn es unsigned int wäre. Das bedeutet, dass unsigned int bis zu gehen kann 2^32-1, während signierte Ints bis zu gehen können 2^31-1.
JukesOnYou
2
NULLund CHECKsind vollständig orthogonal. Sie können NULL/ NOT NULLSpalten mit oder ohne haben CHECK. Beachten Sie nur , dass gemäß der Dokumentation bei postgresql.org/docs/9.4/ddl-constraints.html , CHECKNULL Werte TRUE zurückkehrt, wenn Sie also wirklich will NULLs verhindern, verwenden Sie NOT NULLstattdessen (oder zusätzlich zu CHECK).
Flaviovs
Mit einem CHECK kann ich keine IPv4-Adressen speichern integer(nicht ohne dass sie zufällig positiv oder negativ sind).
Hanshenrik
5

Das Gespräch über DOMAINS ist interessant, aber nicht relevant für den einzig möglichen Ursprung dieser Frage. Der Wunsch nach Ints ohne Vorzeichen besteht darin, den Bereich der Ints mit der gleichen Anzahl von Bits zu verdoppeln. Dies ist ein Effizienzargument und nicht der Wunsch, negative Zahlen auszuschließen. Jeder weiß, wie man eine Prüfbedingung hinzufügt.

Auf die Frage von jemandem sagte Tome Lane:

Grundsätzlich besteht keine Chance, dass dies passiert, es sei denn, Sie finden eine Möglichkeit, sie in die numerische Promotion-Hierarchie einzufügen, die nicht viele vorhandene Anwendungen beschädigt. Wir haben uns das mehr als einmal angesehen, wenn der Speicher dient, und es ist uns nicht gelungen, ein funktionsfähiges Design zu entwickeln, das die POLA nicht zu verletzen schien.

Was ist die "POLA"? Google hat mir 10 Ergebnisse gegeben, die bedeutungslos sind . Ich bin mir nicht sicher, ob es politisch falsch gedacht und daher zensiert ist. Warum sollte dieser Suchbegriff kein Ergebnis liefern? Was auch immer.

Sie können vorzeichenlose Ints als Erweiterungstypen ohne allzu große Probleme implementieren. Wenn Sie dies mit C-Funktionen tun, gibt es überhaupt keine Leistungseinbußen. Sie müssen den Parser nicht erweitern, um mit Literalen umzugehen, da PgSQL eine so einfache Möglichkeit bietet, Zeichenfolgen als Literale zu interpretieren. Schreiben Sie einfach '4294966272' :: uint4 als Ihre Literale. Casts sollten auch keine große Sache sein. Sie müssen nicht einmal Bereichsausnahmen ausführen, sondern können die Semantik von '4294966273' :: uint4 :: int einfach als -1024 behandeln. Oder Sie können einen Fehler auslösen.

Wenn ich das gewollt hätte, hätte ich es getan. Da ich Java auf der anderen Seite von SQL verwende, ist es für mich von geringem Wert, da Java auch keine vorzeichenlosen Ganzzahlen hat. Also gewinne ich nichts. Ich ärgere mich schon, wenn ich eine BigInteger aus einer Bigint-Spalte bekomme, wenn sie in lange passen sollte.

Eine andere Sache, wenn ich 32-Bit- oder 64-Bit-Typen speichern musste, kann ich PostgreSQL int4 bzw. int8 verwenden, wobei ich nur daran denke, dass die natürliche Reihenfolge oder Arithmetik nicht zuverlässig funktioniert. Das Speichern und Abrufen bleibt davon jedoch unberührt.


So kann ich ein einfaches vorzeichenloses int8 implementieren:

Zuerst werde ich verwenden

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
)

die minimalen 2 Funktionen uint8_inund uint8_outich muss zuerst definieren.

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;

müssen dies in C uint8_funcs.c implementieren. Also benutze ich das komplexe Beispiel von hier und mache es einfach:

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);
}

na ja, oder du kannst es einfach schon erledigt finden .

Gunther Schadow
quelle
1
Ich vermute, POLA ist das "Prinzip des geringsten Erstaunens". Dies deutet darauf hin, dass die Änderung das Potenzial hat, vorhandenes Verhalten auf unerwartete Weise zu ändern.
Doktor Eval
1

Gemäß der neuesten Dokumentation wird die gesungene Ganzzahl unterstützt, jedoch keine vorzeichenlose Ganzzahl in der Tabelle. Der serielle Typ ähnelt jedoch dem vorzeichenlosen Typ, beginnt jedoch bei 1 und nicht bei Null. Aber die Obergrenze ist die gleiche wie gesungen. Das System hat also wirklich keine Unterstützung ohne Vorzeichen. Wie Peter betonte, steht die Tür offen, um die nicht signierte Version zu implementieren. Der Code muss möglicherweise viel aktualisiert werden, einfach zu viel Arbeit aus meiner Erfahrung mit der C-Programmierung.

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
Kemin Zhou
quelle
0

Postgres hat einen vorzeichenlosen Integer-Typ, der vielen unbekannt ist : OID.

Der oidTyp ist derzeit als vorzeichenlose 4-Byte-Ganzzahl implementiert. […]

Der oidTyp selbst hat nur wenige Operationen, die seinesgleichen suchen. Es kann jedoch in eine Ganzzahl umgewandelt und dann mit den Standard-Ganzzahloperatoren bearbeitet werden. (Achten Sie in diesem Fall auf mögliche Verwechslungen zwischen Vorzeichen und Vorzeichen.)

Es ist jedoch kein numerischer Typ , und der Versuch, damit Arithmetik (oder sogar bitweise Operationen) durchzuführen, wird fehlschlagen. Außerdem sind es nur 4 Bytes ( INTEGER), es gibt keinen entsprechenden 8-Byte- BIGINTTyp ( ) ohne Vorzeichen.

Es ist also keine gute Idee, dies selbst zu verwenden, und ich stimme allen anderen Antworten zu, dass Sie in einem Postgresql-Datenbankdesign immer eine INTEGERoder BIGINT-Spalte für Ihren seriellen Primärschlüssel verwenden sollten - damit er negativ beginnt ( MINVALUE) oder zulässt um ( CYCLE) zu wickeln, wenn Sie die gesamte Domain erschöpfen möchten.

Es ist jedoch sehr nützlich für die Eingabe / Ausgabe-Konvertierung, z. B. für die Migration von einem anderen DBMS. Das Einfügen des Werts 2147483648in eine Ganzzahlspalte führt zu einem " FEHLER: Ganzzahl außerhalb des Bereichs ", während die Verwendung des Ausdrucks einwandfrei 2147483648::OIDfunktioniert.
Wenn Sie eine Ganzzahlspalte als Text mit auswählen mycolumn::TEXT, erhalten Sie irgendwann negative Werte, aber mit erhalten mycolumn::OID::TEXTSie immer eine natürliche Zahl.

Sehen Sie ein Beispiel an dbfiddle.uk .

Bergi
quelle
Wenn Sie keine Operationen benötigen, ist der einzige Wert bei der Verwendung von OID, dass Ihre Sortierreihenfolge funktioniert. Wenn es das ist, was Sie brauchen, gut. Aber bald wird jemand eine Uint8 wollen und dann sind sie auch verloren. Die Quintessenz ist, dass Sie zum Speichern von 32-Bit- oder 64-Bit-Werten nur int4 bzw. int8 verwenden können. Sie müssen nur vorsichtig mit den Operationen sein. Es ist jedoch einfach, eine Erweiterung zu schreiben.
Gunther Schadow