Inleiding
PostgreSQL levert native een rijke diversiteit aan gegevenstypen die veel praktische gebruiksscenario's ondersteunen. Dit artikel introduceert de speciale implementatie van seriële datatypes die doorgaans worden gebruikt voor het maken van synthetische primaire sleutels.
Unieke sleutels
Een basisprincipe van de database-ontwerptheorie is dat elke tuple (d.w.z. rij) van een relatie (d.w.z. tabel) uniek moet worden geïdentificeerd ten opzichte van andere tuples. De attributen, of kolommen, die samen één tuple duidelijk onderscheiden van alle andere, worden een "sleutel" genoemd. Sommige puristen beweren dat elk gemodelleerd object of concept inherent een attribuut of set attributen bezit die als een sleutel kunnen dienen en dat het belangrijk is om deze set sleutelattributen te identificeren en ze te gebruiken voor de unieke selectie van tuples.
Maar in de praktijk kan het onpraktisch zijn om een voldoende grote set attributen te identificeren die de uniciteit van een gemodelleerd object garanderen, en daarom wenden ontwikkelaars zich voor implementaties in de echte wereld vaak tot synthetische sleutels als surrogaat. Dat wil zeggen, in plaats van te vertrouwen op een of andere combinatie van feitelijke attributen, wordt een waarde die intern is in de database, typisch verhoogde integerwaarden, en die verder geen fysieke betekenis heeft, gedefinieerd als een sleutel. Naast de eenvoud van een enkele kolomsleutel, betekent het feit dat er geen echte afhankelijkheid is, dat externe factoren nooit een noodzaak kunnen afdwingen om de waarde te wijzigen, zoals bijvoorbeeld het geval zou kunnen zijn als de naam van een persoon wordt gebruikt als een sleutel ... en toen trouwde de persoon of ging een getuigenbeschermingsprogramma van de federale overheid in en veranderde zijn naam. Zelfs sommige waarden die door leken gewoonlijk als uniek en onveranderlijk worden beschouwd, zoals het Amerikaanse sofinummer, zijn geen van beide:een persoon kan een nieuw SSN krijgen en SSN's worden soms hergebruikt.
Een serieel gegevenstype aangeven
PostgreSQL biedt een speciale gegevenstypedeclaratie om aan deze behoefte aan synthetische sleutels te voldoen. Het declareren van een databasetabelkolom als type SERIAL voldoet aan de vereiste voor synthetische sleutels door unieke gehele getallen te leveren bij het invoegen van nieuwe tuples. Dit pseudo-gegevenstype implementeert een kolom van het gegevenstype integer met een bijbehorende standaardwaarde die is afgeleid via een functieaanroep die verhoogde integerwaarden levert. De volgende code uitvoeren om een eenvoudige tabel te maken met een id-kolom van het type 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);
Dat wil zeggen, het sleutelwoord "serieel" als een gegevenstypespecificatie impliceert de uitvoering van DDL-instructies die een kolom van het type integer maken met een NOT NULL-beperking, een SEQUENCE, en vervolgens wordt de standaardkolom van de kolom GEWIJZIGD om een ingebouwde functie aan te roepen die toegang heeft tot die SEQUENCE.
De ingebouwde functie nextval voert een auto-increment-service uit:elke keer dat nextval wordt aangeroepen, wordt de gespecificeerde reeksteller verhoogd en wordt de nieuw verhoogde waarde geretourneerd.
U kunt het resultaat van dit effect zien door de tabeldefinitie te bekijken:
postgres=# \d person
Table "public.person"
Column | Type | Modifiers
-----------+---------+-----------------------------------------
Id | integer | not null default nextval('person_id_seq'::regclass)
full_name | text |
Seriële waarden invoegen
Om gebruik te maken van de functie voor automatisch verhogen, voegen we eenvoudig rijen in, gebaseerd op de standaardwaarde voor de seriële kolom:
INSERT INTO person (full_name) VALUES ('Alice');
SELECT * FROM person;
id | full_name
----+-----------
1 | Alice
(1 row)
We zien dat er automatisch een waarde is gegenereerd voor de id-kolom die overeenkomt met de nieuwe rij "Alice". Als alternatief kan men het DEFAULT-sleutelwoord gebruiken als expliciete vermelding van alle kolomnamen gewenst is:
INSERT INTO person (id, full_name) VALUES (DEFAULT, 'Bob');
SELECT * FROM person;
id | full_name
----+-----------
1 | Alice
2 | Bob
(2 rows)
waar we de functie voor automatisch verhogen meer duidelijk zien, door de serieel-volgende waarde toe te wijzen aan de nieuwe rij voor de tweede invoeging van "Bob".
Het invoegen van meerdere rijen werkt zelfs:
INSERT INTO person (full_name) VALUES ('Cathy'), ('David');
SELECT * FROM person;
id | full_name
----+-----------
1 | Alice
2 | Bob
3 | Cathy
4 | David
(4 rows)
Download de whitepaper vandaag PostgreSQL-beheer en -automatisering met ClusterControlLees wat u moet weten om PostgreSQL te implementeren, bewaken, beheren en schalenDownload de whitepaper Ontbrekende seriewaarden
De ingebouwde nextval-functie is geoptimaliseerd voor non-blocking, high-concurrency applicaties en houdt dus geen rekening met rollback. Dit betekent dat er mogelijk waarden in de reeks ontbreken. Hieronder draaien we een invoeging terug, maar zien dan dat een volgende invoeging een nieuwe waarde krijgt die de waarde overslaat die zou zijn geassocieerd met de afgebroken transactie:
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)
Het voordeel van het niet respecteren van terugdraaiingen is dat andere sessies die gelijktijdige invoegingen proberen niet worden geblokkeerd door andere invoegsessies.
Een andere manier om te eindigen met ontbrekende waarden is als rijen worden verwijderd:
DELETE FROM person WHERE full_name = 'Cathy';
SELECT * FROM person;
id | full_name
----+-----------
1 | Alice
2 | Bob
4 | David
6 | Fred
(4 rows)
Merk op dat zelfs na het verwijderen van de meest recent ingevoegde rij die overeenkomt met de grootste auto-increment id-kolomwaarde, de reeksteller niet terugkeert, d.w.z. ook al behoudt de reeksteller na het verwijderen van de rij die overeenkomt met 'Fred' voor volgende invoegingen de reeksteller nog steeds de eerder bekende grootste waarde en stappen vanaf daar:
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)
Hiaten of ontbrekende waarden, zoals hierboven weergegeven, worden naar verluidt door sommige applicatieontwikkelaars als een probleem beschouwd, omdat op de PostgreSQL General-mailinglijst een langzame maar gestage herhaling is van de vraag hoe sequentiehiaten kunnen worden vermeden bij het gebruik van het seriële pseudo-datatype. Soms is er geen echte onderliggende zakelijke behoefte, het is gewoon een kwestie van persoonlijke afkeer van ontbrekende waarden. Maar er zijn omstandigheden waarin het voorkomen van ontbrekende nummers een reële noodzaak is, en dat is het onderwerp voor een volgend artikel.
NEE KUNT U NIET - JA U KAN!
De NOT NULL-beperking die door het seriële pseudo-datatype wordt geïmplanteerd, beschermt tegen het invoegen van NULL voor de id-kolom door dergelijke invoegpogingen te weigeren:
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).
We zijn er dus zeker van dat we een waarde voor dat kenmerk hebben.
Een probleem dat sommige mensen echter tegenkomen, is dat, zoals hierboven aangegeven, niets het expliciet invoegen van waarden verhindert, waarbij de standaardwaarde voor automatisch ophogen wordt omzeild die is afgeleid via het aanroepen van de nextval-functie:
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)
Maar dan twee invoegingen later met de standaardwaarde produceert een dubbele waarde voor de id-kolom als er geen beperkingscontrole is van kolomwaarden ten opzichte van de reekswaarde:
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)
Als we in feite de kolom met het serienummer als sleutel zouden gebruiken, zouden we deze als PRIMAIRE SLEUTEL hebben gedeclareerd of op zijn minst een UNIEKE INDEX hebben gemaakt. Als we dat hadden gedaan, zou de 'Karen'-invoeging hierboven zijn mislukt met een dubbele sleutelfout. De meest recente release van PostgreSQL bevat een nieuwe syntaxis voor beperkingsdeclaraties die standaard als identiteit wordt gegenereerd, waardoor deze valkuil en enkele andere legacy-problemen met betrekking tot het seriële pseudo-gegevenstype worden vermeden.
Sequence Manipulatie Functies
Naast de functie nextval die we al noemden, die de reeks voortschrijdt en de nieuwe waarde retourneert, zijn er een paar andere functies voor het opvragen en instellen van de reekswaarden:de functie currval retourneert de meest recentelijk verkregen waarde met nextval voor de opgegeven reeks, de functie lastval retourneert de meest recentelijk verkregen waarde met nextval voor elke reeks, en de setval-functie stelt de huidige waarde van een reeks in. Deze functies worden aangeroepen met eenvoudige zoekopdrachten., bijvoorbeeld
SELECT currval('person_id_seq');
currval
---------
9
(1 row)
En merk op dat als een aanroep van de nextval-functie wordt gedaan, onafhankelijk van het daadwerkelijk uitvoeren van een invoeging, dit de volgorde verhoogt, en dat zal worden weerspiegeld in volgende invoegingen:
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)
Conclusie
We hebben een basiskennis geïntroduceerd van het PostgreSQL SERIAL pseudo-gegevenstype voor automatisch verhoogde synthetische sleutels. Ter illustratie in dit artikel hebben we de declaratie van het type SERIAL gebruikt, waarmee een integerkolom van 4 bytes wordt gemaakt. PostgreSQL komt tegemoet aan verschillende bereikbehoeften met de pseudo-gegevenstypen SMALLSERIAL en BIGSERIAL voor respectievelijk 2-byte en 8-byte kolomgroottes. Zoek naar een toekomstig artikel over een manier om de behoefte aan reeksen zonder ontbrekende waarden aan te pakken.