Osobně daleko nejlépe hodnotím možnost použít INSERT, UPDATE a DELETE v CTE. Díky tomu lze řešit spoustu věcí, na které byly třeba stored procedures.
Namátkou např. autoincrement v updatable pohledu sestávajícím se z více tabulek, nebo smazat data z jedné tabulky a zároveň ji vložit do druhé.
Druhá možnost je IMHO obzvlášť užitečná, protože když to chtěl člověk dřív dělat dvěma kroky a neměl repeatable read, tak musel řešit, aby toho nesmazal víc, než vložil.
Co mě v poslední době v postgresql chybělo.. teda spíš přebejvalo :-)... jsou různý omezení na rekurzivní CTE (např. zákaz limit, omezený možnosti joinování apod.)
Přeju Oraclu, aby vývojáři co nejvíc zamakali na přenositelnosti z Oracle Database (stále to ještě není ono ale poslední dobou obrovský pokrok) a z Postgresu se stal opravdový Oracle DB Killer. Má jasně našlápnuto a první vlaštovky různých supportních firem (EnterpriseDB) jsou na trhu také.
Chci víc konkurence v enterprise rdbm světě. Tedy méně Oraclu.
Co s navázanýma produktama má proboha společného to, že na poli čistě relačních databází může být PostgreSQL špičkovým konkurentem?!?
Posledně mi obchodník z Oraclu říkal, že sice prodej ostatních produktů jde nahoru, ale stále je absolutní top produkt čistá Oracle Database bez nějakých dalších serepetiček. Možná by tě překvapilo jak velké procento zákazníků chce právě a jenom tu relační databázi a nic jiného.
Takže je to přesně naopak, než se tady vyjadřuješ...
Pokud bych extrémně zjednodušil situaci na pouze read-only přístup, tak lze orientačně počítat s tím, že u jednoduchých SQL dotazů bude MySQL cca o 1/3 rychlejší (v průměru TPC-B test), případně několika násobně pomalejší (raw SELECT) u nudloidních tabulek. S vyšší složitostí dotazů klesá rychlost MySQL a roste rychlost dotazů u PostgreSQL. U složitějších dotazů může být PostgreSQL několikanásobně rychlejší - při použití pohledů, poddotazů. PostgreSQL má oproti MySQL složitější "formát" pro ukládání dat na disk + nějaká režie, a komplexnější planner - v PostgreSQL je větší nabídka vestavěných algoritmů pro JOIN, pro poddotazy - hlavně je tam hasjoin a hashagg, umí lépe používat víc indexů v jednom dotazu. MySQL s InnoDB používá clustrovaný index, s MyISAM zase extrémně jednoduchý formát na disku. MySQL je dlouhodobě optimalizováno na jednoduché dotazy, PostgreSQL je zase dlouhodobě optimalizovano na složitější dotazy - takže záleží, kde se nacházíte. Při testování, kterého jsem se účastnil a kde se porovnával výkon 5.5 a 9.1 v nejhorších případech (pro pg) byl pg 2x pomalejší, v jiných byl naopak 5x 10x rychlejší - databáze na kterých se testovalo byly něco mezi 5-11G. Z testů se ukazuje, že je pro dlouhodobé uživatele MySQL minimálně zajímavé důkladněji otestovat chování a výkon aplikací v PostgreSQL - minimálně ten zákazník, který platí testování, tak do PostgreSQL jde.
Pokud se neomezíme jen na read-only přístup, tak je situace zase složitější. PostgreSQL se nehodí na ukládání krátkodobých netransakčních dat - tj. data, která třeba po půl hodině smažeme - na druhou stranu se PostgreSQL docela dobře vyrovná s komplexnějším multiuživatelským přístupem - většinou lépe než MySQL - takže opět záleží co děláte. V PostgreSQL už je několik let group commit, který se do MySQL teprve přidává - tím se docela dost snižuje zátěž systému a volná kapacita může přispět k vyšší rychlosti dotazů. Prostě ohledně výkonu není univerzální vítěz - každá databáze je lepší v té oblasti pro kterou je dlouhodobě optimalizovaná.
Pokud vím, tak v MySQL je mnohem širší paleta replikačních modelů. Teď si nejsem jistý, jestli to už má 5.5 - MySQL by mělo obsahovat thread-pooling - což by mělo zajistit lepší chování v ms-win při velkém počtu souběžně připojených uživatelů (v PostgreSQL se musí řešit externí aplikací - pgpool nebo bucardo). Jinak ve všem ostatním (si myslím,- je to můj názor, který tu jen prezentuji, ale nikomu nevnucuji) je PostgreSQL dál než MySQL - komfort pro vývojáře, administrátory, nabídce datových typů, rozšiřitelnosti, podpoře geodat, v uložených procedurách.
MySQL hodně ztratila bitvou s Oraclem (poté co Oracle koupil InnoDB). Zase dneska existuje několik týmů, které se snaží optimalizovat výkon MySQL - v Googlu, ve Facebooku, v Perconě, v Oraclu (dost často jsou to lidé z původní MySQL a.b) - těm jde ale primárně o surový výkon elemntárních dotazů a monitoring a o nic jiného. A pár malých firmiček se snaží uspět se specializovanými engines - do low level optimalizace MySQL se asi dost investuje - hodně firem je na MySQL životně závislá a aktuálně není situace ohledně MySQL nijak zvlášť přehledná. PostgreSQL jde trochu jinou cestou - spíš než na web se orientuje na vnitropodnikové IS, telco, finance nebo embeded databáze u náročnějšího hw - rengeny, ústředny, ..
kdyz to shrnu pro jakykoliv slozitejsi datovy model a slozitejsi datovou vrstvu je lepsi postgres,zvlast od verze 9 sleduji i narust vykonu, coz u mysql od verze k verzi je horsi. Navic se pridaly problemy se zavislostmi, kterou mysql nepekne resi, napr. uznam ze chybe v navrhu triggeru jsem odstrelil celou tabulku jen protoze jsem jinou smazal... a tech veci tam bylo vic..
nelze stavet na tom ze nikdo pri vyvoji funkci a celkovem navrhu neudela chybu, z toho pohledu je pro me Mysql doslova nebezpecna databaze pri slozitejsi architekture
kdyz to shrnu pro jakykoliv slozitejsi datovy model a slozitejsi datovou vrstvu je lepsi postgres,zvlast od verze 9 sleduji i narust vykonu, coz u mysql od verze k verzi je horsi. Navic se pridaly problemy se zavislostmi, kterou mysql nepekne resi, napr. uznam ze chybe v navrhu triggeru jsem odstrelil celou tabulku jen protoze jsem jinou smazal... a tech veci tam bylo vic..
nelze stavet na tom ze nikdo pri vyvoji funkci a celkovem navrhu neudela chybu, z toho pohledu je pro me Mysql doslova nebezpecna databaze pri slozitejsi architekture
Přecijenom jsem si na jednu hodně otravnou vlastnost vzpomněl. Pokud je trigger procedure, tak při insertu není definován OLD tuple. Takže nejde udělat unvierzální trigger pro insert/update (respektive se tam v každý podmínce musí zjištovat zdali jde o insert nebo update, což je opruz).
Nešlo by s tím něco dělat? Úprava je to triviální a přispěla by k čitelnějšímu kódu. Nebo to jde nějak řešit, popř mi něco ušlo?
Tohle je zrovna věc, kterou nelze udělat, tak aby to bylo sémanticky správně - v PL/pgSQL a v SQL obecně se nerozlišuje mezi NULL hodnotou a složenou hodnotou, kde všechny položky jsou NULL - tudíž nelze použít NULL ve významu 'nedefinováno' nebo neplatné - tudíž fakticky, pokud píšete univerzální trigger, tak stejně musíte kód rozdělit do větví, podle události. Souhlasím s Vámi, že je to opruz, ale myslím si, že je to správně, protože to vede ke správnému kódu. Já osobně jsem během té doby, co dělám s Postgresem přešel od univerzálních triggerů a procedur k jednoúčelovým adresným triggerům a funkcím. Pokud zrovna nepíšete univerzální framework, tak s generickými triggery (čest výjimkám) narazíte - a jde to i proti smyslu uložených procedur (tak jak to chápu já). Každá uložená procedura včetně triggeru by měla řešit specificky jeden specifický problém a přistupovat stále ke stejným tabulkám - hlavně, když používáte PL/pgSQL. Tím si dost zjednodušíte život. V C lze psát genericky, ale v PL/pgSQL to moc nejde, resp. lze, ale musíte nadužívat EXECUTE - a výsledek může být dost pomalý a nečitelný.
Omlouvám se, tady Vám život neusnadním - pokud chcete psát univerzální triggery, tak je nepište v PL/pgSQL, ale v PL/Perlu nebo PL/Pythonu nebo v C. Dost věcí totiž v PL/pgSQL principiálně (z důvodu kešování prováděcích plánů) nelze napsat elegantně nebo efektivně.
Díky za odpověď. Mě jde o triviální a přitom hrozně častý případ, kdy trigger reaguje na změnu nějaké hodnoty. Např. proto, aby jí zkontroloval, nebo aby vykonal nějakou akci. Nebo naopak o "úklid" při změně či rušení objektu. V drtivém počtu případů je přitom třeba něco dělat, pokud se hodnota změnila, přičemž stav NULL je ekvivalentní stavu před založním objektu. Nejde tedy o nějaké generické triggery, ale právě o malé jednoúčelové funkce.
V současné době musím udělat pro každou operaci insert/update/delete jeden trigger, což vede k nečitelnému a hůř udržitelnému kódu (jedna kontrola je na třech místech), nebo tam musí bejt podmínka ve stylu
(TG_OP = 'UPDATE' AND .... ) OR (TG_OP = 'INSERT' AND ...) OR
což je nečitelné. Přitom by stačilo:
(NEW.value IS DISTINCT FROM OLD.value)
jen kdyby šlo nějak rozumně pracovat s OLD/NEW, když je nedefinované. Napadá mě spousta variant, jak to udělat, např.
- Nedefinované OLD/NEW by bylo NULL, tak, jak to už od verze 8.4 tvrdí dokumentace :-)
>> This variable is NULL in statement-level triggers and for INSERT operations.
Chápu, proč se Vám to systémově nelíbí, ale vzhledem k tomu, že v současném stavu nelze OLD tam, kde je nedefinované používat, tak by tato úprava nic nerozbila, kód, který by mohl být po úpravě špatně by před úpravou neběžel vůbec. Pro rozlišení, zdali má OLD = NULL význam nedefinováno nebo definováno, ale NULL, by šlo užít TG_OP stejně jako dnes.
- Kdyby to i přesto vadilo, tak doplněním nějakého přepínače do definice funkce, kterým by si člověk mohl nové chování explicitně zapnout. Např. zápis v následujícím tvaru by byl hezký a myslím i vcelku konzistentní (defaultní hodnota parametru).
function f(OLD = NULL, NEW = NULL) returns trigger
- Vytvořením funkce ekvivalentní COALESCE, která by z nedefinované hodnoty udělala definovanou. Podmínka typu
(NEW.value IS DISTINCT FROM defined(OLD,NULL).value)
nebo ještě lépe
(NEW.value IS DISTINCT FROM defornull(OLD).value)
je furt daleko čitelnější, než to, co se musí psát dnes.
Kdyby byl typ record podobnější refernci v Javě nebo ukazateli v C, tak by o tom šlo uvažovat. Ale protože není, tak si myslím, že by to nadělalo větší paseku než užitek. PL/pgSQL vychází s ADY a je snaha, aby potenciálně chybný kód šel napsat hůře i za cenu větší ukecanosti správného kódu.
Pokud Vám vadí nutnost testování TG_OP, tak piště triggery čistě pro jednotlivé události - nemá to žádný vliv na výkon. Musíte napsat víc omáčky :( ale zase kód může být o něco jednodušší a o fous rychlejší. Navíc pokud chcete porovnávat NEW a OLD tak je mnohem lepší tyto hodnoty porovnat mimo trigger - skrze podmíněný trigger. Tím můžete snížit počet volání triggeru (následně počet inicializací plpgsql) a urychlit provádění příkazu.
http://developer.postgresql.org/pgdocs/postgres/sql-createtrigger.html
CREATE TRIGGER log_update AFTER UPDATE ON accounts FOR EACH ROW WHEN (OLD.* IS DISTINCT FROM NEW.*) EXECUTE PROCEDURE log_account_update();
Podmíněný trigger to někdy částečně řeší, jenže...
V definici podmíněného triggeru taktéž můžu použít OLD jen v UPDATE triggeru. Takže sice napíšu jednu proceduru, ale zas místo jednoho triggeru tři. Navíc budu mít podmínku a z ní vyplývající akci na jiném místě, takže se čitelnost zhorší. Kdyby alespoň v postgresql existovali anonymní procedury (něco ve stylu):
CREATE TRIGGER ..... EXECUTE BLOCK $$ $$;
Další problém je v tom, že např. někdy tyto triggery udržují např. nějakou předpočítanou sumu apod. A v tom případě potřebuji vypočíst něco ve stylu
COALESCE(NEW.value) - COALESCE(old.value): takže tady opět i s podmíněným triggerem skončím u tří procedur. Nebo mohu to old.value a new.value předat jako parametr, ale tím se kód stane opět nepřehlednějším.
To, že se PLPGSQL snaží být maximálně bezpečný beru - u DB jazyka je to jen dobře. Ale nezdá se mi, že by explicitní vyžádání toho, aby bylo NEW definované na NULL, když jde o DELETE trigger nějak narušilo bezpečnost jazyka. Nemá to žádné sideefekty, jde pouze o definici jedné proměnné a její inicializaci na NULL. (A také by se sladil skutečný stav s dokumentací, byť uznávám, že opravit dokumentaci je asi snažší... jen to, že se to tam objevilo mi asi zavdalo plané naděje....).
PS: Ale přes ty všechny výhrady na postgresql po přechodu z mysql slintám blahem, to zas jo :-) Abych zas nevypadal jak nějakej nespokojenec.
to zas není trivka neb parametry funkce triggeru znamenají trochu něco jiného :( - v podstatě jsou to per statement parametry - navíc popisované chování NULL a OLD se váže na trigger nikoliv na funkci, takže logičtější umístění flagů by bylo v definici triggeru. Ve standardu je možnost přejmenovat NEW a OLD - něco by se tam vešlo - nicméně si nemyslím, že by tato možnost v PostgreSQL v dohledné době byla - pokud by někdo nenapsal patch, kterým by se implementovala plná ANSI SQL syntaxe triggerů. Takový patch by asi akceptován byl. Párkrát jsem narazil :). Z důvody údržby kódu je dost velký (a mnou chápaný) odpor k přidávání funkcí, které lze označit jako "syntactic sugar". U každé nové funkce se musí prokázat, že díky ní lze psát rychlejší aplikace - občas - téměř vždy je to krvavý boj - v diskuzích kolem FOREACH bylo sem tam docela náročné si udržet duševní klid :).
Možnost přiřadit kód triggeru přímo by se mi také líbila - a jelikož je to blízko tomu, co je ve standardu, tak si myslím, že by to prošlo - jen by to někdo musel napsat - to by úplně čajíček nebyl :).
Že by se mohlo změnit chování NEW a OLD proměnných - to jako pravděpodobné nevidím - spíš opravím dokumentaci :) - pokud narazíte na chybu, prosím, zareportujte to. Je dost nepravděpodobné, že by se opravil kód, tak aby ladil s dokumentací :).
Nešlo by to jednoduše napsat tak, že by to byl jen shortcat a vytvořilo by to klasickou uloženou proceduru (s nějakým speciálním názvem vygenerovaným z názvu tabulky a triggeru) a k trigeru by se poznamenalo, že se s ním má ta procedura i smazat. Se zdrojáky postgresu jsem si ještě moc nehrál, ale nepřipadá mi to tak složitá operace - v podstatě by se změnil jen parser a k trigeru by se poznamenalo, že má při zrušení zrušit danou fci.
Sladění kódu s dokumentací je defakto oprava každého bugu, ne? :-)
Nicméně na todle chování OLD/NEW jsem narazil v několika diskusích a měl jsem za to, že se o tom ví, akorát nebyl čas to opravit. Ale přiznám se, že kompromis s OPT-INEm (ta druhá varianta) se mi líbí čim dál více: bezpečné, konzervativní, smysluplné..... Snad bych i pomohl ji napsat, kdyby mi někdo řekl kam šáhnout. Ale dobře, už dávám pokoj. :-)
To je v pohodě - diskuzí se tříbí názory a navíc třeba Vás to chytne a napíšete do Postgresu :)
Vývoj PostgreSQL má svá pravidla - začíná obhajobou návrhu a to před všemi vývojáři přihlášenými v pg-hackers, přičemž platí pravidlo, že nikdo nesmí být zásadně proti - nefunguje tam demokratické hlasování, kdy platí, to co se líbí většině. Občas je to docela o nervy, ale tím se člověk nejvíc učí - oponují vám jedni z nejlepších kodérů - a když to člověk vydrží a na něčem se dohodne, tak se fakt dost naučí - je to dobrá škola - jak vyjednávání, tak programování.
Zpátky k triggerům - myslím si, že byste automaticky generovanou procedurou narazil (je to můj názor - názor hackers může být jiný). Mezi vývojáři je averze k automaticky generovaným objektům, které mohou žít vlastním životem - a teď si vybavím pouze typ Serial a indexy u primárních klíčů. Jednak musite řešit závislosti, druhak přístupová práva a do třetice místo jedné systémové tabulky musíte editovat minimálně dvě systémové tabulky - teď vše musí fungovat za chodu bez zamykání - uvnitř transakce s možností rollbacku. Jednodušší (architektonicky) nikoliv na kódování je rozšíření systémové tabulky s def. triggerů o možnost uložení zdrojáku triggeru a specifikaci PL jazyka. Tím odpadne veškerá komplexnost z evidence závislosti.
Jen tak pro informaci přihodím pár odkazů http://wiki.postgresql.org/wiki/Submitting_a_Patch https://commitfest.postgresql.org/action/commitfest_view?id=10
No mě by to i chytlo, jen ten čas... :-) Uvidím, jestli nějakej našetřim...
A ještě mě napadly dvě fíčury, ale bojím se, že jsou ještě nereálnější, než to, co jsem zatím doteď zmiňoval, ale třeba pro inspiraci.
1) podpora objektů. V jakékoli možné míře, hodně by bodla by i jednoduchá možnost vytvořit metody asociované s rowtype, které by se volali syntaxí promena.metoda().
Velmi by to zpřehlednilo kód, protože by se mohli jména metod o dost zkrátit a zároveň to vede ke strukturovanějšímu a tedy čitelnějšímu kódu.
Todle by nemuselo být těžké na implementaci, anžto je to víceméně na úrovni syntaktické analýzy, střeva by to měnilo minimálně.
2) Předávání dotazů jako parametru. Nevím, jestli je to správný název,
nejblíž je to asi kursorům, ale přes ně jde pouze iterovat. Já bych ocenil nástroj, který by mi umožnil v jedný proceduře vybrat hromadu idček, dát je jako parametr do jiné procedury a tam moci dělat dotaz WHERE něco IN (hromada idček). a bylo to přitom efektivní. Většinou používám pole, ale to se hodí jen pro malé množiny hodnot, jinak předpokládám, že je to dost drahé, stejně tak dočasná tabulka.
V podstatě by asi stačilo, kdyby šlo např. použít neotevřený cursor v dotazu jako tabulku. Ale bojím se, že todle je asi nereálné....?
ad 1. Je to pár let, kdy jsem se o něco takového pokoušel - a nedopadlo to. Možná byste se divil, jak málo uživatelů mimo akademickou sféru tohle zajímá - chybí opora ve standardu a také nelze použít tečkovou notaci - neb se tluče v parseru - schema.funkce(). Navíc to lze obejít zápisem metoda(promenna, ...) a díky přetížení to bude fungovat úplně stejně.
ad 2. se v PostgreSQL řeší přes kurzory nebo pole. Kurzor lze využít i pro jiné operace - existuje DELETE a UPDATE podporují klauzuli WHERE OF. Jinak pole jsou zásadně lacinější než dočasné tabulky - v podstatě jsou docela laciné - pole o 1000 int si vezme cca 4KB což nic není - není problém pracovat s poly o stovkách tisíc položek - při opravdu velkých polích se musí používat pár triků nebo použít novou konstrukci FOREACH která je v 9.1. Pouze pokud byste potreboval přenést více než statisíce id, tak pak je výhodnější kurzor - pro stovky, tisíce id jsou pole v pohodě - jinak u kurzorů se používá typ refkursor, což je fakticky jen název kurzoru zapsaný jako klasický string.
p.s. ad2 ze své zkušenosti požadavek na 2 dost často znamená, že se vymýšlejí vzdušné zámky. V uložených procedurách je potřeba programovat 'zemitěji' - co nejjednodušeji - je nepraktické vymýšlet univerzální procedury. Pokud by se kód v procedurách měl extrémně opakovat, pak lepším řešením jsou generátory procedur - což jste nakousl, např. šablonovací systém případně něco jiného.
mozna nerozumim, ale existuji triggerove fce, ty nejsou nutne vazane na tabulku, jinak ono stejne vetsina lidi co pise v db neco slozitejsiho zjisti ze triggery jako takovy jsou cesta do pekla a nelze je je brat jako klicovy element ve vyvoji, cistejsi a spravnejsi je pouzit stored procedures na ruzne akce..
A není nejjednodušší si nadefinovat vlastní proměnnou s rowtype dané tabulky, na začátku si to nastavit jak potřebuji (na NULL v případě insertu, na OLD jinak), a pak porovnávat už tu proměnnou. Něco jako toto
create table tx (i int, j int);
create or replace function tx_trigger() returns trigger as $$
declare
v_old tx%rowtype;
begin
IF (TG_OP = 'INSERT') THEN
v_old := NULL;
ELSE
v_old := OLD;
END IF;
IF (v_old.i = NEW.i) THEN
raise notice 'equals';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
create trigger tx_trig before insert or update on tx for each row execute procedure tx_trigger();
To samozřejmě jde, ale dělat to na začátku každého jednoúčelové triggeru je hrozná opičárna :-).Kdyby se všechno dělalo takhle tak to by bylo jednodušší dělat všechnu logiku na straně klienta.
I když musím říct, že čím dál tím víc přemýšlím nad vytvořením nějakého šablonovacího jazyka pro postgresql. Prostě sadu maker, který by překládali jednoduchý konstruklce na ukecanost PLPGSQL :-).
Vlastní šablonovací jazyk není úplně běžná záležitost, ale není to zas úplně neobvyklé. Např. zdrojáky PostGISu se prohánějí sedem před tím než se pošlou do pg. Vím o několika projektech, kde mají generátor tabulek - který jim vegeneruje CREATE TABLE a všechny potřebné triggery a funkce. No a já jsem dělal na jednom projektu, kde byl v PL/pgSQL napsán jazyk, který podporoval objekty, metody a workflow, a který se překládal do PL/pgSQL.
SERIALIZABLE úroveň je už ze své podstaty pomalejší, navíc musíte aplikaci připravit na to že transakce mohou běžně padat protože nejsou serializovatelné (tj. aplikace by je měla být schopna zopakovat).
Primárně si musíte rozmyslet jestli anomálie které se standardním READ COMMITED módu mohou objevit (non-repeatable reads a phantom reads) jsou pro vaši aplikaci problém nebo ne (a pokud to jde tak to rovnou psát tak aby nebyly). Pokud vám stačí READ COMMITED, není důvod nastavovat SERIALIZABLE.
Jiná otázka je optimalita implementace, nicméně neočekávám že by tam byly nějaké zásadní boty které by to řádově zpomalovaly.
A nezapomínejme že se to dá nastavovat pro jednotlivé transakce, takže aplikace může běžet jako READ COMMITED ale pár vybraných procesů může běžet jako SERIALIZABLE.
Těžko říct - PostgreSQL je první databází, kde se produkčně nasazuje SSI řešení - takže zkušenosti z ostatních db nejsou relevantní. V každém případě bude SERIALIZABLE o hodně náročnější než býval v předchozích verzích - přičemž ale v předchozích verzích měla tato úroveň téměř nulovou režii v porovnání s ostatními db. SSI ještě nikdo nezkoušel - zveřejněna byla teprve v roce 2009.
Mne najviac chyba nieco ako AS OF TIMESTAMP z Oracle. Cize vidiet stav tabulky v minulosti, pricom je mozne pozerat stav tabulky pred par hodinami, vo vynimocnych pripadoch aj pred par dnami.
Pokial viem tak format ulozenia tabuliek a spracovanie transakcii v Postres by takuto funkcionalitu umoznili.