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

Pochopení kontrolních omezení v PostgreSQL

Správa dat je velká výzva. Jak se náš svět obrací, data jsou stále rozšířená, hojná a intenzivní. Proto musíme přijmout opatření, abychom tento příliv zvládli.

Ověřování každého jednotlivého kusu dat 'ručně 24 hodin denně je prostě nepraktické. Jaký fantastický sen. Ale koneckonců je to právě tak. Sen. Špatná data jsou špatná data. Bez ohledu na to, jak to nakrájíte nebo nakrájíte na kostičky (zamýšlená slovní hříčka). Je to problém od počátku, který vede k ještě větším problémům.

Moderní databáze zvládají velkou část těžkých zátěží pro nás. Mnoho z nich poskytuje vestavěná řešení, která pomáhají při správě této konkrétní oblasti dat.

Jistý způsob, jak ovládat data zadávaná do sloupce tabulky, je datový typ. Potřebujete sloupec s desetinnými čísly s celkovým počtem číslic 4, přičemž 2 z nich jsou za desetinnou čárkou?

Jasná věc! Vůbec žádný problém.

NUMERIC(4,2), schůdná možnost, hlídá tento sloupec jako hlídací pes. Mohou tam vklouznout hodnoty znakového textu? Žádná sněhová koule.

PostgreSQL nabízí velké množství datových typů. Je pravděpodobné, že jeden již existuje, aby uspokojil vaše potřeby. Pokud ne, můžete si vytvořit vlastní. (Viz:PostgreSQL CREATE TYPE)

Samotné datové typy však nestačí. Nemůžete zaručit, že budou pokryty ty nejkonkrétnější požadavky a že budou vyhovovat tak širokému strukturování. Při navrhování schématu jsou obvykle vyžadována pravidla shody a nějaký druh „standardu“.

Předpokládejme, že ve stejném sloupci NUMERIC(4,2) chcete pouze hodnoty větší než 25,25, ale menší než 74,33? V případě, že je uložena hodnota 88.22, datový typ není na závadu. Tím, že povolí 4 celkové číslice, s maximálně 2 za desetinnou čárkou, dělá svou práci. Svalte vinu jinam.

Jak vyhrajeme na této frontě, pokud jde o kontrolu údajů povolených v naší databázi? Datová konzistence je nejvyšší prioritou a je nedílnou součástí každého spolehlivého datového řešení. Při (ne)pravděpodobnosti, že byste kontrolovali shromážděná data od počátku jejich zdroje, by konzistence pravděpodobně představovala menší problém.

Ale dokonalý svět existuje (možná) pouze v jednom z mnoha fantasy románů, které rád čtu.

Bohužel neúplná, nekonzistentní a „špinavá“ data jsou až příliš běžné charakteristiky a reality přítomné v oblasti zaměřené na databáze.

Nicméně, ne vše je ztraceno ve zkáze a temnotě, protože máme kontrolní omezení, která tyto problémy zmírňují. Pro tato konkrétní pravidla musíme z nutnosti zavést, která zajistí, že budeme zpracovávat a ukládat pouze konzistentní data. Nařízením těchto specifikací v databázi můžeme minimalizovat dopad, který mají nekonzistentní data na naše obchodní cíle a řešení.

Co je omezení? - Definice na vysoké úrovni

V tomto kontextu je omezení typ pravidla nebo omezení umístěného na sloupec databázové tabulky. Tato specifičnost vyžaduje, aby přicházející data před uložením vyhovovala stanoveným požadavkům. Uvedené požadavky bývají „profesionálně“ vytvořeny (a často jsou) jako obchodní pravidla . To se scvrkává na ověřovací booleovský test pravdy. Pokud data projdou (pravda), jsou uložena. Pokud ne, žádná položka (false).

Omezení dostupná v PostgreSQL

V době psaní tohoto článku uvádí dokumentace PostgreSQL 6 kategorií omezení.

Jsou to:

  • Zkontrolujte omezení
  • Není nulová omezení
  • Jedinečná omezení
  • Primární klíče
  • Cizí klíče
  • Omezení vyloučení

Zkontrolujte omezení

Jednoduchým příkladem pro sloupec INTEGER by bylo zakázání hodnot větších než řekněme 100.

learning=> CREATE TABLE no_go(id INTEGER CHECK (id < 100));
CREATE TABLE
learning=> INSERT INTO no_go(id) VALUES(101);
ERROR: new row for relation "no_go" violates check constraint "no_go_id_check"
DETAIL: Failing row contains (101).

Jak je vidět výše, pokusy o INSERT jakékoli hodnoty, které porušují podmínku Check, selžou.

Omezení kontroly nejen monitorují sloupce během INSERT, ale i příkazy UPDATE (a další, např. \copy a COPY) musí také dodržovat omezení.

Předpokládejme, že tabulka no_go má tuto hodnotu:

learning=> TABLE no_go;
id 
----
55
(1 row)

Selhala také AKTUALIZACE hodnoty sloupce id na hodnotu, která neodpovídá omezení kontroly:

learning=> UPDATE no_go SET id = 155
learning-> WHERE id = 55;
ERROR: new row for relation "no_go" violates check constraint "no_go_id_check"
DETAIL: Failing row contains (155).

Omezení kontroly musí „dávat smysl“ pro datový typ cílového sloupce. Je neplatné pokoušet se omezovat sloupec INTEGER tak, aby zakazoval ukládání textových hodnot, protože samotný datový typ to neumožňuje.

Podívejte se na tento příklad, kde se pokouším zavést tento typ omezení kontroly během vytváření tabulky:

learning=> CREATE TABLE num_try(id INTEGER CHECK(id IN ('Bubble', 'YoYo', 'Jack-In-The-Box')));
ERROR: invalid input syntax for integer: "Bubble"

Život bez kontrolních omezení

Jedno staré přísloví, které jsem slyšel a které se mnou rezonuje, zní:„Voda vám nechybí, dokud studna nevyschne . "

."

Bez kontrolních omezení se můžeme jistě spojit, protože jejich pozoruhodný přínos nejvíce oceníte, když se bez nich musíte obejít.

Vezměte si tento příklad…

Pro začátek máme tuto tabulku a data, která představují materiály povrchu stezky:

learning=> SELECT * FROM surface_material;
surface_id | material 
------------+--------------
101 | Gravel
202 | Grass
303 | Dirt
404 | Turf
505 | Concrete
606 | Asphalt
707 | Clay
808 | Polyurethane
(8 rows)

A tato tabulka s názvy stezek a vlastním surface_id:

learning=> SELECT * FROM trails;
id | name | surface_id 
----+-----------------+------------
1 | Dusty Storm | 303
2 | Runners Trip | 808
3 | Pea Gravel Pass | 101
4 | Back 40 Loop | 404
(4 rows)

Chceme zajistit, aby tabulka trails obsahovala pouze surface_id pro odpovídající hodnoty v tabulce surface_material.

Ano ano, já vím. Křičíš na mě.

Nedá se to vyřešit pomocí CIZÍ KLÍČ?!?"

Ano, může. Ale používám to k demonstraci obecného použití spolu s úskalím, které je třeba znát (zmíněno dále v příspěvku).

Bez omezení Check se můžete uchýlit k TRIGGER a zabránit ukládání nekonzistentních hodnot.

Zde je hrubý (ale fungující) příklad:

CREATE OR REPLACE FUNCTION check_me()
RETURNS TRIGGER AS
$$
BEGIN
IF NEW.surface_id NOT IN (SELECT surface_id FROM surface_material)
THEN Raise Exception '% is not allowed for surface id', NEW.surface_id;
END IF;
RETURN NEW;
END;
$$ LANGUAGE PLpgSQL;
CREATE TRIGGER check_me BEFORE INSERT OR UPDATE ON trails
FOR EACH ROW EXECUTE PROCEDURE check_me();

Pokusy o VLOŽENÍ hodnoty, která nemá odpovídající surface_id ve stopách tabulky, se nezdaří:

learning=> INSERT INTO trails(name, surface_id)
learning-> VALUES ('Tennis Walker', 110);
ERROR: 110 is not allowed for surface id
CONTEXT: PL/pgSQL function check_me() line 4 at RAISE

Níže uvedené výsledky dotazu potvrzují 'porušení ' hodnota nebyla uložena:

learning=> SELECT * FROM trails;
id | name | surface_id 
----+-----------------+------------
1 | Dusty Storm | 303
2 | Runners Trip | 808
3 | Pea Gravel Pass | 101
4 | Back 40 Loop | 404
(4 rows)

To jistě dá hodně práce zakázat nechtěné hodnoty.

Pojďme tento požadavek znovu implementovat s omezením Check.

Protože nemůžete použít poddotaz (zde je důvod, proč jsem použil výše uvedený příklad) ve skutečné definici omezení kontroly, hodnoty musí být pevně zakódované .

Pro malý stůl nebo triviální příklad, jaký je zde uveden, je to v pořádku. V jiných scénářích, které zahrnují více hodnot, může být lepší hledat alternativní řešení.

learning=> ALTER TABLE trails ADD CONSTRAINT t_check CHECK (surface_id IN (101, 202, 303, 404, 505, 606, 707, 808));
ALTER TABLE

Zde jsem pojmenoval omezení kontroly t_check versus nechal systém pojmenovat.

(Poznámka:Dříve definované FUNKCE check_me() a doprovodná TRIGGER byly vypuštěny (nezobrazeno) před spuštěním níže uvedeného INSERT.)

learning=> INSERT INTO trails(name, surface_id)
VALUES('Tennis Walker', 110);
ERROR: new row for relation "trails" violates check constraint "t_check"
DETAIL: Failing row contains (7, Tennis Walker, 110).

Podívali byste se, jak snadné to bylo! Není potřeba žádný TRIGGER a FUNCTION.

Díky kontrolním omezením je tento typ práce snadný.

Chcete se zorientovat v definici kontroly omezení?

Můžete.

Předpokládejme, že potřebujete tabulku se seznamem stezek, které jsou o něco šetrnější k těm s citlivými kotníky a koleny. Nejsou zde požadovány žádné tvrdé povrchy.

Chcete se ujistit, že jakákoli turistická stezka nebo trasa uvedená v tabulce nice_trail má povrchový materiál buď 'Gravel' nebo 'Dirt'.

Toto omezení kontroly zvládá tento požadavek bez problémů:

learning=> CREATE TABLE nice_trail(id SERIAL PRIMARY KEY,
learning(> name TEXT, mat_surface_id INTEGER CONSTRAINT better_surface CHECK(id IN (101, 303))); 
CREATE TABLE

To naprosto funguje dobře.

Ale co třeba FUNKCE, která vrací obě id požadované k tomu, aby kontrola fungovala? Je povolena FUNKCE v definici kontroly omezení?

Ano, jeden může být začleněn.

Zde je pracovní příklad.

Nejprve tělo funkce a definice:

CREATE OR REPLACE FUNCTION easy_hike(id INTEGER)
RETURNS BOOLEAN AS
$$
BEGIN
IF id IN (SELECT surface_id FROM surface_material WHERE material IN ('Gravel', 'Dirt'))
THEN RETURN true;
ELSE RETURN false;
END IF;
END;
$$ LANGUAGE PLpgSQL;

Všimněte si, že v tomto příkazu CREATE TABLE definuji omezení kontroly v 'tabulce ', zatímco dříve jsem uvedl pouze příklady ve 'sloupci ' úroveň.

Kontrolní omezení definovaná na úrovni tabulky jsou dokonale platná:

learning=> CREATE TABLE nice_trail(nt_id SERIAL PRIMARY KEY,
learning(> name TEXT, mat_surface_id INTEGER,
learning(> CONSTRAINT better_surface_check CHECK(easy_hike(mat_surface_id)));
CREATE TABLE

Všechny tyto vložky jsou dobré:

learning=> INSERT INTO nice_trail(name, mat_surface_id)
learning-> VALUES ('Smooth Rock Loop', 101), ('High Water Bluff', 303);
INSERT 0 2

Nyní přichází INSERT pro stezku, která nesplňuje omezení pro sloupec mat_surface_id:

learning=> INSERT INTO nice_trail(name, mat_surface_id)
learning-> VALUES('South Branch Fork', 404);
ERROR: new row for relation "nice_trail" violates check constraint "better_surface_check"
DETAIL: Failing row contains (3, South Branch Fork, 404).

Naše volání FUNCTION v definici kontroly omezení funguje tak, jak bylo navrženo, a omezuje nežádoucí hodnoty sloupců.

Kouř a zrcadla?

Je vše, jak se zdá, s omezeními Check? Vše černobílé? Žádná fasáda vpředu?

Příklad, který stojí za zmínku.

Máme jednoduchou tabulku, ve které chceme, aby hodnota DEFAULT byla 10 pro jediný přítomný sloupec INTEGER:

learning=> CREATE TABLE surprise(id INTEGER DEFAULT 10, CHECK (id <> 10));
CREATE TABLE

Zahrnul jsem však také omezení kontroly, které zakazuje hodnotu 10, protože id se nemůže rovnat tomuto číslu.

Který den vyhraje? Omezení DEFAULT nebo Check?

Možná budete překvapeni, když víte, která to je.

Byl jsem.

Libovolný INSERT, který funguje dobře:

learning=> INSERT INTO surprise(id) VALUES(17);
INSERT 0 1
learning=> SELECT * FROM surprise;
id 
----
17
(1 row)

A INSERT s hodnotou DEFAULT:

learning=> INSERT INTO surprise(id) VALUES(DEFAULT);
ERROR: new row for relation "surprise" violates check constraint "surpise_id_check"
DETAIL: Failing row contains (10).

Jejda...

Opět s alternativní syntaxí:

learning=> INSERT INTO surprise DEFAULT VALUES;
ERROR: new row for relation "surprise" violates check constraint "surpise_id_check"
DETAIL: Failing row contains (10).

Omezení kontroly vítězí nad hodnotou DEFAULT.

Příklad liché

Omezení Kontrola se během vytváření může objevit prakticky kdekoli v definici tabulky. Dokonce i na úrovni sloupců jej lze nastavit na sloupec, který se do kontroly vůbec nepodílí.

Zde je příklad pro ilustraci:

learning=> CREATE TABLE mystery(id_1 INTEGER CHECK(id_2 > id_3),
learning(> id_2 INTEGER, id_3 INTEGER);
CREATE TABLE

INSERT pro testování omezení:

learning=> INSERT INTO mystery(id_1, id_2, id_3) VALUES (1, 2, 3);
ERROR: new row for relation "mystery" violates check constraint "mystery_check"
DETAIL: Failing row contains (1, 2, 3).

Funguje tak, jak má.

VALIDACE a NEPLATNÉ

Máme tuto jednoduchou tabulku a data:

learning=> CREATE TABLE v_check(id INTEGER);
CREATE TABLE
learning=> INSERT INTO v_check SELECT * FROM generate_series(1, 425);
INSERT 0 425

Předpokládejme, že nyní potřebujeme implementovat omezení Check, které zakazuje jakékoli hodnoty menší než 50.

Představte si, že se jedná o velký stůl ve výrobě a v tuto chvíli si skutečně nemůžeme dovolit žádný získaný zámek, vyplývající z příkazu ALTER TABLE. Ale je potřeba toto omezení zavést a posunout se vpřed.

ALTER TABLE získá zámek (v závislosti na každém různém podformuláři). Jak již bylo zmíněno, tento stůl je ve výrobě, takže si přejeme počkat, až budeme mimo 'špičku '.

Při vytváření omezení Kontrola:

můžete použít volbu NO VALID
learning=> ALTER TABLE v_check ADD CONSTRAINT fifty_chk CHECK(id > 50) NOT VALID; 
ALTER TABLE
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

Pokračující operace v případě pokusu o INSERT nebo UPDATE, který porušuje omezení kontroly:

learning=> INSERT INTO v_check(id) VALUES(22);
ERROR: new row for relation "v_check" violates check constraint "fifty_chk"
DETAIL: Failing row contains (22).

Hodnota sloupce 'nevhodná' je zakázána.

Poté během odstávky ověříme omezení kontroly, abychom ho použili na (jakékoli) již existující sloupce, které mohou porušovat:

learning=> ALTER TABLE v_check VALIDATE CONSTRAINT fifty_chk;
ERROR: check constraint "fifty_chk" is violated by some row

Zpráva je podle mého názoru dost záhadná. Informuje nás však, že existují řádky, které nejsou v souladu s omezením.

Zde je několik klíčových bodů, které jsem chtěl zahrnout z dokumentace ALTER TABLE (Verbiage přímo z dokumentů v uvozovkách):

  • Syntaxe:ADD table_constraint [ NENÍ PLATN ZKONTROLUJTE omezení. Pokud je omezení označeno jako NEPLATNÉ, bude potenciálně zdlouhavá úvodní kontrola k ověření, že všechny řádky v tabulce vyhovují omezení, přeskočena."
  • Syntaxe:VALIDATE CONSTRAINT název_podmínky – Doprovodný popis (částečný) "Tento formulář ověřuje platnost cizího klíče nebo kontrolního omezení, které bylo dříve vytvořeno jako NEPLATNÉ, prohledáním tabulky, aby se zajistilo, že neexistují žádné řádky, pro které by omezení nebylo splněno. " "Ověření získává pouze zámek SHARE UPDATE EXCLUSIVE na stole, který se mění."

Kromě toho, dva body stojí za zmínku, které jsem se naučil během cesty. Funkce vracející sady a poddotazy nejsou v definicích kontroly povoleny. Jsem si jistý, že existují další a uvítám jakoukoli zpětnou vazbu na ně v komentářích níže.

Omezení kontroly jsou úžasná. Použití „vestavěných“ řešení poskytovaných samotnou databází PostgreSQL k vynucení jakýchkoli omezení dat dává dokonalý smysl. Čas a úsilí vynaložené na implementaci kontrolních omezení pro nezbytné sloupce výrazně převažují nad tím, že se neimplementují vůbec žádná. Z dlouhodobého hlediska tak šetří čas. Čím více se opíráme o databázi, abychom zvládli tyto druhy požadavků, tím lépe. Umožňuje nám soustředit se a aplikovat naše zdroje na jiné oblasti/aspekty správy databází.

Děkuji za přečtení.


  1. PŘIPOJIT SE k MySQL vs POUŽÍVAT?

  2. Výjimka SQLite při pokusu o odstranění řádku

  3. Jak aktualizovat vybrané řádky hodnotami ze souboru CSV v Postgresu?

  4. PGLogical 1.1 balíčky pro PostgreSQL 9.6beta1