Mengapa unsigned integer tidak tersedia di PostgreSQL?

113

Saya menemukan posting ini ( Apa perbedaan antara tinyint, smallint, mediumint, bigint dan int di MySQL? ) Dan menyadari bahwa PostgreSQL tidak mendukung unsigned integer.

Adakah yang bisa membantu menjelaskan mengapa demikian?

Sebagian besar waktu, saya menggunakan unsigned integer sebagai kunci primer bertambah otomatis di MySQL. Dalam desain seperti itu, bagaimana saya bisa mengatasinya ketika saya mem-port database saya dari MySQL ke PostgreSQL?

Terima kasih.

Adrian Hoe
sumber
Belum, tapi segera dan kami sedang mempertimbangkan untuk pindah ke PostgreSQL.
Adrian Hoe
4
Saya rasa ini bukan tempat terbaik untuk bertanya mengapa keputusan tertentu dibuat, salah satu milis PostgreSQL mungkin lebih cocok. Jika Anda menginginkan nilai penambahan otomatis, gunakan serial(1 hingga 2147483647) atau bigserial(1 hingga 9223372036854775807). Integer 64bit yang ditandatangani mungkin menawarkan lebih dari cukup ruang.
mu terlalu pendek
4
Terima kasih @muistooshort. Itu menjawab masalah kunci utama. Tapi bagaimana dengan tipe integer unsigned yang bukan auto incremented atau primary key? Saya memiliki kolom yang menyimpan integer unsigned yang memiliki rentang dari 0 hingga 2 ^ 32.
Adrian Hoe
4
Menjalankan cepat melalui dokumen PostgreSQL ( postgresql.org/docs/current/interactive/index.html ) mungkin berguna untuk membantu Anda mendapatkan gambaran yang lebih baik tentang kemampuan PostgreSQL. Satu-satunya alasan saya menggunakan MySQL akhir-akhir ini adalah jika saya sudah berinvestasi di dalamnya: PostgreSQL cepat, sarat dengan fitur-fitur berguna, dan dibuat oleh orang-orang yang cukup paranoid tentang datanya. IMO tentu saja :)
mu terlalu pendek
Sekali lagi terima kasih @muistooshort untuk petunjuknya.
Adrian Hoe

Jawaban:

47

Sudah terjawab mengapa postgresql tidak memiliki tipe unsigned. Namun saya akan menyarankan untuk menggunakan domain untuk tipe unsigned.

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 seperti sebuah tipe tetapi dengan batasan tambahan.

Untuk contoh konkret Anda bisa menggunakan

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

Inilah yang psql berikan ketika saya mencoba menyalahgunakan tipe.

DS1 = # pilih (346346 :: uint2);

EROR: nilai untuk domain uint2 melanggar batasan pemeriksaan "uint2_check"

Karl Tarbe
sumber
Tapi saya rasa bahwa menggunakan domain ini setiap kali kita menginginkan kolom unsigned akan memiliki overhead pada INSERT / UPDATE. Lebih baik menggunakan ini di tempat yang benar-benar diperlukan (yang jarang terjadi) dan biasakanlah dengan gagasan bahwa tipe data tidak menempatkan batas bawah yang kita inginkan. Bagaimanapun, ini juga menempatkan batas atas yang biasanya tidak berarti dari sudut pandang logis. Jenis numerik tidak dirancang untuk memaksakan batasan aplikasi kami.
Federico Razzoli
Satu-satunya masalah dengan pendekatan ini adalah Anda "membuang-buang" 15 bit penyimpanan data yang tidak digunakan. Belum lagi, cek juga membutuhkan sedikit efisiensi. Solusi yang lebih baik adalah Postgres menambahkan unsigned sebagai tipe kelas pertama. Dalam tabel dengan 20 juta catatan, dengan dan bidang terindeks seperti ini, Anda membuang-buang 40MB ruang pada bit yang tidak digunakan. Jika Anda menyalahgunakannya di 20 tabel lainnya, Anda sekarang membuang-buang ruang 800MB.
tpartee
85

Ini tidak ada dalam standar SQL, jadi dorongan umum untuk mengimplementasikannya lebih rendah.

Memiliki terlalu banyak tipe bilangan bulat yang berbeda membuat sistem resolusi tipe lebih rapuh, jadi ada beberapa hambatan untuk menambahkan lebih banyak tipe ke dalam campuran.

Meski begitu, tidak ada alasan mengapa itu tidak bisa dilakukan. Itu hanya banyak pekerjaan.

Peter Eisentraut
sumber
35
Pertanyaan ini cukup populer sehingga saya telah menetapkan untuk memperbaikinya: github.com/petere/pguint
Peter Eisentraut
Memiliki konversi input / output untuk literal integer unsigned akan sangat berguna. Atau bahkan hanya sebuah to_charpola.
Bergi
37

Anda dapat menggunakan batasan PERIKSA, misalnya:

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

Juga, PostgreSQL memiliki smallserial, serial, dan bigserialjenis untuk auto-increment.

TriAnMan
sumber
2
Satu hal lagi, Anda tidak dapat memiliki NULL di kolom yang menggunakan CHECK.
Minutis
1
@Minutis apakah Anda yakin tidak dapat memiliki x IS NULL ATAU x ANTARA 4 DAN 40
jgmjgm
Dan ini tidak memberi Anda resolusi yang sama seperti jika unsigned int. Artinya unsigned int bisa naik 2^32-1, sementara int yang ditandatangani bisa naik 2^31-1.
JukesOnYou
2
NULLdan CHECKsepenuhnya ortogonal. Anda dapat memiliki NULL/ NOT NULLkolom dengan atau tanpa CHECK. Perhatikan saja, sesuai dokumentasi di postgresql.org/docs/9.4/ddl-constraints.html , CHECKmengembalikan nilai NULL ke TRUE, jadi jika Anda benar-benar ingin mencegah NULL, gunakan NOT NULLsebagai gantinya (atau sebagai tambahan CHECK).
flaviov
menggunakan PERIKSA tidak memungkinkan saya untuk menyimpan alamat ipv4 di integer(tidak tanpa membuatnya menjadi positif atau negatif secara acak, setidaknya ..)
hanshenrik
5

Pembicaraan tentang DOMAIN menarik tetapi tidak relevan dengan satu-satunya kemungkinan asal pertanyaan itu. Keinginan untuk unsigned int adalah untuk menggandakan rentang int dengan jumlah bit yang sama, ini adalah argumen efisiensi, bukan keinginan untuk mengecualikan angka negatif, semua orang tahu bagaimana menambahkan batasan cek.

Ketika ditanya oleh seseorang tentang hal itu , Tome Lane menyatakan:

Pada dasarnya, tidak ada kemungkinan hal ini akan terjadi kecuali Anda dapat menemukan cara untuk memasangnya ke dalam hierarki promosi numerik yang tidak merusak banyak aplikasi yang ada. Kami telah melihat ini lebih dari sekali, jika memori berfungsi, dan gagal menghasilkan desain yang bisa diterapkan yang tampaknya tidak melanggar POLA.

Apa itu "POLA"? Google memberi saya 10 hasil yang tidak berarti . Tidak yakin apakah itu pemikiran yang salah secara politis dan karena itu disensor. Mengapa istilah pencarian ini tidak membuahkan hasil? Masa bodo.

Anda dapat mengimplementasikan unsigned int sebagai jenis ekstensi tanpa terlalu banyak kesulitan. Jika Anda melakukannya dengan fungsi-C, maka tidak akan ada penalti kinerja sama sekali. Anda tidak perlu memperluas parser untuk menangani literal karena PgSQL memiliki cara yang mudah untuk menafsirkan string sebagai literal, cukup tulis '4294966272' :: uint4 sebagai literal Anda. Pemain juga seharusnya tidak menjadi masalah besar. Anda bahkan tidak perlu melakukan pengecualian rentang, Anda cukup memperlakukan semantik '4294966273' :: uint4 :: int sebagai -1024. Atau Anda bisa membuat kesalahan.

Jika saya menginginkan ini, saya akan melakukannya. Tetapi karena saya menggunakan Java di sisi lain SQL, bagi saya nilainya kecil karena Java juga tidak memiliki bilangan bulat yang tidak ditandatangani. Jadi saya tidak mendapatkan apa-apa. Saya sudah kesal jika saya mendapatkan BigInteger dari kolom bigint, padahal harus muat menjadi panjang.

Hal lain, jika saya memiliki kebutuhan untuk menyimpan tipe 32 bit atau 64 bit, saya dapat menggunakan PostgreSQL int4 atau int8 masing-masing, hanya dengan mengingat bahwa urutan natural atau aritmatika tidak akan bekerja dengan andal. Tapi menyimpan dan mengambil tidak terpengaruh olehnya.


Berikut adalah bagaimana saya dapat mengimplementasikan unsigned int8 sederhana:

Pertama saya akan gunakan

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
)

minimal 2 fungsi uint8_indan uint8_outsaya harus mendefinisikan terlebih dahulu.

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;

perlu menerapkan ini di C uint8_funcs.c. Jadi saya menggunakan contoh kompleks dari sini dan membuatnya sederhana:

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

ah baiklah, atau Anda bisa menemukannya sudah selesai .

Gunther Schadow
sumber
1
Saya menduga POLA adalah "prinsip yang paling tidak mengherankan". Ini menunjukkan bahwa perubahan berpotensi mengubah perilaku yang ada dengan cara yang tidak terduga.
Dokter Eval
1

Menurut dokumentasi terbaru, integer yang dihanguskan didukung tetapi tidak ada integer yang tidak bertanda tangan dalam tabel. Namun, tipe serialnya mirip dengan unsigned kecuali dimulai dari 1 bukan dari nol. Tapi batas atasnya sama dengan hangus. Jadi sistem benar-benar tidak memiliki dukungan tanpa tanda tangan. Seperti yang ditunjukkan oleh Peter, pintu terbuka untuk mengimplementasikan versi unsigned. Kode mungkin harus banyak diperbarui, terlalu banyak pekerjaan dari pengalaman saya bekerja dengan pemrograman 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
Kemin Zhou
sumber
0

Postgres memang memiliki tipe unsigned integer yang tanpa sepengetahuan banyak: OID.

The oidtipe dilaksanakan saat ini sebagai unsigned empat byte integer. […]

The oidjenis itu sendiri memiliki beberapa operasi di luar perbandingan. Ini dapat dilemparkan ke integer, dan kemudian dimanipulasi menggunakan operator integer standar. (Waspadalah terhadap kemungkinan kebingungan antara tanda-versus-tidak bertanda tangan jika Anda melakukan ini.)

Ini bukan tipe numerik , dan mencoba melakukan aritmatika (atau bahkan operasi bitwise) dengannya akan gagal. Juga, ini hanya 4 byte ( INTEGER), tidak ada BIGINTtipe unsigned 8 byte ( ) yang sesuai.

Jadi bukan ide yang baik untuk menggunakan ini sendiri, dan saya setuju dengan semua jawaban lain bahwa dalam desain database Postgresql Anda harus selalu menggunakan kolom INTEGERatau BIGINTuntuk kunci utama serial Anda - memulainya dengan negatif ( MINVALUE) atau mengizinkannya untuk membungkus ( CYCLE) jika Anda ingin menghabiskan domain penuh.

Namun, ini cukup berguna untuk konversi input / output, seperti migrasi Anda dari DBMS lain. Memasukkan nilai 2147483648ke dalam kolom integer akan menghasilkan " ERROR: integer out of range ", sementara ekspresi 2147483648::OIDbekerja dengan baik.
Demikian pula, saat memilih kolom integer sebagai teks mycolumn::TEXT, Anda akan mendapatkan nilai negatif di beberapa titik, tetapi dengan mycolumn::OID::TEXTAnda akan selalu mendapatkan bilangan asli.

Lihat contoh di dbfiddle.uk .

Bergi
sumber
Jika Anda tidak memerlukan operasi, maka satu-satunya nilai dari penggunaan OID adalah urutan sortir Anda berfungsi. Jika itu yang Anda butuhkan, baiklah. Tetapi segera seseorang akan menginginkan uint8 dan kemudian mereka juga tersesat. Intinya adalah untuk menyimpan nilai 32 bit atau 64 bit Anda hanya dapat menggunakan int4 dan int8, hanya perlu berhati-hati dengan pengoperasiannya. Tetapi mudah untuk menulis ekstensi.
Gunther Schadow