Tại sao số nguyên không dấu không có sẵn trong PostgreSQL?


113

Tôi đã xem qua bài đăng này ( Sự khác biệt giữa tinyint, smallint, mediumint, bigint và int trong MySQL là gì? ) Và nhận ra rằng PostgreSQL không hỗ trợ số nguyên không dấu.

Bất cứ ai có thể giúp giải thích tại sao nó như vậy?

Hầu hết thời gian, tôi sử dụng số nguyên không dấu làm khóa chính tự động tăng dần trong MySQL. Trong thiết kế như vậy, làm thế nào tôi có thể khắc phục điều này khi tôi chuyển cơ sở dữ liệu của mình từ MySQL sang PostgreSQL?

Cảm ơn.


Vẫn chưa nhưng sẽ sớm và chúng tôi đang xem xét chuyển sang PostgreSQL.
Adrian Hoe

4
Tôi không nghĩ đây là nơi tốt nhất để hỏi tại sao một số quyết định được đưa ra, một trong những danh sách gửi thư PostgreSQL có thể phù hợp hơn. Nếu bạn muốn các giá trị tự động tăng, hãy sử dụng serial(1 đến 2147483647) hoặc bigserial(1 đến 9223372036854775807). Một số nguyên 64 bit có chữ ký có thể cung cấp quá đủ chỗ.
mu quá ngắn

4
Cảm ơn @muistooshort. Điều đó đã giải đáp vấn đề chính. Nhưng làm thế nào về một kiểu số nguyên không dấu không được tự động tăng lên hoặc không có khóa chính? Tôi có các cột lưu trữ số nguyên không dấu có phạm vi từ 0 đến 2 ^ 32.
Adrian Hoe

4
Việc chạy nhanh qua các tài liệu PostgreSQL ( postgresql.org/docs/current/interactive/index.html ) có thể hữu ích để giúp bạn hiểu rõ hơn về khả năng của PostgreSQL. Lý do duy nhất tôi sử dụng MySQL những ngày này là nếu tôi đã đầu tư nhiều vào nó: PostgreSQL nhanh, được tải với các tính năng hữu ích và được xây dựng bởi những người khá hoang tưởng về dữ liệu của họ. IMO tất nhiên :)
mu quá ngắn

Một lần nữa, xin cảm ơn @muistooshort vì những gợi ý.
Adrian Hoe

Câu trả lời:


47

Nó đã được giải đáp tại sao postgresql thiếu các loại không dấu. Tuy nhiên, tôi khuyên bạn nên sử dụng tên miền cho các loại không dấu.

http://www.postgresql.org/docs/9.4/static/sql-createomain.html

 CREATE DOMAIN name [ AS ] data_type
    [ COLLATE collation ]
    [ DEFAULT expression ]
    [ constraint [ ... ] ]
 where constraint is:
 [ CONSTRAINT constraint_name ]
 { NOT NULL | NULL | CHECK (expression) }

Tên miền giống như một kiểu nhưng với một ràng buộc bổ sung.

Đối với một ví dụ cụ thể, bạn có thể sử dụng

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

Đây là những gì psql đưa ra khi tôi cố gắng lạm dụng kiểu.

DS1 = # select (346346 :: uint2);

LỖI: giá trị cho miền uint2 vi phạm ràng buộc kiểm tra "uint2_check"


Nhưng tôi đoán rằng việc sử dụng miền này mỗi khi chúng ta muốn một cột không dấu sẽ có chi phí trên INSERT / UPDATE. Tốt hơn nên sử dụng điều này ở những nơi thực sự cần thiết (hiếm gặp) và chỉ cần quen với ý tưởng rằng kiểu dữ liệu không đặt giới hạn thấp hơn mà chúng ta mong muốn. Xét cho cùng, nó cũng đặt ra một giới hạn trên thường là vô nghĩa theo quan điểm logic. Kiểu số không được thiết kế để thực thi các ràng buộc ứng dụng của chúng tôi.
Federico Razzoli,

Vấn đề duy nhất với phương pháp này là bạn đang "lãng phí" 15 bit lưu trữ dữ liệu không được sử dụng. Chưa kể kiểm tra cũng tốn một số hiệu quả không nhỏ. Giải pháp tốt hơn sẽ là Postgres thêm unsigned như một loại lớp đầu tiên. Trong một bảng có 20 triệu bản ghi, với và trường được lập chỉ mục như thế này, bạn đang lãng phí 40MB dung lượng cho các bit không sử dụng. Nếu bạn đang lạm dụng điều đó trên 20 bảng khác, bạn hiện đang lãng phí 800 MB dung lượng.
tpartee

85

Nó không có trong tiêu chuẩn SQL, vì vậy yêu cầu chung để triển khai nó thấp hơn.

Có quá nhiều kiểu số nguyên khác nhau làm cho hệ thống phân giải kiểu trở nên mỏng manh hơn, do đó, có một số vấn đề đối với việc thêm nhiều kiểu vào hỗn hợp.

Điều đó nói rằng, không có lý do tại sao nó không thể được thực hiện. Nó chỉ là rất nhiều công việc.


35
Câu hỏi này là đủ phổ biến mà tôi đã đặt ra để làm cho nó cố định: github.com/petere/pguint
Peter Eisentraut

Tuy nhiên, có các chuyển đổi đầu vào / đầu ra cho các ký tự số nguyên không dấu sẽ cực kỳ hữu ích. Hoặc thậm chí chỉ là một to_charmẫu.
Bergi

37

Bạn có thể sử dụng ràng buộc KIỂM TRA, ví dụ:

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

Ngoài ra, PostgreSQL có smallserial, serialbigserialcác loại xe ô increment.


2
Một điều cần đề cập, bạn không thể có bất kỳ NULL nào trong các cột sử dụng CHECK.
Minutis

1
@Minutis bạn có chắc là bạn không thể có x LÀ
KHÔNG

Và điều này không cung cấp cho bạn độ phân giải giống như nếu nó là int unsigned. Có nghĩa là int chưa ký có thể tăng lên 2^32-1, trong khi int đã ký có thể tăng lên 2^31-1.
JukesOnYou

2
NULLCHECKhoàn toàn trực giao. Bạn có thể có NULL/ NOT NULLcột có hoặc không CHECK. Chỉ cần lưu ý rằng, theo tài liệu tại postgresql.org/docs/9.4/ddl-constraints.html , việc CHECKtrả về NULL đánh giá là TRUE, vì vậy nếu bạn thực sự muốn ngăn chặn NULL, thì hãy sử dụng NOT NULLthay thế (hoặc thêm vào CHECK).
flaviovs

sử dụng CHECK không cho phép tôi lưu trữ các địa chỉ ipv4 integer(ít nhất là không có việc chúng chuyển sang trạng thái dương hoặc âm, ít nhất là ..)
hanshenrik

5

Cuộc thảo luận về DOMAINS rất thú vị nhưng không liên quan đến nguồn gốc duy nhất có thể có của câu hỏi đó. Mong muốn cho các số nguyên không dấu là tăng gấp đôi phạm vi các số nguyên có cùng số bit, đó là một đối số hiệu quả, không phải mong muốn loại trừ các số âm, mọi người đều biết cách thêm một ràng buộc kiểm tra.

Khi được ai đó hỏi về nó , Tome Lane nói:

Về cơ bản, không có khả năng điều này xảy ra trừ khi bạn có thể tìm ra cách điều chỉnh chúng vào hệ thống phân cấp quảng cáo số mà không phá vỡ nhiều ứng dụng hiện có. Chúng tôi đã xem xét điều này nhiều lần, nếu bộ nhớ phục vụ và không đưa ra được thiết kế khả thi mà dường như không vi phạm POLA.

"POLA" là gì? Google đã cho tôi 10 kết quả vô nghĩa . Không chắc đó có phải là suy nghĩ không chính xác về mặt chính trị và do đó bị kiểm duyệt hay không. Tại sao cụm từ tìm kiếm này không mang lại bất kỳ kết quả nào? Bất cứ điều gì.

Bạn có thể triển khai các int không dấu dưới dạng các loại tiện ích mở rộng mà không gặp quá nhiều khó khăn. Nếu bạn làm điều đó với C-functions, thì sẽ không bị phạt về hiệu suất. Bạn sẽ không cần phải mở rộng trình phân tích cú pháp để xử lý các ký tự vì PgSQL có một cách dễ dàng để giải thích các chuỗi dưới dạng các ký tự, chỉ cần viết '4294966272' :: uint4 làm các ký tự của bạn. Diễn viên cũng không phải là một vấn đề lớn. Bạn thậm chí không cần thực hiện các ngoại lệ phạm vi, bạn chỉ có thể xử lý ngữ nghĩa của '4294966273' :: uint4 :: int là -1024. Hoặc bạn có thể ném một lỗi.

Nếu tôi muốn điều này, tôi đã làm được. Nhưng vì tôi đang sử dụng Java ở phía bên kia của SQL, đối với tôi nó chẳng có giá trị gì vì Java cũng không có những số nguyên không dấu đó. Vì vậy, tôi không đạt được gì. Tôi đã rất khó chịu nếu tôi nhận được BigInteger từ một cột bigint, khi nó sẽ phù hợp với dài.

Một điều khác, nếu tôi có nhu cầu lưu trữ các loại 32 bit hoặc 64 bit, tôi có thể sử dụng PostgreSQL int4 hoặc int8 tương ứng, chỉ cần nhớ rằng thứ tự tự nhiên hoặc số học sẽ không hoạt động đáng tin cậy. Nhưng việc lưu trữ và truy xuất không bị ảnh hưởng bởi điều đó.


Đây là cách tôi có thể triển khai một int8 không dấu đơn giản:

Đầu tiên tôi sẽ sử dụng

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 chức năng tối thiểu uint8_inuint8_outtôi phải xác định trước.

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ần thực hiện điều này trong C uint8_funcs.c. Vì vậy, tôi sử dụng ví dụ phức tạp từ đây và làm cho nó đơn giản:

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

à tốt, hoặc bạn có thể thấy nó đã hoàn thành .


1
Tôi đoán POLA là "nguyên tắc ít gây ngạc nhiên nhất". Nó gợi ý rằng sự thay đổi có khả năng thay đổi hành vi hiện tại theo những cách không mong đợi.
Bác sĩ Eval

1

Theo tài liệu mới nhất, số nguyên đã chọn được hỗ trợ nhưng không có số nguyên không dấu trong bảng. Tuy nhiên, kiểu nối tiếp tương tự như kiểu không dấu ngoại trừ nó bắt đầu từ 1 chứ không phải từ 0. Nhưng giới hạn trên cũng giống như singed. Vì vậy, hệ thống thực sự không có hỗ trợ không dấu. Như Peter đã chỉ ra, cánh cửa mở ra để thực hiện phiên bản không dấu. Mã có thể phải được cập nhật rất nhiều, chỉ là quá nhiều công việc từ kinh nghiệm làm việc với lập trình 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 không có một kiểu dữ liệu integer unsigned đó là unbeknownst với nhiều: OID.

Các oidloại hiện đang thực hiện như một số nguyên bốn byte unsigned. […]

Bản oidthân loại này có vài thao tác ngoài sự so sánh. Tuy nhiên, nó có thể được ép kiểu thành số nguyên và sau đó được xử lý bằng cách sử dụng các toán tử số nguyên tiêu chuẩn. (Hãy cẩn thận với sự nhầm lẫn có thể có chữ ký và không dấu nếu bạn làm điều này.)

Mặc dù vậy, nó không phải là một kiểu số và cố gắng thực hiện bất kỳ phép toán số học nào (hoặc thậm chí là các phép toán bit) với nó sẽ không thành công. Ngoài ra, nó chỉ là 4 byte ( INTEGER), không có BIGINTkiểu không dấu 8 byte ( ) tương ứng .

Vì vậy, không thực sự là một ý tưởng hay nếu bạn tự mình sử dụng cái này và tôi đồng ý với tất cả các câu trả lời khác rằng trong thiết kế cơ sở dữ liệu Postgresql, bạn nên luôn sử dụng một INTEGERhoặc BIGINTcột cho khóa chính nối tiếp của mình - bắt đầu bằng dấu ( MINVALUE) hoặc cho phép nó để quấn quanh ( CYCLE) nếu bạn muốn sử dụng toàn miền.

Tuy nhiên, nó khá hữu ích cho việc chuyển đổi đầu vào / đầu ra, giống như việc bạn di chuyển từ một DBMS khác. Chèn giá trị 2147483648vào cột số nguyên sẽ dẫn đến " LỖI: số nguyên nằm ngoài phạm vi ", trong khi sử dụng biểu thức vẫn 2147483648::OIDhoạt động tốt.
Tương tự, khi chọn một cột số nguyên làm văn bản với mycolumn::TEXT, bạn sẽ nhận được các giá trị âm tại một số điểm, nhưng với mycolumn::OID::TEXTbạn sẽ luôn nhận được một số tự nhiên.

Xem ví dụ tại dbfiddle.uk .


Nếu bạn không cần các thao tác, thì giá trị duy nhất từ ​​việc sử dụng OID là thứ tự sắp xếp của bạn hoạt động. Nếu đó là những gì bạn cần, tốt. Nhưng sẽ sớm có người muốn có uint8 và sau đó họ cũng bị mất. Điểm mấu chốt là để lưu trữ các giá trị 32 bit hoặc 64 bit, bạn chỉ có thể sử dụng int4 và int8 tương ứng, chỉ cần bạn cẩn thận với các thao tác. Nhưng nó rất dễ dàng để viết một phần mở rộng.
Gunther Schadow
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.