Чому непідписане ціле число недоступне в PostgreSQL?


113

Я натрапив на цю публікацію (в чому різниця між tinyint, smallint, mediumint, bigint та int в MySQL? ) І зрозумів, що PostgreSQL не підтримує непідписане ціле число.

Хтось може допомогти пояснити, чому так?

Більшу частину часу я використовую непідписане ціле число в якості автоматичного збільшення первинного ключа в MySQL. Як в такому дизайні я можу це подолати, коли переношу свою базу даних з MySQL на PostgreSQL?

Дякую.


Поки що, але незабаром, і ми розглядаємо можливість перейти до PostgreSQL.
Адріан Хой

4
Я не думаю, що це найкраще місце, щоб запитати, чому приймаються певні рішення, один із списків розсилки PostgreSQL може бути більш підходящим. Якщо потрібно автоматично збільшувати значення, тоді використовуйте serial(1 до 2147483647) або bigserial(1 до 9223372036854775807). Підписане 64-бітове ціле число, ймовірно, пропонує більше, ніж достатньо місця.
mu занадто короткий

4
Дякую @muistooshort. Це відповіло на питання первинного ключа. Але як щодо непідписаного цілого типу, який не збільшується автоматично, ані первинного ключа? У мене є стовпці, в яких зберігається непідписане ціле число, яке має діапазон від 0 до 2 ^ 32.
Адріан Хой

4
Швидкий пробіг через документи PostgreSQL ( postgresql.org/docs/current/interactive/index.html ) може бути корисним, щоб допомогти вам краще зрозуміти, на що здатний PostgreSQL. Єдиною причиною, якою я сьогодні користувався MySQL, є те, що я вже багато вклав у це: PostgreSQL швидкий, завантажений корисними функціями та побудований людьми, які досить параноїчні щодо своїх даних. IMO звичайно :)
mu занадто короткий

Ще раз дякую @muistooshort за покажчики.
Адріан Хой

Відповіді:


47

Уже відповіли, чому в postgresql не вистачає неподписаних типів. Однак я б запропонував використовувати домени для неподписаних типів.

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

Домен - це тип, але з додатковим обмеженням.

Для конкретного прикладу ви можете використати

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

Ось що дає psql, коли я намагаюся зловживати типом.

DS1 = # select (346346 :: uint2);

ПОМИЛКА: значення для домену uint2 порушує обмеження перевірки "uint2_check"


Але я здогадуюсь, що використання цього домену кожного разу, коли ми хочемо, щоб стовпець без підпису мав накладні витрати на INSERT / UPDATE. Краще використовувати це там, де це дійсно необхідно (що рідко), і просто звикнути до думки, що тип даних не ставить нижньої межі, яку ми бажаємо. Зрештою, це також ставить верхню межу, яка, як правило, безглуздо з логічного погляду. Числові типи не призначені для забезпечення обмежень наших програм.
Федеріко Разцолі

Єдина проблема такого підходу полягає в тому, що ви "витрачаєте" 15 біт зберігання даних, які не використовуються. Не кажучи вже про перевірку, це також коштує невеликої ефективності. Кращим рішенням буде Postgres, що додає неподписаний як тип першого класу. У таблиці з 20 мільйонами записів із таким і індексованим полем ви витрачаєте 40 МБ місця на невикористані біти. Якщо ви зловживаєте цим протягом ще 20 таблиць, ви витрачаєте 800 МБ місця.
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
Варто зазначити одне, що ви не можете мати NULL в стовпцях, які використовують CHECK.
Minutis

1
@Minutis Ви впевнені, що у вас не може бути х НІЛЬНО АБО МІЖ 4 І 40
jgmjgm

І це не дає вам таку ж роздільну здатність, як це було б, якби він був непідписаний int. Значення неподписаного int може піднятися 2^32-1, тим часом підписані вставки можуть піднятися 2^31-1.
JukesOnYou

2
NULLі CHECKє повністю ортогональними. Ви можете мати NULL/ NOT NULLстовпці з або без CHECK. Просто зауважте, що згідно з документацією на postgresql.org/docs/9.4/ddl-constraints.html , CHECKповернення NULL оцінюється як ІСТИНА, тож якщо ви дійсно хочете запобігти NULL, тоді використовуйте NOT NULLзамість цього (або на додаток до нього CHECK).
флавійови

використання CHECK не дозволяє мені зберігати ipv4 адреси в integer (не без того, щоб вони йшли випадково позитивними чи негативними, принаймні ..)
hanshenrik

5

Розмова про DOMAINS цікава, але не стосується єдиного можливого походження цього питання. Прагнення до безпідписаних ints полягає в подвоєнні діапазону ints з однаковою кількістю біт, це аргумент ефективності, а не бажання виключати негативні числа, всі знають, як додати обмеження для перевірки.

На запитання когось про це , Том Лейн заявив:

В основному, немає жодного шансу, що це станеться, якщо ви не зможете знайти спосіб їх вписання в числову ієрархію просування, яка не порушує багато існуючих додатків. Ми розглянули це не раз, якщо пам'ять слугує, і не вдалося придумати працездатний дизайн, який, схоже, не порушує POLA.

Що таке "ПОЛА"? Google дав мені 10 безглуздих результатів . Не впевнений, чи це політично некоректна думка, і тому це цензура. Чому цей пошуковий термін не дасть жодного результату? Що б там не було.

Ви можете реалізувати неподписані вставки як типи розширень без особливих проблем. Якщо ви робите це з C-функціями, то покарання за продуктивність взагалі не буде. Вам не потрібно буде розширювати аналізатор для роботи з літералами, оскільки PgSQL має такий простий спосіб інтерпретувати рядки як літерали, просто напишіть '4294966272' :: uint4 як ваші літерали. Касти також не повинні бути великою справою. Вам навіть не потрібно робити винятки з діапазону, ви можете просто трактувати семантику '4294966273' :: uint4 :: int як -1024. Або ви можете викинути помилку.

Якби я цього хотів, я би це зробив. Але оскільки я використовую Java з іншого боку SQL, для мене це малоцінно, оскільки Java також не має цілих непідписаних цілих чисел. Тож я нічого не отримую. Мене вже дратує, якщо я отримаю BigInteger з стовпця bigint, коли він повинен вписатися в long.

Інша річ, якщо у мене була необхідність зберігати 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_inі uint8_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
Я здогадуюсь, що ПОЛА - це "принцип найменшого здивування". Це дозволяє припустити, що зміна може потенційно змінити існуючу поведінку несподіваними способами.
Лікар Еваль

1

Відповідно до останньої документації, підписане ціле число підтримується, але в таблиці немає жодного неподписаного цілого числа. Однак серійний тип схожий на безпідписаний, за винятком того, що він починається від 1, а не від нуля. Але верхня межа така ж, як і співана. Таким чином, система справді не має підписаної підтримки. Як вказував Петро, ​​двері відкриті для реалізації непідписаної версії. Код, можливо, доведеться багато оновлювати, просто надто багато роботи з мого досвіду роботи з програмуванням на С.

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.

The oid даний момент тип реалізований як безпідписане чотирибайтове ціле число. […]

Сам oidтип має кілька операцій поза порівнянням. Однак це може бути передано цілому числу, а потім маніпулювати за допомогою стандартних цілих операторів. (Остерігайтеся можливої ​​плутанини між підписаними та неподписаними, якщо ви це зробите.)

Це не числовий тип, але спроба зробити будь-яку арифметику (або навіть побітові операції) з нею не вдасться. Крім того, це всього 4 байти ( INTEGER), немає відповідного 8-байтового ( BIGINT) неподписаного типу.

Тому не дуже добре використовувати це самостійно, і я погоджуюся з усіма іншими відповідями, що в дизайні бази даних Postgresql ви завжди повинні використовувати INTEGERабо BIGINTстовпець для свого серійного первинного ключа - запускаючи його в мінусі ( MINVALUE) або дозволяючи це обернути (CYCLE ), якщо ви хочете вичерпати повний домен.

Однак це досить корисно для перетворення вводу / виводу, як ваша міграція з іншої СУБД. Вставлення значення 2147483648в цілий стовпець призведе до " ПОМИЛКИ: ціле число поза діапазоном ", а використання виразу 2147483648::OIDпрацює добре.
Аналогічно, вибираючи цілий стовпець як текст з mycolumn::TEXT, ви отримаєте від'ємні значення в якийсь момент, але з mycolumn::OID::TEXTвами завжди вийде натуральне число.

Дивіться приклад на dbfiddle.uk .


Якщо вам не потрібні операції, то єдине значення використання OID - це те, що ваш порядок сортування працює. Якщо це те, що вам потрібно, добре. Але незабаром хтось захоче uint8, і тоді вони теж втрачаються. Суть полягає в тому, що для зберігання 32-бітових або 64-бітних значень ви можете просто використовувати int4 та int8 відповідно, потрібно бути обережними з операціями. Але просто написати розширення.
Гюнтер Шадов
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.