PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Преглед на серийния псевдотип на данни за PostgreSQL

Въведение

PostgreSQL първоначално предоставя богато разнообразие от типове данни, поддържащи много практически случаи на употреба. Тази статия представя специалната реализация на серийни типове данни, които обикновено се използват за създаване на синтетични първични ключове.

Уникални ключове

Основна заповед на теорията за проектиране на база данни е, че всеки кортеж (т.е. ред) на релация (т.е. таблица) трябва да бъде уникално идентифициран от други кортежи. Атрибутите или колоните, които заедно ясно идентифицират един кортеж от всички останали, се наричат ​​"ключ". Някои пуристи твърдят, че всеки моделиран обект или концепция по своята същност притежава атрибут или набор от атрибути, които могат да служат като ключ и че е важно да се идентифицира този набор от ключови атрибути и да се използват за уникалната селекция от кортежи.

Но като практически въпрос, идентифицирането на достатъчно голям набор от атрибути, гарантиращи уникалност на моделирания обект, може да бъде непрактично и затова за реални реализации разработчиците често се обръщат към синтетичните ключове като сурогат. Това означава, че вместо да се разчита на някаква комбинация от действителни атрибути, стойност, вътрешна за базата данни, обикновено увеличени целочислени стойности и в противен случай няма физическо значение, се дефинира като ключ. В допълнение към простотата на ключ от една колона, фактът, че няма зависимост в реалния свят, означава, че външни фактори никога не могат да принудят нуждата от промяна на стойността, като например, може да бъде случаят, ако името на човек е използвано като ключ ... и след това лицето се ожени или влезе в програма за защита на свидетелите на федералното правителство и промени името си. Дори някои стойности, които обикновено се смятат от обикновените хора за уникални и неизменни, като например номера на социалното осигуряване в САЩ, не са нито едното, нито другото:човек може да получи нов SSN, а SSN понякога се използва повторно.

Деклариране на сериен тип данни

PostgreSQL предоставя специална декларация за тип данни, за да задоволи тази нужда от синтетични ключове. Декларирането на колона на таблица на база данни като тип SERIAL удовлетворява изискването за синтетични ключове чрез предоставяне на уникални цели числа при вмъкване на нови кортежи. Този псевдотип на данни имплементира колона с целочислен тип данни със свързана стойност по подразбиране, получена чрез извикване на функция, което предоставя увеличени целочислени стойности. Изпълнение на следния код за създаване на проста таблица с колона с идентификатор от тип сериен:

CREATE TABLE person (id serial, full_name text);
actually executes the following DDL
CREATE TABLE person (
    id integer NOT NULL,
    full_name text
);

CREATE SEQUENCE person_id_seq
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;
ALTER SEQUENCE person_id_seq OWNED BY person.id;
ALTER TABLE ONLY person
    ALTER COLUMN id
    SET DEFAULT nextval('person_id_seq'::regclass);

Тоест, ключовата дума "serial" като спецификация на типа данни предполага изпълнение на DDL изрази, създаващи колона от целочислен тип с ограничение NOT NULL, SEQUENCE и след това колоната по подразбиране е ПРОМЕНЯНА за извикване на вградена функция, осъществяваща достъп до тази SEQUENCE.

Вградената функция nextval изпълнява услуга за автоматично нарастване:всеки път, когато nextval бъде извикан, тя увеличава посочения брояч на последователността и връща новоувеличената стойност.

Можете да видите резултата от този ефект, като разгледате дефиницията на таблицата:

postgres=# \d person
                   Table "public.person"
  Column   |  Type   |         Modifiers
-----------+---------+-----------------------------------------
 Id        | integer | not null default nextval('person_id_seq'::regclass)
 full_name | text    |

Вмъкване на серийни стойности

За да използваме функцията за автоматично увеличаване, просто вмъкваме редове, разчитайки на стойността по подразбиране за серийната колона:

INSERT INTO person (full_name) VALUES ('Alice');
SELECT * FROM person;
 id | full_name
----+-----------
  1 | Alice
(1 row)

Виждаме, че стойността за колоната id, съответстваща на новия ред „Алиса“, е генерирана автоматично. Като алтернатива може да се използва ключовата дума DEFAULT, ако се желае изрично изброяване на всички имена на колони:

INSERT INTO person (id, full_name) VALUES (DEFAULT, 'Bob');
SELECT * FROM person;
 id | full_name
----+-----------
  1 | Alice
  2 | Bob
(2 rows)

където виждаме функцията за автоматично увеличаване по-очевидно, присвоявайки последователно-следващата стойност на новия ред за второто вмъкване на „Bob“.

Вмъкването на няколко реда дори работи:

INSERT INTO person (full_name) VALUES ('Cathy'), ('David');
SELECT * FROM person;
 id | full_name
----+-----------
  1 | Alice
  2 | Bob
  3 | Cathy
  4 | David
(4 rows)
Изтеглете Бялата книга днес Управление и автоматизация на PostgreSQL с ClusterControl Научете какво трябва да знаете, за да внедрите, наблюдавате, управлявате и мащабирате PostgreSQLD Изтеглете Бялата книга

Липсващи серийни стойности

Вградената функция nextval е оптимизирана за неблокиращи приложения с висок едновременност и затова не зачита връщане назад. Следователно, това означава, че може да има липсващи стойности в последователността. По-долу връщаме вмъкване, но след това виждаме, че следващо вмъкване получава нова стойност, която прескача стойността, която би била свързана с прекратената транзакция:

BEGIN TRANSACTION;
INSERT INTO person (full_name) VALUES ('Eve');
SELECT * FROM person;
 id | full_name
----+-----------
  1 | Alice
  2 | Bob
  3 | Cathy
  4 | David
  5 | Eve
(5 rows)
ROLLBACK;
INSERT INTO person (full_name) VALUES ('Fred');
SELECT * FROM person;
 id | full_name
----+-----------
  1 | Alice
  2 | Bob
  3 | Cathy
  4 | David
  6 | Fred
(5 rows)

Предимството да не се зачита връщането назад е, че други сесии, които се опитват да вмъкнат едновременно, не се блокират от други сесии за вмъкване.

Друг начин да завършите с липсващи стойности е ако редовете бъдат изтрити:

DELETE FROM person WHERE full_name = 'Cathy';
SELECT * FROM person;
 id | full_name
----+-----------
  1 | Alice
  2 | Bob
  4 | David
  6 | Fred
(4 rows)

Имайте предвид, че дори след изтриване на последния вмъкнат ред, съответстващ на стойността на колоната с най-голям идентификационен номер, броячът на последователността не се връща обратно, т.е., въпреки че след изтриване на реда, съответстващ на „Fred“, за следващи вмъквания броячът на последователността все още запазва известната по-рано най-голяма стойност и увеличения от там:

DELETE FROM person WHERE full_name = 'Fred';
INSERT INTO person (full_name) VALUES ('Gina');
SELECT * FROM person;
 id | full_name
----+-----------
  1 | Alice
  2 | Bob
  4 | David
  7 | Gina
(4 rows)

Съобщава се, че пропуските или липсващите стойности, както е показано по-горе, се разглеждат като проблем от някои разработчици на приложения, тъй като в пощенския списък на PostgreSQL General има бавно, но стабилно повтаряне на въпроса как да се избегнат пропуски в последователността при използване на сериен псевдотип на данни. Понякога няма действително основно бизнес изискване, това е просто въпрос на лично отвращение към липсващи ценности. Но има обстоятелства, при които предотвратяването на липсващи числа е реална нужда и това е тема за следваща статия.

НЕ НЕ МОЖЕШ - ДА МОЖЕШ!

Ограничението NOT NULL, импулирано от серийния псевдотип на данни, предпазва от вмъкване на NULL за колоната id, като отхвърля такива опити за вмъкване:

INSERT INTO person (id, full_name) VALUES (NULL, 'Henry');
ERROR:  null value in column "id" violates not-null constraint
DETAIL:  Failing row contains (null, Henry).

По този начин сме сигурни, че имаме стойност за този атрибут.

Въпреки това, проблем, с който се сблъскват някои хора, е, че, както е декларирано по-горе, нищо не пречи на изричното вмъкване на стойности, заобикаляйки стойността на автоматично увеличение по подразбиране, получена чрез извикване на функцията nextval:

INSERT INTO person (id, full_name) VALUES (9, 'Ingrid');
SELECT * FROM person;
 id | full_name
----+-----------
  1 | Alice
  2 | Bob
  4 | David
  7 | Gina
  9 | Ingrid
(5 rows)

Но след това две вмъквания по-късно, използвайки по подразбиране, създават дублирана стойност за колоната id, ако няма проверка на ограничения на стойностите на колоните спрямо стойността на последователността:

INSERT INTO person (full_name) VALUES ('James');
INSERT INTO person (full_name) VALUES ('Karen');
SELECT * FROM person;
 id | full_name
----+-----------
  1 | Alice
  2 | Bob
  4 | David
  7 | Gina
  9 | Ingrid
  8 | James
  9 | Karen
(7 rows)

Ако всъщност използвахме колоната за сериен идентификатор като ключ, щяхме да я декларираме като ПЪРВИЧЕН КЛЮЧ или поне да създадем УНИКАЛЕН ИНДЕКС. Ако бяхме направили това, тогава вмъкването „Карън“ по-горе щеше да се провали с грешка с дублиран ключ. Най-новата версия на PostgreSQL включва нов синтаксис за декларация на ограничения, „генериран по подразбиране като идентичност“, който избягва този капан и някои други наследени проблеми, свързани със серийния псевдотип на данни.

Функции за манипулиране на последователността

В допълнение към функцията nextval, която вече споменахме, която придвижва напред последователността и връща новата стойност, има няколко други функции за запитване и задаване на стойностите на последователността:функцията currval връща най-скоро получената стойност с nextval за определена последователност, функцията lastval връща стойност, получена последно с nextval за всяка последователност, а функцията setval задава текущата стойност на последователността. Тези функции се извикват с прости заявки, например

SELECT currval('person_id_seq');
 currval
---------
       9
(1 row)

И имайте предвид, че ако се направи извикване на функцията nextval независимо от действителното изпълнение на вмъкване, то увеличава последователността и това ще бъде отразено в следващите вмъквания:

SELECT nextval('person_id_seq');
 nextval
---------
      10
(1 row)
INSERT INTO person (full_name) VALUES ('Larry');
SELECT * FROM person;
 id | full_name
----+-----------
  1 | Alice
  2 | Bob
  4 | David
  7 | Gina
  9 | Ingrid
  8 | James
  9 | Karen
 11 | Larry
(8 rows)

Заключение

Въведохме основно разбиране на псевдотипа на PostgreSQL SERIAL за автоматично инкрементирани синтетични ключове. За илюстрация в тази статия използвахме декларацията за тип SERIAL, която създава 4-байтова целочислена колона. PostgreSQL отговаря на различни нужди от диапазон с псевдотиповете SMALLSERIAL и BIGSERIAL за съответно 2-байтови и 8-байтови размери на колони. Потърсете бъдеща статия за едно средство за справяне с нуждата от последователности без липсващи стойности.


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Няколко области на подобрения в PostgreSQL 9.4

  2. cron задача за премахване на стари данни от postgres на debian

  3. Обединяване на връзки към база данни на Celery Worker

  4. Автоматизирани надстройки на PostgreSQL клъстери в облак с почти нулев престой (част I)

  5. Не може да се извади офсетно-наивно и офсетно-известно време за дати