Neden işaretsiz tamsayı PostgreSQL'de mevcut değil?


114

Bu gönderiye rastladım ( gönderiye rastladım (MySQL'de tinyint, smallint, mediumint, bigint ve int arasındaki fark nedir? ) Ve PostgreSQL'in işaretsiz tamsayıyı desteklemediğini fark ettim.

Biri neden böyle olduğunu açıklamaya yardımcı olabilir mi?

Çoğu zaman, MySQL'de otomatik artan birincil anahtar olarak işaretsiz tamsayı kullanıyorum. Böyle bir tasarımda, veritabanımı MySQL'den PostgreSQL'e taşıdığımda bunun üstesinden nasıl gelebilirim?

Teşekkürler.


Henüz değil ama yakında ve PostgreSQL'e geçmeyi düşünüyoruz.
Adrian Hoe

4
Bunun neden belirli kararların alındığını sormak için en iyi yer olduğunu sanmıyorum, PostgreSQL e-posta listelerinden biri daha uygun olabilir. Otomatik artan değerler istiyorsanız, serial(1 ila 2147483647) veya bigserial(1 ila 9223372036854775807) kullanın. İmzalı bir 64 bit tamsayı muhtemelen fazlasıyla yeterli alan sunar.
mu çok kısa

4
Teşekkürler @muistooshort. Bu, birincil anahtar soruna cevap verdi. Ancak, otomatik olarak artırılmayan veya birincil anahtar olmayan işaretsiz bir tam sayı türüne ne dersiniz? 0 ile 2 ^ 32 aralığında işaretsiz tamsayı depolayan sütunlarım var.
Adrian Hoe

4
PostgreSQL belgelerini ( postgresql.org/docs/current/interactive/index.html ) hızlı bir şekilde incelemek, PostgreSQL'in neler yapabildiği hakkında daha iyi bir fikir edinmenize yardımcı olabilir. Bu günlerde MySQL'i kullanmamın tek nedeni, zaten çok fazla yatırım yapmış olmamdır: PostgreSQL hızlıdır, kullanışlı özelliklerle yüklüdür ve verileri konusunda oldukça paranoyak olan insanlar tarafından oluşturulmuştur. IMO elbette :)
mu çok kısa

Öneriler için tekrar teşekkürler @muistooshort.
Adrian Hoe

Yanıtlar:


48

Postgresql'de neden işaretsiz türlerin bulunmadığı yanıtlanmıştır. Ancak, imzasız türler için etki alanlarını kullanmanızı öneririm.

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

Etki alanı bir tür gibidir ancak ek bir kısıtlamaya sahiptir.

Somut bir örnek için kullanabilirsiniz

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

Tipi kötüye kullanmaya çalıştığımda psql'nin verdiği şey burada.

DS1 = # select (346346 :: uint2);

HATA: uint2 etki alanı değeri "uint2_check" denetim kısıtlamasını ihlal ediyor


Ancak, imzasız bir sütun istediğimizde bu alanı kullanmanın INSERT / UPDATE üzerinde bir ek yükü olacağını tahmin ediyorum. Bunu gerçekten gerekli olduğu yerde (ki bu nadirdir) kullanmak ve veri türünün istediğimiz alt sınırı koymadığı fikrine alışmak daha iyidir. Sonuçta, mantıksal açıdan genellikle anlamsız olan bir üst sınır da koyar. Sayısal türler, uygulama kısıtlamalarımızı zorlamak için tasarlanmamıştır.
Federico Razzoli

Bu yaklaşımla ilgili tek sorun, kullanılmayan 15 bitlik veri depolama alanını "boşa harcıyor" olmanızdır. Çekin aynı zamanda küçük bir miktar verimliliğe mal olduğundan bahsetmiyorum bile. Daha iyi bir çözüm, Postgres'in işaretsizleri birinci sınıf bir tür olarak eklemesidir. 20 milyon kaydın olduğu bir tabloda, bunun gibi indekslenmiş alan, kullanılmayan bitler için 40MB alan harcıyorsunuz. Bunu başka bir 20 masada kötüye kullanıyorsanız, artık 800 MB alan harcıyorsunuz.
tpartee

85

SQL standardında değildir, bu nedenle onu uygulama genel dürtüsü daha düşüktür.

Çok fazla farklı tam sayı türüne sahip olmak, tür çözümleme sistemini daha kırılgan hale getirir, bu nedenle karışıma daha fazla tür eklemeye karşı bir miktar direnç vardır.

Bununla birlikte, yapılamaması için hiçbir neden yok. Bu sadece çok fazla iş.



İşaretsiz tamsayı değişmezleri için girdi / çıktı dönüşümlerine sahip olmak çok faydalı olacaktır. Ya da sadece bir to_charkalıp.
Bergi

37

Bir KONTROL kısıtlaması kullanabilirsiniz, örneğin:

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

Ayrıca, PostreSQL sahiptir smallserial, serialve bigserialotomatik artış için türleri.


2
Söylenecek bir şey, CHECK kullanan sütunlarda herhangi bir NULL olamaz.
Minutis

1
@Minutis, x IS BOŞ VEYA x 4 VE 40 ARASINDA
jgmjgm

Ve bu size imzasız int olsaydı vereceği aynı çözünürlüğü vermez. İmzasız int kadar çıkabilir 2^32-1, bu arada oturum açma bilgileri de çıkabilir 2^31-1.
JukesOnYou

2
NULLve CHECKtamamen ortogonaldir. Sen olabilir NULL/ NOT NULLolan veya olmayan sütunlar CHECK. Sadece en belgelendirmesi olarak, dikkat postgresql.org/docs/9.4/ddl-constraints.html , CHECKgerçekten o, boş değerlere önlemek kullanmak isteyen eğer öyleyse, TRUE olarak NULL değerlendirir dönen NOT NULLyerine (veya ek olarak CHECK).
flaviovs

CHECK kullanmak ipv4 adreslerini içinde saklamama izin vermiyor integer(en azından rastgele pozitif ya da negatif gitmeden değil ..)
hanshenrik

5

DOMAINS hakkındaki konuşma ilginçtir, ancak bu sorunun tek olası kaynağıyla ilgili değildir. İmzalanmamış bütünler için arzu, aynı sayıda bit ile tamsayıların aralığını ikiye katlamaktır, bu bir verimlilik argümanıdır, negatif sayıları dışlama arzusu değildir, herkes bir kontrol kısıtlamasının nasıl ekleneceğini bilir.

Ne zaman bu konuda birileri tarafından sorulan , Tome Lane belirtti:

Temel olarak, pek çok mevcut uygulamayı bozmayan sayısal promosyon hiyerarşisine uydurmanın bir yolunu bulamazsanız, bunun gerçekleşmesi ihtimali sıfırdır. Bellek hizmet ederse, buna birden fazla kez baktık ve POLA'yı ihlal etmeyen uygulanabilir bir tasarım bulamadık.

"POLA" nedir? Google bana anlamsız 10 sonuç verdi . Politik olarak yanlış bir düşünce ve bu nedenle sansürlendiğinden emin değilim. Bu arama terimi neden hiçbir sonuç vermiyor? Her neyse.

İmzasız girişleri çok fazla sorun yaşamadan uzantı türleri olarak uygulayabilirsiniz. Bunu C işlevleriyle yaparsanız, o zaman hiçbir performans cezası olmayacaktır. Değişmez değerlerle uğraşmak için ayrıştırıcıyı genişletmenize gerek kalmayacak çünkü PgSQL dizeleri değişmez değerler olarak yorumlamak için çok kolay bir yola sahiptir, sadece '4294966272' :: uint4'ü değişmez değerler olarak yazın. Oyuncular da çok önemli olmamalı. Aralık istisnaları yapmanıza bile gerek yok, sadece '4294966273' :: uint4 :: int'in anlamını -1024 olarak ele alabilirsiniz. Veya bir hata atabilirsiniz.

Bunu isteseydim, yapardım. Ama Java'yı SQL'in diğer tarafında kullandığım için, Java'nın da bu işaretsiz tamsayılara sahip olmadığı için benim için çok az değeri var. Yani hiçbir şey kazanmıyorum. Uzun sığması gereken bir bigint sütunundan bir BigInteger alırsam zaten sinirleniyorum.

Başka bir şey, 32 bit veya 64 bit türleri saklama ihtiyacım olsaydı, sırayla PostgreSQL int4 veya int8 kullanabilirim, sadece doğal sıranın veya aritmetiğin güvenilir bir şekilde çalışmayacağını hatırlıyorum. Ancak depolama ve geri alma bundan etkilenmez.


İşte basit bir işaretsiz int8'i nasıl uygulayabilirim:

İlk kullanacağım

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
)

en az 2 işlevi uint8_inve uint8_outönce tanımlamalıyım.

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;

bunu C uint8_funcs.c'de uygulamanız gerekir. Bu yüzden buradan karmaşık örneği kullanıyorum ve basitleştiriyorum:

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 peki, yoksa zaten bitmiş olduğunu görebilirsin .


1
POLA'nın "en az şaşkınlık ilkesi" olduğunu tahmin ediyorum. Değişikliğin mevcut davranışı beklenmedik şekillerde değiştirme potansiyeline sahip olduğunu öne sürüyor.
Doctor Değerlendir

1

En son belgelere göre, şarkı söylenen tamsayı desteklenir ancak tabloda işaretsiz tam sayı yoktur. Bununla birlikte, seri tipi, sıfırdan değil 1'den başlaması dışında, işaretsiz tipine benzer. Ancak üst sınır şarkı söylenenle aynıdır. Yani sistem gerçekten imzasız desteğe sahip değil. Peter'ın işaret ettiği gibi, imzasız versiyonu uygulamak için kapı açıktır. Kodun çok fazla güncellenmesi gerekebilir, C programlamayla çalışma deneyimime göre çok fazla çalışma.

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 birçok bilmediği bir işaretsiz tamsayı türü var: OID.

oidTipi, şu anda işaretsiz dört bayt tamsayı olarak uygulanır. [...]

oidTip kendisi karşılaştırma ötesinde birkaç faaliyet göstermektedir. Ancak tamsayıya çevrilebilir ve ardından standart tamsayı operatörleri kullanılarak değiştirilebilir. (Bunu yaparsanız, olası imzalı ve imzasız karışıklığa dikkat edin.)

Yine de sayısal bir tür değildir ve onunla herhangi bir aritmetik (veya hatta bitsel işlemler) yapmaya çalışmak başarısız olacaktır. Ayrıca, sadece 4 bayttır ( INTEGER), karşılık gelen 8 bayt ( BIGINT) işaretsiz tür yoktur.

Bu yüzden bunu kendiniz kullanmak gerçekten iyi bir fikir değil ve bir Postgresql veritabanı tasarımında seri birincil anahtarınız için her zaman bir INTEGERveya BIGINTsütun kullanmanız gerektiğine dair diğer tüm yanıtlara katılıyorum - negatif ( ) ile başlamasını sağlamak veya buna izin vermek tam etki alanını tüketmek istiyorsanız etrafını sarın ( ).MINVALUECYCLE

Ancak, başka bir DBMS'den geçişiniz gibi girdi / çıktı dönüşümü için oldukça kullanışlıdır. Değerin 2147483648bir tamsayı sütununa girilmesi " HATA: tamsayı aralık dışı " olmasına neden olurken, ifadeyi kullanmak 2147483648::OIDgayet iyi çalışır.
Benzer şekilde, metin olarak bir tamsayı sütunu seçerken, bir mycolumn::TEXTnoktada negatif değerler alırsınız, ancak mycolumn::OID::TEXTher zaman doğal bir sayı alırsınız.

Dbfiddle.uk adresindeki bir örneğe bakın .


İşlemlere ihtiyacınız yoksa, OID kullanmanın tek değeri sıralama düzeninizin çalışmasıdır. Eğer ihtiyacın olan buysa, tamam. Ama yakında birisi bir uint8 isteyecek ve sonra onlar da kaybolacak. Sonuç olarak 32 bit veya 64 bit değerleri saklamak için sırasıyla int4 ve int8 kullanabilirsiniz, sadece işlemlere dikkat etmeniz gerekir. Ancak bir uzantı yazmak kolaydır.
Gunther Schadow
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.