Budeme zde vytvářet hodně tabulek, které později již nebudeme používat, tak je nezapomeňte smazat příkazem DROP TABLE.
To ovšem nemusí být vždy nejlepší řešení. Defaultní hodnoty Vám mohou ušetřit trochu toho psaní a navíc vám zajistí, že v sloupci nebudete mít tu ošklivou hodnotu NULL. (Typicky datový typ bool většinou není dobré nechávat nevyplněný).
Příklad: Dejme tomu, že pravidelně sázím sportku za 60Kč. Vytvořím si tedy tabulku, kde bude kolik jsem vsadil, kdy a kolik jsem vyhrál. Záznam o sázce vložím vždy ten den, kdy vsadím a záznam o výhře budu měnit dodatečně při tahu sportky. Vytvořím tedy defaultní hodnotu nejen pro sázku 60Kč, ale i pro výhru 0Kč. Když pak nevyhraji, nemusím se obtěžovat záznam měnit a nebudu mít nikde ve sloupci výher hodnotu NULL (pokud si jí tam schválně nevložím :-)). Protože sázky i výhry budou v celých korunách, použiji jako typ celé číslo (integer).
Nyní vložím do tabulky sportka několik "sázek" (příkazem INSERT).rimmer1=> CREATE TABLE sportka (datum DATE,vsazeno INTEGER DEFAULT 60, rimmer1(> vyhra INTEGER DEFAULT 0); CREATE
rimmer1=> INSERT INTO sportka(datum) VALUES ('2002-22-09');
INSERT 18663 1
rimmer1=> INSERT INTO sportka(datum) VALUES ('2002-23-09');
INSERT 18664 1
rimmer1=> INSERT INTO sportka(datum,vsazeno) VALUES ('2002-24-09',120);
INSERT 18665 1
rimmer1=> SELECT * FROM sportka;
datum | vsazeno | vyhra
------------+---------+-------
2002-09-22 | 60 | 0
2002-09-23 | 60 | 0
2002-09-24 | 120 | 0
(3 rows)
Defaultní hodnotou může být také výraz. Opět ukážu na
příkladě:
Představte si, že vedete malý obchod s cukrovým. Jedno ze zboží, které objednáváte je "Sladký rohlík". Většinou jich objednáváte 100 Kusů a jejich trvanlivost je 5 dní. Pokud si je objednáte ve speciálním balení, bude také jejich trvanlivost delší. Musíte také vědět, kdo Vám rohlíky dovezl (kvůli případné reklamaci). Váš obvyklý závozník se jmenuje Vladimír Lenin (ale někdy se může stát, že třeba potřebujete větší množství a tak si objednáte i u někoho jiného). Přehled o zboží si pak budete udržovat v takovéto tabulce:
Poznámka: current_date je funkce, která vrací hodnotu aktuálního datumu. Funkcemi se budeme zabývat později.rimmer1=> CREATE TABLE sladky_rohlik (zavoznik CHAR(30) rimmer1(> DEFAULT 'Vladimir Lenin', pocet INTEGER DEFAULT 100, rimmer1(> dovezeno DATE DEFAULT current_date, rimmer1(> spotrebovat_do DATE DEFAULT current_date+5); CREATE rimmer1=> INSERT INTO sladky_rohlik(pocet) VALUES(100); INSERT 18670 1
Bohužel nemůžete do výrazu použít hodnotu sloupce (hezčí by jistě bylo mít defaultní hodnotu ve sloupci spotrebovat_do "dovezeno+5"). Další malou nepříjemností je to, že příkaz INSERT musí obdržet jako argument hodnotu alespoň jednoho sloupce (i když mají všechny sloupce svou defaultní hodnotu). Vyzkoušejte.
Například budete udržovat databázi zboží na skladě. Nemůžete prodávat zboží, které nemáte a tak si v databázi určíte podmínku, že množství zboží nesmí být menší než 0. Máte také omezené místo ve skladu, proto nesmí žádné zboží přesáhnout počet 500 kusů (Tolik zboží by jste ani nestihly do příští objednávky prodat). Když se pak pokusíte odečíst z databáze více zboží, než máte na skladě, nebo naopak více zboží objednat, než kolik můžete přijmout, nepůjde to. Zboží musí mít také svou cenu. I cena by měla být vyšší než 0.
Taková tabulka může vypadat následovně:
Teď se pokusím vložit nějaké hodnoty do tabulky:rimmer1=> CREATE TABLE zbozi (nazev CHAR(20), rimmer1(> pocet INTEGER CHECK (pocet >= 0 AND pocet <= 500), rimmer1(> cena NUMERIC(8,2) CHECK (cena >= 0)); CREATE
rimmer1=> INSERT INTO zbozi VALUES ('Kulomety',500,1499.90);
INSERT 18681 1
rimmer1=> INSERT INTO zbozi VALUES ('Tarasnice',0,500);
INSERT 18682 1
rimmer1=> UPDATE zbozi SET pocet = pocet + 100;
ERROR: ExecReplace: rejected due to CHECK constraint zbozi_pocet
rimmer1=> INSERT INTO zbozi VALUES ('Samopaly',501,1499.90);
ERROR: ExecAppend: rejected due to CHECK constraint zbozi_pocet
rimmer1=> INSERT INTO zbozi(nazev,pocet) VALUES ('Samopaly',300);
INSERT 18683 1
Záznamy o Kulometech a Tarasnicích jsem vložil bez problémů.
Splňovali všechny podmínky. Pokus o zvýšení všech položek o 100
kusů neprošel, neboť jedna položka (Kulomety) by nesplnila
sloupcové omezení pocet <= 500; Příkaz UPDATE se tedy neprovedl
a nebyla změněná žádná položka v databázi. Ze stejného
důvodu nešlo vložit záznam o Samopalech.
Zajímavý je ovšem poslední příkaz INSERT. Po jeho provedení existuje položka, jejíž cena nebyla udána a je tedy NULL. My jsme ovšem po tabulce požadovali, aby byla u každého zboží cena! Tento problém vyřešíme v následujícím odstavci.
Náš problém z předchozího příkladu se pak vyřeší kombinací podmínek CHECK a NOT NULL (tabulku vytvoříme znova, proto jí nejdříve smažeme):
rimmer1=> DROP TABLE zbozi; DROP rimmer1=> CREATE TABLE zbozi (nazev CHAR(20), rimmer1(> pocet INTEGER CHECK (pocet >= 0 AND pocet <= 500) NOT NULL, rimmer1(> cena NUMERIC(8,2) NOT NULL CHECK (cena >= 0)); CREATE
Ukážeme si na příkladu, jak se dají sloupcové podmínky kombinovat. Řekněme, že chcete mít tabulku, kam smí být proveden jen jeden zápis denně:
Poznámka: current_date je funkce, která vrací hodnotu aktuálního datumu. Funkcemi se budeme zabývat později.rimmer1=> CREATE TABLE zapis (jmeno_zadatele VARCHAR(20) NOT NULL, rimmer1(> text_zapisu TEXT, rimmer1(> datum_zapisu DATE UNIQUE NOT NULL CHECK (datum_zapisu >=current_date) rimmer1(> DEFAULT current_date); NOTICE: CREATE TABLE / UNIQUE will create implicit index 'zapis_datum_zapisu_key' for table 'zapis' CREATE
Index slouží právě k tomu, aby mohl Postgres kontrolovat, že jsou data v sloupci datum_zapisu jedinečná (v případě podmínky UNIQUE; později uvidíte další využití indexu). O to jakým způsobem to dělá se nemusíte starat. Důležité je jen vědět, jak index zrušit:
DROP INDEX nazev_indexu;
Indexy, které máte v databázi zjistíte metapříkazem \di.
rimmer1=> \d zapis
Table "zapis"
Column | Type | Modifiers
----------------+-----------------------+------------------------------------
jmeno_zadatele | character varying(20) |
text_zapisu | text |
datum_zapisu | date | not null default date('now'::text)
Unique keys: zapis_datum_zapisu_key
Check constraints: "zapis_datum_zapisu" (datum_zapisu >= date('now'::text))
rimmer1=> \di
List of relations
Name | Type | Owner
------------------------+-------+--------
zapis_datum_zapisu_key | index | rimmer
(1 row)
Pokud zrušíte index, podmínka UNIQUE (nebo jiná, která index
využívá) bude z tabulky odstraněna, jinak se nic nezmění.
rimmer1=> DROP INDEX zapis_datum_zapisu_key;
DROP
rimmer1=> \d zapis
Table "zapis"
Column | Type | Modifiers
----------------+-----------------------+------------------------------------
jmeno_zadatele | character varying(20) |
text_zapisu | text |
datum_zapisu | date | not null default date('now'::text)
Check constraints: "zapis_datum_zapisu" (datum_zapisu >= date('now'::text))
Vytváření indexu
