sql >> Databáze >  >> RDS >> PostgreSQL

Přehled sériového pseudo-datového typu pro PostgreSQL

Úvod

PostgreSQL nativně poskytuje bohatou rozmanitost datových typů podporujících mnoho praktických případů použití. Tento článek představuje speciální implementaci sériových datových typů obvykle používaných pro vytváření syntetických primárních klíčů.

Jedinečné klíče

Základním principem teorie návrhu databází je, že každá n-tice (tj. řádek) relace (tj. tabulky) musí být jednoznačně identifikována od ostatních n-tic. Atributy nebo sloupce, které dohromady zřetelně identifikují jednu n-tici od všech ostatních, se nazývají „klíč“. Někteří puristé tvrdí, že jakýkoli modelovaný objekt nebo koncept má ze své podstaty atribut nebo sadu atributů, které mohou sloužit jako klíč, a že je důležité identifikovat tuto sadu klíčových atributů a využít je pro jedinečný výběr n-tic.

Ale z praktického hlediska může být identifikace dostatečně velké sady atributů zajišťujících jedinečnost modelovaného objektu nepraktická, a tak se vývojáři u implementací v reálném světě často obracejí k syntetickým klíčům jako k náhradě. To znamená, že namísto spoléhání se na nějakou kombinaci skutečných atributů je jako klíč definována vnitřní hodnota databáze, typicky inkrementované celočíselné hodnoty, které jinak nemají žádný fyzický význam. Kromě jednoduchosti jediného sloupcového klíče skutečnost, že neexistuje žádná závislost v reálném světě, znamená, že vnější faktory si nikdy nemohou vynutit potřebu změnit hodnotu, jako například v případě, kdy je použito jméno osoby jako klíč... a pak se ta osoba oženila nebo vstoupila do federálního vládního programu na ochranu svědků a změnila si jméno. Dokonce i některé hodnoty, které laici běžně považují za jedinečné a neměnné, jako například číslo sociálního pojištění v USA, nejsou ani jedno:osoba může získat nové SSN a SSN jsou někdy znovu použita.

Deklarování typu sériových dat

PostgreSQL poskytuje speciální deklaraci datového typu, která uspokojí tuto potřebu syntetických klíčů. Deklarování sloupce databázové tabulky jako typu SERIAL splňuje požadavek na syntetické klíče tím, že při vkládání nových n-tic dodává jedinečná celá čísla. Tento pseudodatový typ implementuje sloupec celočíselného datového typu s přidruženou výchozí hodnotou odvozenou prostřednictvím volání funkce, které poskytuje inkrementované celočíselné hodnoty. Provedení následujícího kódu k vytvoření jednoduché tabulky se sloupcem id typu 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);

To znamená, že klíčové slovo "serial" jako specifikace datového typu implikuje provádění příkazů DDL vytvoření sloupce typu celé číslo s omezením NOT NULL, SEQUENCE, a poté je výchozí sloupec ALTERED pro volání vestavěné funkce přistupující k této SEKVENCI.

Vestavěná funkce nextval provádí službu autoinkrementace:pokaždé, když je zavolána nextval, zvýší zadaný čítač sekvence a vrátí nově inkrementovanou hodnotu.

Výsledek tohoto efektu můžete vidět prozkoumáním definice tabulky:

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

Vkládání sériových hodnot

Abychom mohli využít funkci automatického přírůstku, jednoduše vložíme řádky, přičemž se spoléháme na výchozí hodnotu pro sériový sloupec:

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

Vidíme, že hodnota pro sloupec id odpovídající novému řádku „Alice“ byla automaticky vygenerována. Alternativně lze použít klíčové slovo DEFAULT, pokud je požadováno explicitní uvedení všech názvů sloupců:

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

kde vidíme funkci automatického přírůstku zjevněji, přiřazování sériově-next hodnoty k novému řádku pro druhé vložení „Bob“.

Vložení více řádků dokonce funguje:

INSERT INTO person (full_name) VALUES ('Cathy'), ('David');
SELECT * FROM person;
 id | full_name
----+-----------
  1 | Alice
  2 | Bob
  3 | Cathy
  4 | David
(4 rows)
Stáhněte si Whitepaper Today Správa a automatizace PostgreSQL s ClusterControlZjistěte, co potřebujete vědět k nasazení, monitorování, správě a škálování PostgreSQLStáhněte si Whitepaper

Chybějící sériové hodnoty

Vestavěná funkce nextval je optimalizována pro neblokující aplikace s vysokým počtem souběžných aplikací, a proto nerespektuje vrácení zpět. V důsledku to znamená, že v sekvenci mohou chybět hodnoty. Níže vrátíme vložení zpět, ale pak uvidíme, že následující vložení získá novou hodnotu, která přeskočí hodnotu, která by byla spojena s přerušenou transakcí:

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)

Výhodou nerespektování vrácení zpět je, že další relace pokoušející se o souběžné vkládání nejsou blokovány jinými relacemi vkládání.

Dalším způsobem, jak skončit s chybějícími hodnotami, je odstranění řádků:

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

Všimněte si, že i po smazání naposledy vloženého řádku odpovídajícího největší hodnotě sloupce id automatického přírůstku se počítadlo sekvencí nevrátí, tj. i když po smazání řádku odpovídajícího 'Fred', pro následující vložení čítač sekvencí stále zachová dříve známá největší hodnota a přírůstky odtud:

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)

Mezery nebo chybějící hodnoty, jak je uvedeno výše, jsou údajně považovány za problém některými vývojáři aplikací, protože v poštovním seznamu PostgreSQL General se pomalu, ale vytrvale opakuje otázka, jak se vyhnout mezerám v sekvenci při použití sériového pseudodatatypu. Někdy neexistuje žádný skutečný základní obchodní požadavek, je to jen otázka osobní averze k chybějícím hodnotám. Existují však okolnosti, kdy je skutečně potřeba zabránit chybějícím číslům, a to je téma pro následující článek.

NE, NEMŮŽETE – ANO, MŮŽETE!

Omezení NOT NULL imputované sériovým pseudodatovým typem chrání před vložením NULL do sloupce id tím, že takové pokusy o vložení odmítá:

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

Máme tedy jistotu, že tento atribut má hodnotu.

Problém, s nímž se někteří lidé setkávají, je ten, že, jak je deklarováno výše, nic nebrání explicitnímu vložení hodnot, čímž se obchází výchozí hodnota autoinkrementu odvozená pomocí vyvolání funkce 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)

Ale potom dvě vložení později pomocí výchozího nastavení vytvoří duplicitní hodnotu pro sloupec id, pokud neexistuje žádná kontrola omezení hodnot sloupců vůči hodnotě sekvence:

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)

Pokud bychom ve skutečnosti používali sloupec sériového id jako klíč, deklarovali bychom jej jako PRIMÁRNÍ KLÍČ nebo alespoň vytvořili UNIKÁTNÍ INDEX. Kdybychom to udělali, výše uvedená vložka „Karen“ by selhala s duplicitní chybou klíče. Nejnovější verze PostgreSQL obsahuje novou syntaxi deklarace omezení 'generována ve výchozím nastavení jako identita', která se tomuto úskalí a některým dalším starším problémům souvisejícím se sériovým pseudodatatypem vyhýbá.

Funkce pro manipulaci se sekvencemi

Kromě již zmíněné funkce nextval, která posouvá sekvenci dopředu a vrací novou hodnotu, existuje několik dalších funkcí pro dotazování a nastavení hodnot sekvence:funkce currval vrací hodnotu naposledy získanou pomocí nextval pro zadanou sekvenci, funkce lastval vrátí hodnotu naposledy získanou pomocí nextval pro jakoukoli sekvenci a funkce setval nastaví aktuální hodnotu sekvence. Tyto funkce jsou volány pomocí jednoduchých dotazů, například

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

A povšimněte si, že pokud je provedeno volání funkce nextval nezávisle na skutečném provedení vložení, zvýší se sekvence a to se projeví v následujících vloženích:

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)

Závěr

Představili jsme základní pochopení pseudodatového typu PostgreSQL SERIAL pro automaticky inkrementované syntetické klíče. Pro ilustraci v tomto článku jsme použili deklaraci typu SERIAL, která vytvoří 4bajtový celočíselný sloupec. PostgreSQL vyhovuje různým potřebám rozsahu pomocí pseudodatových typů SMALLSERIAL a BIGSERIAL pro velikosti sloupců 2 bajty a 8 bajtů. Podívejte se na budoucí článek o jednom způsobu řešení potřeby sekvencí bez chybějících hodnot.


  1. 10 nejčastějších dotazů o sledování výkonu serveru SQL Server

  2. Vyhledejte řetězec v textovém sloupci v MySQL

  3. Funkce JSON_VALUE() v Oracle

  4. Ekvivalent varchar(max) v MySQL?