<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="cs">
	<id>http://postgres.cz/index.php?action=history&amp;feed=atom&amp;title=PL%2FpgSQL_efektivn%C4%9B</id>
	<title>PL/pgSQL efektivně - Historie editací</title>
	<link rel="self" type="application/atom+xml" href="http://postgres.cz/index.php?action=history&amp;feed=atom&amp;title=PL%2FpgSQL_efektivn%C4%9B"/>
	<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=PL/pgSQL_efektivn%C4%9B&amp;action=history"/>
	<updated>2026-05-12T22:42:43Z</updated>
	<subtitle>Historie editací této stránky</subtitle>
	<generator>MediaWiki 1.43.3</generator>
	<entry>
		<id>http://postgres.cz/index.php?title=PL/pgSQL_efektivn%C4%9B&amp;diff=505&amp;oldid=prev</id>
		<title>imported&gt;Pavel v 27. 8. 2013, 04:14</title>
		<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=PL/pgSQL_efektivn%C4%9B&amp;diff=505&amp;oldid=prev"/>
		<updated>2013-08-27T04:14:44Z</updated>

		<summary type="html">&lt;p&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Nová stránka&lt;/b&gt;&lt;/p&gt;&lt;div&gt;[[Category:Články]]&lt;br /&gt;
&amp;#039;&amp;#039;Autor: Pavel Stěhule, srpen 2011&amp;#039;&amp;#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;blockquote style=&amp;quot;background-color: #eeeee0; border: solid thin lightgrey; padding:15px; &amp;quot;&amp;gt;&lt;br /&gt;
Poznámka autora: Srpen 2013 - Počínaje PostgreSQL 9.2 by měly být nejčastější problémy způsobené slepou optimalizací (generickým plánem) částečně odstraněné. Minimálně prvních 5 exekucí je provedeno s adhoc plánem - a jedině v případě, že průměrná cena plánu adhoc plánů je vyšší než cena generického plánu, tak se začne používat generický plán. Průměr se neustále kontroluje, pokud převýší cenu generického plánu, tak už se používá nevratně generický plán (v rámci session). Ve výsledku se ovšem pro programátora nic moc nemění - tam, kde bylo nutné dříve použít dynamické SQL, zůstává nutnost jeho použítí i dnes (pokud si nejsme jisti, že SQL příkaz nebude vykonán méně než 5x za dobu existence session).&lt;br /&gt;
&amp;lt;/blockquote&amp;gt;&lt;br /&gt;
&lt;br /&gt;
V následujícím článku bych chtěl zkompletovat [http://www.root.cz/clanky/jemny-uvod-do-plpgsql sérii článků], které jsem o &lt;br /&gt;
PL/pgSQL pro root napsal. Kvůli PL/pgSQL jsem si vybral PostgreSQL a vlastně&lt;br /&gt;
kvůli PL/pgSQL jsem se stal jedním z vývojářů PostgreSQL - při práci na jednom&lt;br /&gt;
projektu mne PL/pgSQL drobet štval, tak jsem napsal patch, který byl přijat&lt;br /&gt;
a v další verzi PL/pgSQL se objevil příkaz &amp;lt;tt&amp;gt;CONTINUE&amp;lt;/tt&amp;gt; - což byla pro mne bomba&lt;br /&gt;
(člověku stačí ke štěstí málo :)).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;[http://www.postgresql.org/docs/9.0/interactive/plpgsql.html PL/pgSQL]&amp;lt;/i&amp;gt; je docela věrná kopie starších verzí programovacího jazyka &amp;lt;i&amp;gt;PL/SQL&amp;lt;/i&amp;gt;,&lt;br /&gt;
což je jazyk pro vývoj uložených procedur fy. Oracle. [http://en.wikipedia.org/wiki/PL/SQL PL/SQL] je fakticky&lt;br /&gt;
hodně osekaná &amp;lt;i&amp;gt;[http://en.wikipedia.org/wiki/Ada_%28programming_language%29 ADA]&amp;lt;/i&amp;gt; rozšířená o SQL. Syntaxe PL/pgSQL a PL/SQL jsou si hodně&lt;br /&gt;
podobné - zásadně se liší implementace. PL/pgSQL je velice jednoduchý [http://en.wikipedia.org/wiki/Interpreter_%28computer_software%29#Abstract_Syntax_Tree_interpreters interpret abstraktního syntaktického stromu],&lt;br /&gt;
který běží ve stejném procesu, v kterém probíhá zpracování SQL příkazu. Procedury&lt;br /&gt;
PL/SQL běží ve svém vlastním procesu, přičemž PL/SQL je překládáno do strojového&lt;br /&gt;
kódu. Každý přístup má své výhody a nevýhody - a má samozřejmě i jinou motivaci&lt;br /&gt;
a jiné historické pozadí. PL/pgSQL je úzce integrován s PostgreSQL - má zanedbatelnou &lt;br /&gt;
režii, pro přístup k datům není nutné používat interprocess komunikaci, snadno se &lt;br /&gt;
udržuje, snadno se rozšiřuje, snadno se učí. Sílou PL/SQL je jeho bohatost a fakt, že je překládán&lt;br /&gt;
do strojového kódu (resp. používá interpret tzv M-Code (interpret se nazývá PVM), který lze případně převést do strojového kódu). Přeci jen PL/SQL je méně osekanější ADA než PL/pgSQL a i &lt;br /&gt;
díky tomu je úplnější, univerzálnější než PL/pgSQL a také náročnější na naučení. Jinak  oba jazyky jsou prověřené časem &lt;br /&gt;
- v PL/SQL se napsaly milióny řádků kódu a myslím si, že i v PL/pgSQL jsou nepochybně napsány stovky tisíc řádků. &lt;br /&gt;
&lt;br /&gt;
PL/pgSQL je založen na jednoduchém interpretu - tak jednoduchém, že neimplementuje&lt;br /&gt;
ani základní aritmetické a logické operace - každý výraz se převádí na &amp;lt;tt&amp;gt;SELECT&amp;lt;/tt&amp;gt;,&lt;br /&gt;
který zpracovává executor. PL/pgSQL obsahuje jen implementaci proměnných&lt;br /&gt;
a řídících konstrukcí (&amp;lt;tt&amp;gt;IF&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;LOOP&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;RETURN&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;:=&amp;lt;/tt&amp;gt;, ..). &lt;br /&gt;
Naštěstí jednodušší SELECTy,&lt;br /&gt;
které odpovídají výrazům - tj neobsahují odkaz na tabulky, dokáže interpret&lt;br /&gt;
PL/pgSQL spouštět řádově efektivněji než typické dotazy - tj dotazy&lt;br /&gt;
do tabulek. Z této implementace vychází i efektivní použití PL/pgSQL. Je to&lt;br /&gt;
bezvadné lepidlo pro SQL příkazy. Na druhou stranu, PL/pgSQL se vůbec nehodí&lt;br /&gt;
pro numerické úlohy, které vyžadují velký počet aritmetických operací. PL/pgSQL&lt;br /&gt;
se nehodí pro náročnější operace, kde se intenzivně modifikují řetězce nebo pole.&lt;br /&gt;
Každá úprava řetězce nebo pole znamená vytvoření upravené kopie původních dat, kterou&lt;br /&gt;
jsou původní data nahrazena. Tento přístup je v případě&lt;br /&gt;
většího objemu nebo většího počtu operací neefektivní. &lt;br /&gt;
&lt;br /&gt;
V PL/pgSQL se setkávají dva programovací jazyky - dva interpretované programovací&lt;br /&gt;
jazyky - PL/pgSQL a SQL. Při prvním použití funkce (v rámci session) se kód PL/pgSQL &lt;br /&gt;
převede do syntaktického stromu ([http://en.wikipedia.org/wiki/Abstract_syntax_tree Abstract Syntax Tree]), při prvním použití SQL příkazu se generuje prováděcí plán SQL příkazu. Parser PL/pgSQL (stejně jako PostgreSQL) je postavený nad [http://en.wikipedia.org/wiki/GNU_bison GNU Bisonem]. Získaný syntaktický strom a vytvořené prováděcí plány&lt;br /&gt;
se používají opakovaně dokud nedojde ke změně kódu funkce nebo k ukončení session.&lt;br /&gt;
&lt;br /&gt;
Přeložený kód lze zpětně zrekonstruovat a zobrazit. K tomu slouží přepínač&lt;br /&gt;
&amp;lt;tt&amp;gt;#option dump&amp;lt;/tt&amp;gt; (výsledek najdeme v logu Postgresu):&lt;br /&gt;
&lt;br /&gt;
Zaregistruji funkci &amp;lt;i&amp;gt;iifn3&amp;lt;/i&amp;gt; spuštěním příkazu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;[pavel@nemesis ~]$ psql.5491 postgres&lt;br /&gt;
psql.5491 (9.1devel)&lt;br /&gt;
Type &amp;quot;help&amp;quot; for help.&lt;br /&gt;
&lt;br /&gt;
postgres=# CREATE OR REPLACE FUNCTION public.iifn3(boolean, integer, integer)&lt;br /&gt;
 RETURNS integer&lt;br /&gt;
 LANGUAGE plpgsql&lt;br /&gt;
 IMMUTABLE STRICT&lt;br /&gt;
AS $function$#option dump&lt;br /&gt;
BEGIN&lt;br /&gt;
  IF $1 THEN&lt;br /&gt;
    RETURN $2;&lt;br /&gt;
  ELSE&lt;br /&gt;
    RETURN $3;&lt;br /&gt;
  END IF;&lt;br /&gt;
END;&lt;br /&gt;
$function$;&lt;br /&gt;
&lt;br /&gt;
CREATE FUNCTION&lt;br /&gt;
&lt;br /&gt;
[root@nemesis pavel]# tail -n 20 /usr/local/pgsql91/data/serverlog &lt;br /&gt;
&lt;br /&gt;
Execution tree of successfully compiled PL/pgSQL function iifn3:&lt;br /&gt;
&lt;br /&gt;
Function&amp;#039;s data area:&lt;br /&gt;
    entry 0: VAR $1               type bool (typoid 16) atttypmod -1&lt;br /&gt;
    entry 1: VAR $2               type int4 (typoid 23) atttypmod -1&lt;br /&gt;
    entry 2: VAR $3               type int4 (typoid 23) atttypmod -1&lt;br /&gt;
    entry 3: VAR found            type bool (typoid 16) atttypmod -1&lt;br /&gt;
&lt;br /&gt;
Function&amp;#039;s statements:&lt;br /&gt;
  2:BLOCK &amp;lt;&amp;lt;*unnamed*&amp;gt;&amp;gt;&lt;br /&gt;
  3:  IF &amp;#039;SELECT $1&amp;#039; THEN&lt;br /&gt;
  4:    RETURN &amp;#039;SELECT $2&amp;#039;&lt;br /&gt;
      ELSE&lt;br /&gt;
  6:    RETURN &amp;#039;SELECT $3&amp;#039;&lt;br /&gt;
      ENDIF&lt;br /&gt;
    END -- *unnamed*&lt;br /&gt;
&lt;br /&gt;
End of execution tree of function iifn3&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[http://www.root.cz/clanky/cteni-provadecich-planu-v-postgresql/ Prováděcí plány SQL příkazů] se generují podle potřeby - před &amp;lt;i&amp;gt;prvním&amp;lt;/i&amp;gt; vyhodnocením&lt;br /&gt;
SQL příkazu. Generování prováděcích plánů je výpočetně náročná úloha, a díky&lt;br /&gt;
výše popsanému chování můžeme ušetřit milisekundy tím, že negenerujeme plány&lt;br /&gt;
pro dotazy, které nebudou prováděny. Na druhou stranu pouze generování prováděcího&lt;br /&gt;
plánu provede skutečně důslednou kontrolu SQL příkazu (zda-li sedí názvy tabulek,&lt;br /&gt;
názvy sloupců), a pokud ke generování plánu nedochází, tak si nemůžeme být jistí,&lt;br /&gt;
zda-li jsou SQL příkazy uvnitř funkce správně zapsány. Pro důslednější kontrolu &lt;br /&gt;
jsem napsal rozšíření (doplněk) pro PostreSQL - modul &amp;lt;i&amp;gt;[http://groups.google.com/group/postgresql-cz/browse_thread/thread/6372b393ced4d2be?hl=cs plpgsql_lint]&amp;lt;/i&amp;gt;. Ten kromě&lt;br /&gt;
jiného si vynutí generování plánů pro všechny &amp;quot;embeded&amp;quot; SQL příkazy uvnitř funkce&lt;br /&gt;
při jejím prvním spuštění (výše zmíněný modul neinstalujte na produkční servery,&lt;br /&gt;
zpomaluje první spuštění funkce).&lt;br /&gt;
&lt;br /&gt;
Výhodou PL/pgSQL je jeho integrace s PostgreSQL. Proměnné v tomto jazyce jsou&lt;br /&gt;
datově kompatibilní s [[C_a_PostgreSQL_-_intern%C3%AD_mechanismy|interními formáty]] Postgresu. Odpadá nutnost konverzí.&lt;br /&gt;
Výhodou PL/pgSQL je paměťová nenáročnost interpretu a relativní rychlost spouštění &lt;br /&gt;
funkcí. V řadě případů může zavolání funkce v PL/pgSQL vyžadovat méně času&lt;br /&gt;
než volání funkce v [http://www.postgresql.org/docs/9.0/interactive/plperl.html PL/Perl] nebo [http://www.postgresql.org/docs/9.0/interactive/plpython.html PL/Python]. Vždy záleží na vzájemných proporcích&lt;br /&gt;
faktorů, které ovlivňují dobu provádění funkce. &lt;br /&gt;
&lt;br /&gt;
Nejhorší chybou je nevhodné použití PL/pgSQL. Kromě PL/pgSQL můžeme použít i jazyk&lt;br /&gt;
SQL. Pro jednodušší funkce v jazyce SQL umí optimalizátor použít techniku, která&lt;br /&gt;
je podobná tzv &amp;lt;i&amp;gt;[http://en.wikipedia.org/wiki/Inlining inliningu]&amp;lt;/i&amp;gt; - tělo funkce se přímo zkopíruje do SQL příkazu, který&lt;br /&gt;
používá funkci. Tím naprosto odpadá režie spojená s voláním funkce. Pro ilustraci&lt;br /&gt;
použiji jednoduchou funkci &amp;lt;i&amp;gt;iifn&amp;lt;/i&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;postgres=# \sf iifn1&lt;br /&gt;
CREATE OR REPLACE FUNCTION public.iifn1(boolean, integer, integer)&lt;br /&gt;
 RETURNS integer&lt;br /&gt;
 LANGUAGE sql&lt;br /&gt;
AS $function$&lt;br /&gt;
SELECT CASE WHEN $1 THEN $2 ELSE $3 END;&lt;br /&gt;
$function$&lt;br /&gt;
&lt;br /&gt;
postgres=# \sf iifn2&lt;br /&gt;
CREATE OR REPLACE FUNCTION public.iifn2(boolean, integer, integer)&lt;br /&gt;
 RETURNS integer&lt;br /&gt;
 LANGUAGE plpgsql&lt;br /&gt;
 IMMUTABLE STRICT&lt;br /&gt;
AS $function$&lt;br /&gt;
BEGIN&lt;br /&gt;
  RETURN CASE WHEN $1 THEN $2 ELSE $3 END;&lt;br /&gt;
END;&lt;br /&gt;
$function$&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
První verze funkce je psaná v sql, druhá v PL/pgSQL. Obě funkce jsou jinak&lt;br /&gt;
téměř identické.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;postgres=# select sum(iifn1((random()::int)::bool, 1, 0)) &lt;br /&gt;
              from generate_series(1,100000);&lt;br /&gt;
  sum  &lt;br /&gt;
-------&lt;br /&gt;
 49904&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
Time: 139.824 ms&lt;br /&gt;
&lt;br /&gt;
postgres=# select sum(iifn2((random()::int)::bool, 1, 0)) &lt;br /&gt;
              from generate_series(1,100000);&lt;br /&gt;
  sum  &lt;br /&gt;
-------&lt;br /&gt;
 50030&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
Time: 581.466 ms&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pro větší názornost ukáži prováděcí plány obou dotazů:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
postgres=# explain verbose select sum(iifn1((random()::int)::bool, 1, 0)) &lt;br /&gt;
              from generate_series(1,100000);&lt;br /&gt;
                                       QUERY PLAN                                        &lt;br /&gt;
-----------------------------------------------------------------------------------------&lt;br /&gt;
 Aggregate  (cost=12.50..12.52 rows=1 width=0)&lt;br /&gt;
   Output: sum(CASE WHEN ((random())::integer)::boolean THEN 1 ELSE 0 END)&lt;br /&gt;
   -&amp;gt;  Function Scan on pg_catalog.generate_series  (cost=0.00..10.00 rows=1000 width=0)&lt;br /&gt;
         Output: generate_series&lt;br /&gt;
         Function Call: generate_series(1, 100000)&lt;br /&gt;
(5 rows)&lt;br /&gt;
&lt;br /&gt;
postgres=# explain verbose select sum(iifn2((random()::int)::bool, 1, 0)) &lt;br /&gt;
              from generate_series(1,100000);&lt;br /&gt;
                                       QUERY PLAN                                        &lt;br /&gt;
-----------------------------------------------------------------------------------------&lt;br /&gt;
 Aggregate  (cost=12.50..12.77 rows=1 width=0)&lt;br /&gt;
   Output: sum(iifn2(((random())::integer)::boolean, 1, 0))&lt;br /&gt;
   -&amp;gt;  Function Scan on pg_catalog.generate_series  (cost=0.00..10.00 rows=1000 width=0)&lt;br /&gt;
         Output: generate_series&lt;br /&gt;
         Function Call: generate_series(1, 100000)&lt;br /&gt;
(5 rows)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
V prvém případě se tělo funkce vkopírovalo přímo do dotazu. &lt;br /&gt;
&lt;br /&gt;
Díky inliningu je funkce napsaná v SQL téměř 4x rychlejší. V situaci, kdy&lt;br /&gt;
voláme funkci pro každý řádek výsledku dotazu musíme brát v potaz rychlost&lt;br /&gt;
provádění funkce. Mám tu kód, který je 4x pomalejší, a ten je ještě navržen&lt;br /&gt;
maximálně efektivně - obsahuje pouze jediný výraz. Dovedu si dobře představit,&lt;br /&gt;
že programátor, který nezná dobře PL/pgSQL napíše &amp;quot;C&amp;quot; style kód,:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;postgres=# \sf iifn3&lt;br /&gt;
CREATE OR REPLACE FUNCTION public.iifn3(boolean, integer, integer)&lt;br /&gt;
 RETURNS integer&lt;br /&gt;
 LANGUAGE plpgsql&lt;br /&gt;
 IMMUTABLE STRICT&lt;br /&gt;
AS $function$&lt;br /&gt;
BEGIN&lt;br /&gt;
  IF $1 THEN&lt;br /&gt;
    RETURN $2;&lt;br /&gt;
  ELSE&lt;br /&gt;
    RETURN $3;&lt;br /&gt;
  END IF;&lt;br /&gt;
END;&lt;br /&gt;
$function$&lt;br /&gt;
&lt;br /&gt;
postgres=# select sum(iifn3((random()::int)::bool, 1, 0)) &lt;br /&gt;
              from generate_series(1,100000);&lt;br /&gt;
  sum  &lt;br /&gt;
-------&lt;br /&gt;
 50151&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
Time: 630.804 ms&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
případně ještě horší variantu:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;postgres=# \sf iifn4&lt;br /&gt;
CREATE OR REPLACE FUNCTION public.iifn4(boolean, integer, integer)&lt;br /&gt;
 RETURNS integer&lt;br /&gt;
 LANGUAGE plpgsql&lt;br /&gt;
 IMMUTABLE STRICT&lt;br /&gt;
AS $function$&lt;br /&gt;
DECLARE prom int;&lt;br /&gt;
BEGIN&lt;br /&gt;
  IF $1 THEN&lt;br /&gt;
    prom := $2;&lt;br /&gt;
  ELSE&lt;br /&gt;
    prom := $3;&lt;br /&gt;
  END IF;&lt;br /&gt;
  RETURN prom;&lt;br /&gt;
END;&lt;br /&gt;
$function$&lt;br /&gt;
&lt;br /&gt;
postgres=# select sum(iifn4((random()::int)::bool, 1, 0)) &lt;br /&gt;
              from generate_series(1,100000);&lt;br /&gt;
  sum  &lt;br /&gt;
-------&lt;br /&gt;
 49728&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
Time: 668.183 ms&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Přičemž z pohledu respektování obecných doporučení pro návrh kódu je kód &lt;br /&gt;
použitý ve funkci &amp;lt;i&amp;gt;iifn4&amp;lt;/i&amp;gt; perfektní - ale je nejpomalejší, protože obsahuje &lt;br /&gt;
největší počet výrazů. Zas tak pomalé to není - 100 tis volání netrvá ani 1 sec&lt;br /&gt;
na mém obstarožním Prestigio Nobile 156 (i jako nové, to bylo ořezávátko). Nicméně můžete napsat kód, který bude&lt;br /&gt;
4-5 rychlejší, pokud se budete držet zásady, že &amp;lt;i&amp;gt;jednoduché funkce se navrhují&lt;br /&gt;
v jazyce sql&amp;lt;/i&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Připravil jsem ještě jeden příklad demonstrující vliv počtu výrazů uvnitř funkce&lt;br /&gt;
na rychlost funkce. Napsal jsem funkci, jejíchž výsledkem je spojení dvou řeězců&lt;br /&gt;
oddělených mezerou. Pokud jeden z řetězců je NULL, pak výsledkem je druhý řetězec.&lt;br /&gt;
Pokud oba řetězce jsou NULL, pak výsledkem je NULL:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CREATE OR REPLACE FUNCTION public.join_str1(text, text)&lt;br /&gt;
 RETURNS text&lt;br /&gt;
 LANGUAGE plpgsql&lt;br /&gt;
 IMMUTABLE&lt;br /&gt;
AS $function$&lt;br /&gt;
DECLARE aux text;&lt;br /&gt;
BEGIN&lt;br /&gt;
  IF $1 IS NOT NULL THEN&lt;br /&gt;
    aux := $1;&lt;br /&gt;
  END IF;&lt;br /&gt;
  IF $2 IS NOT NULL THEN&lt;br /&gt;
    IF aux IS NOT NULL THEN&lt;br /&gt;
      aux := aux || &amp;#039; &amp;#039;;&lt;br /&gt;
    ELSE&lt;br /&gt;
      aux := &amp;#039;&amp;#039;;&lt;br /&gt;
    END IF;&lt;br /&gt;
    aux := aux || $2;&lt;br /&gt;
  END IF;&lt;br /&gt;
  RETURN aux;&lt;br /&gt;
END;&lt;br /&gt;
$function$&lt;br /&gt;
&lt;br /&gt;
postgres=# \sf join_str2 &lt;br /&gt;
CREATE OR REPLACE FUNCTION public.join_str2(text, text)&lt;br /&gt;
 RETURNS text&lt;br /&gt;
 LANGUAGE plpgsql&lt;br /&gt;
 IMMUTABLE&lt;br /&gt;
AS $function$&lt;br /&gt;
DECLARE aux text;&lt;br /&gt;
BEGIN&lt;br /&gt;
  IF $1 IS NOT NULL AND $2 IS NOT NULL THEN&lt;br /&gt;
    RETURN $1 || &amp;#039; &amp;#039; || $2;&lt;br /&gt;
  ELSEIF $1 IS NOT NULL AND $2 IS NULL THEN&lt;br /&gt;
    RETURN $1;&lt;br /&gt;
  ELSEIF $1 IS NULL AND $2 IS NOT NULL THEN&lt;br /&gt;
    RETURN $2;&lt;br /&gt;
  ELSE&lt;br /&gt;
    RETURN NULL;&lt;br /&gt;
  END IF;&lt;br /&gt;
END;&lt;br /&gt;
$function$&lt;br /&gt;
&lt;br /&gt;
postgres=# \ef join_str2 &lt;br /&gt;
postgres-# ;&lt;br /&gt;
CREATE FUNCTION&lt;br /&gt;
postgres=# \sf join_str3&lt;br /&gt;
CREATE OR REPLACE FUNCTION public.join_str3(text, text)&lt;br /&gt;
 RETURNS text&lt;br /&gt;
 LANGUAGE plpgsql&lt;br /&gt;
 IMMUTABLE&lt;br /&gt;
AS $function$&lt;br /&gt;
DECLARE aux text;&lt;br /&gt;
BEGIN&lt;br /&gt;
  RETURN COALESCE($1 || &amp;#039; &amp;#039;|| $2, $1, $2);&lt;br /&gt;
END;&lt;br /&gt;
$function$&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Funkce &amp;lt;i&amp;gt;join_str1&amp;lt;/i&amp;gt; je nejhorší možná varianta, &amp;lt;i&amp;gt;join_str3&amp;lt;/i&amp;gt; nejlepší,&lt;br /&gt;
&amp;lt;i&amp;gt;join_str2&amp;lt;/i&amp;gt; je někde mezi:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;postgres=# select count(join_str1(case when random() &amp;gt; 0.2 then &amp;#039;aaa&amp;#039; end, &lt;br /&gt;
                                  case when random() &amp;gt; 0.2 then &amp;#039;bbb&amp;#039; end)) &lt;br /&gt;
              from generate_series(1,100000);&lt;br /&gt;
 count &lt;br /&gt;
-------&lt;br /&gt;
 95950&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
Time: 1006.042 ms&lt;br /&gt;
&lt;br /&gt;
postgres=# select count(join_str2(case when random() &amp;gt; 0.2 then &amp;#039;aaa&amp;#039; end, &lt;br /&gt;
                                  case when random() &amp;gt; 0.2 then &amp;#039;bbb&amp;#039; end)) &lt;br /&gt;
              from generate_series(1,100000);&lt;br /&gt;
 count &lt;br /&gt;
-------&lt;br /&gt;
 96027&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
Time: 728.356 ms&lt;br /&gt;
&lt;br /&gt;
postgres=# select count(join_str3(case when random() &amp;gt; 0.2 then &amp;#039;aaa&amp;#039; end, &lt;br /&gt;
                                  case when random() &amp;gt; 0.2 then &amp;#039;bbb&amp;#039; end)) &lt;br /&gt;
              from generate_series(1,100000);&lt;br /&gt;
 count &lt;br /&gt;
-------&lt;br /&gt;
 95884&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
Time: 618.247 ms&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Po převedení funkce &amp;lt;i&amp;gt;join_str3&amp;lt;/i&amp;gt; do sql (což je další krok při optimalizaci) ovšem zjistíme, že test trvá&lt;br /&gt;
delší dobu. Explain nám prozradí, že nedošlo k inliningu (z důvodu použití&lt;br /&gt;
volatile funkce &amp;lt;i&amp;gt;random&amp;lt;/i&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CREATE OR REPLACE FUNCTION public.join_str4(text, text)&lt;br /&gt;
 RETURNS text&lt;br /&gt;
 LANGUAGE sql&lt;br /&gt;
 IMMUTABLE&lt;br /&gt;
AS $function$&lt;br /&gt;
  SELECT COALESCE($1 || &amp;#039; &amp;#039;|| $2, $1, $2);&lt;br /&gt;
$function$&lt;br /&gt;
&lt;br /&gt;
postgres=# select count(join_str4(case when random() &amp;gt; 0.2 then &amp;#039;aaa&amp;#039; end, &lt;br /&gt;
                                  case when random() &amp;gt; 0.2 then &amp;#039;bbb&amp;#039; end)) &lt;br /&gt;
              from generate_series(1,100000); count &lt;br /&gt;
-------&lt;br /&gt;
 96018&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
Time: 1300.636 ms&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Je nutné upravit test - použít derivovanou tabulku:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;postgres=# select count(join_str4(a,b)) &lt;br /&gt;
                   from (select case when random() &amp;gt; 0.2 then &amp;#039;aaa&amp;#039; end as a, &lt;br /&gt;
                                case when random() &amp;gt; 0.2 then &amp;#039;bbb&amp;#039; end as b &lt;br /&gt;
                            from generate_series(1,100000)) x;&lt;br /&gt;
 count &lt;br /&gt;
-------&lt;br /&gt;
 95992&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
Time: 225.656 ms&lt;br /&gt;
&lt;br /&gt;
postgres=# explain verbose select count(join_str4(a,b)) &lt;br /&gt;
                   from (select case when random() &amp;gt; 0.2 then &amp;#039;aaa&amp;#039; end as a, &lt;br /&gt;
                                case when random() &amp;gt; 0.2 then &amp;#039;bbb&amp;#039; end as b &lt;br /&gt;
                            from generate_series(1,100000)) x;&lt;br /&gt;
                                                                                                                                                                                            QUERY PLAN                                                                                                                                                                                            &lt;br /&gt;
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------&lt;br /&gt;
 Aggregate  (cost=32.51..32.52 rows=1 width=64)&lt;br /&gt;
   Output: count(COALESCE((((CASE WHEN (random() &amp;gt; 0.2::double precision) THEN &amp;#039;aaa&amp;#039;::text ELSE NULL::text END) || &amp;#039; &amp;#039;::text) || (CASE WHEN (random() &amp;gt; 0.2::double precision) THEN &amp;#039;bbb&amp;#039;::text ELSE NULL::text END)), (CASE WHEN (random() &amp;gt; 0.2::double precision) THEN &amp;#039;aaa&amp;#039;::text ELSE NULL::text END), (CASE WHEN (random() &amp;gt; 0.2::double precision) THEN &amp;#039;bbb&amp;#039;::text ELSE NULL::text END)))&lt;br /&gt;
   -&amp;gt;  Function Scan on pg_catalog.generate_series  (cost=0.00..20.00 rows=1000 width=0)&lt;br /&gt;
         Output: CASE WHEN (random() &amp;gt; 0.2::double precision) THEN &amp;#039;aaa&amp;#039;::text ELSE NULL::text END, CASE WHEN (random() &amp;gt; 0.2::double precision) THEN &amp;#039;bbb&amp;#039;::text ELSE NULL::text END&lt;br /&gt;
         Function Call: generate_series(1, 100000)&lt;br /&gt;
(5 rows)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;i&amp;gt;Volatile&amp;lt;/i&amp;gt; funkcí je naštěstí minimum - pokud je použijeme, tak je vhodné zkontrolovat prováděcí plán.&lt;br /&gt;
Nelze přehlédnout, že bez inliningu je sql funkce cca 2x pomalejší než funkce v PL/pgSQL.&lt;br /&gt;
&lt;br /&gt;
Další zásadní chybou je použití PL/pgSQL místo využití nativní funkce, &lt;br /&gt;
funkcionality v PostgreSQL. Interně PostgreSQL obsahuje &amp;quot;tuny&amp;quot; optimalizovaného&lt;br /&gt;
kódu a je chybou jej nevyužít. Mým oblíbeným příkladem je &amp;lt;i&amp;gt;bublesort&amp;lt;/i&amp;gt;, který&lt;br /&gt;
používám při svých školeních. Bublesort ovšem není určen pro řazení větších&lt;br /&gt;
polí, takže zde budu demonstrovat rozdíl v rychlosti vestavěného &amp;lt;i&amp;gt;quicksortu&amp;lt;/i&amp;gt;&lt;br /&gt;
a quicksortu implementovaného v PL/pgSQL:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;postgres=# \sf quicksort (int, int, int[])&lt;br /&gt;
CREATE OR REPLACE FUNCTION public.quicksort(l integer, r integer, a integer[])&lt;br /&gt;
 RETURNS integer[]&lt;br /&gt;
 LANGUAGE plpgsql&lt;br /&gt;
 IMMUTABLE STRICT&lt;br /&gt;
AS $function$&lt;br /&gt;
DECLARE akt int[] = a;&lt;br /&gt;
  i integer := l; j integer := r; x integer = akt[(l+r) / 2]; &lt;br /&gt;
  w integer;&lt;br /&gt;
BEGIN&lt;br /&gt;
  LOOP&lt;br /&gt;
    WHILE akt[i] &amp;lt; x LOOP i := i + 1; END LOOP;&lt;br /&gt;
    WHILE x &amp;lt; akt[j] loop j := j - 1; END LOOP;&lt;br /&gt;
    IF i &amp;lt;= j THEN&lt;br /&gt;
      w := akt[i];&lt;br /&gt;
      akt[i] := akt[j]; akt[j] := w;&lt;br /&gt;
      i := i + 1; j := j - 1;&lt;br /&gt;
    END IF;&lt;br /&gt;
    EXIT WHEN i &amp;gt; j;&lt;br /&gt;
  END LOOP;&lt;br /&gt;
  IF l &amp;lt; j THEN akt := quicksort(l,j,akt); END IF;&lt;br /&gt;
  IF i &amp;lt; r then akt := quicksort(i,r,akt); END IF;&lt;br /&gt;
  RETURN akt;&lt;br /&gt;
END;&lt;br /&gt;
$function$&lt;br /&gt;
&lt;br /&gt;
postgres=# \sf quicksort(int[])&lt;br /&gt;
CREATE OR REPLACE FUNCTION public.quicksort(integer[])&lt;br /&gt;
 RETURNS integer[]&lt;br /&gt;
 LANGUAGE sql&lt;br /&gt;
AS $function$&lt;br /&gt;
SELECT quicksort(array_lower($1,1), array_upper($1,1), $1);&lt;br /&gt;
$function$&lt;br /&gt;
&lt;br /&gt;
postgres=# select (quicksort(array(select generate_series(10000,1,-1))))[1];&lt;br /&gt;
 quicksort &lt;br /&gt;
-----------&lt;br /&gt;
         1&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
Time: 2712.860 ms&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Seřazení pole s deseti tisící prvky trvá cca 2.5 sec. PostgreSQL, ostatně jako&lt;br /&gt;
jakákoliv jiná databáze umí perfektně řadit - bohužel tabulky, nikoliv pole.&lt;br /&gt;
Naštěstí lze docela jednoduše transformovat pole na tabulku a tabulku na pole.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;postgres=# \sf quicksort (anyarray)&lt;br /&gt;
CREATE OR REPLACE FUNCTION public.quicksort(anyarray)&lt;br /&gt;
 RETURNS anyarray&lt;br /&gt;
 LANGUAGE sql&lt;br /&gt;
AS $function$&lt;br /&gt;
SELECT ARRAY(SELECT unnest($1) ORDER BY 1)&lt;br /&gt;
$function$&lt;br /&gt;
&lt;br /&gt;
postgres=# select (quicksort(array(select generate_series(10000,1,-1))))[1];&lt;br /&gt;
 quicksort &lt;br /&gt;
-----------&lt;br /&gt;
         1&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
Time: 52.980 ms&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Kód je cca 50x rychlejší, a navíc díky jednodušší implementaci mohu použít&lt;br /&gt;
polymorfní typy. Vtip je ve využití interní funkcionality PostgreSQL. Viděl jsem funkce,&lt;br /&gt;
které v PL/pgSQL analyzovaly formát řetězce. Chyba. Přeci mohu použít vestavěné funkce&lt;br /&gt;
pro práci s regulárními výrazy nebo mohu použít PL/Perl.&lt;br /&gt;
&lt;br /&gt;
Následující chyba se může vyskytnout jak v uložených procedurách, tak i na straně&lt;br /&gt;
klienta. Jedná se o tzv &amp;lt;i&amp;gt;ISAM přístup&amp;lt;/i&amp;gt;. Při psaní kódu musíme myslet i na počet&lt;br /&gt;
SQL příkazů, kterými zatěžujeme server. V řadě případů lze nějakým způsobem&lt;br /&gt;
počet SQL příkazů snížit - pozor - není cílem mít několik málo monstrer&lt;br /&gt;
- SQL příkazů o desítkách nebo stovkách řádků.&lt;br /&gt;
&lt;br /&gt;
Na internetu jsem našel příklad funkce, kde programátor uvnitř volal uvnitř funkce&lt;br /&gt;
mazal řádky na základě předaných ídéček.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CREATE OR REPLACE FUNCTION delete_rows(ids int[])&lt;br /&gt;
RETURNS void AS $$&lt;br /&gt;
BEGIN&lt;br /&gt;
  FOR i IN array_lower($1,1)..array_upper($1,1)&lt;br /&gt;
  LOOP&lt;br /&gt;
    DELETE&lt;br /&gt;
       FROM mytable&lt;br /&gt;
      WHERE mytable.id = ids[i];&lt;br /&gt;
  END LOOP;&lt;br /&gt;
END;&lt;br /&gt;
LANGUAGE plpgsql;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pokud seznam ídeček bude malý, tak je výše uvedená funkce ok. Pokud ovšem bude mít &lt;br /&gt;
spíš stovky nebo tisíce hodnot, tak funkce &amp;lt;i&amp;gt;delete_rows&amp;lt;/i&amp;gt; bude serveru podsouvat&lt;br /&gt;
stovky, tisíce příkazů &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;. Díky tomu, že kód poběží ve stejném procesu, že&lt;br /&gt;
se pro &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; opakovaně používá jeden prováděcí plán tak, zpracování &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt; bude rychlejší&lt;br /&gt;
než, kdyby se o totéž pokoušel klient. Ale stále se bude jednat o neefektivní kód.&lt;br /&gt;
Drobnou úpravou můžeme významně snížit zatížení serveru.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CREATE OR REPLACE FUNCTION delete_rows(ids int[])&lt;br /&gt;
RETURNS void AS $$&lt;br /&gt;
BEGIN&lt;br /&gt;
  DELETE&lt;br /&gt;
     FROM mytable&lt;br /&gt;
    WHERE mytable.id = ANY($1);&lt;br /&gt;
END;&lt;br /&gt;
LANGUAGE plpgsql;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
V případě druhé varianty, bez ohledu na velikost vstupního pole, se provede vždy&lt;br /&gt;
přesně jeden příkaz &amp;lt;tt&amp;gt;DELETE&amp;lt;/tt&amp;gt;. Tuto chybu můžeme udělat v libovolném jazyce, v libovolném&lt;br /&gt;
prostředí - pozor na SQL příkazy uvnitř těla cyklu - někdy se jim nevyhneme, ale &lt;br /&gt;
pokud se jim lze vyhnout, tak bychom se jim měli vyhnout.&lt;br /&gt;
&lt;br /&gt;
Rychlost zpracování prováděcího plánu dotazu nezávisí na tom, zda-li byl dotaz &lt;br /&gt;
odeslán z uložené procedury (či zákaznické funkce) nebo z klasické aplikace. Jelikož&lt;br /&gt;
uložené procedury běží na stejném železe jako běží samotná databáze, tak úplně odpadá&lt;br /&gt;
síťová komunikace (překvapivě pořádná brzda i v dnešní době - o co se zrychlily sítě, o to se zvětšil&lt;br /&gt;
počet SQL dotazů a objem přenášených dat). V případě PostgreSQL odpadá i interprocess&lt;br /&gt;
komunikace, která zase pro větší objemy dat může znamenat významnou zátěž - viz&lt;br /&gt;
google a klíčová slova - &amp;lt;i&amp;gt;BULK COLLECT ORACLE&amp;lt;/i&amp;gt;. Pro prováděcí plány všech statických&lt;br /&gt;
SQL dotazů je použitá cache, čímž se také může ušetřit pár milisekund.&lt;br /&gt;
&lt;br /&gt;
Používání cache prováděcích plánů má také své stinné stránky. Jelikož se prováděcí&lt;br /&gt;
plán použije opakovaně s potenciálně hodně odlišnými parametry, optimalizuje se nikoliv &lt;br /&gt;
vůči známým hodnotám parametrů, ale vůči průměrným hodnotám atributů. Pokud bude &lt;br /&gt;
v databázi hodně Nováků, tak přestože se budu dotazovat na Stěhuleho, tak prováděcí&lt;br /&gt;
plán bude optimální pro dotaz na Nováka. Toto chování občas působí výkonnostní&lt;br /&gt;
problémy. Naštěstí je k dispozici nástroj, kterým si můžeme vynutit vytvoření &lt;br /&gt;
prováděcího plánu vůči aktuálním hodnotám parametru dotazu - tímto nástrojem je &lt;br /&gt;
&amp;lt;i&amp;gt;[http://www.postgresql.org/docs/9.0/interactive/plpgsql-statements.html#PLPGSQL-STATEMENTS-EXECUTING-DYN dynamické SQL]&amp;lt;/i&amp;gt;. &lt;br /&gt;
&lt;br /&gt;
O dynamickém SQL se obvykle píše v souvislosti s &amp;lt;i&amp;gt;[http://cs.wikipedia.org/wiki/SQL_injection SQL injection]&amp;lt;/i&amp;gt;. Pozor na to. Pro&lt;br /&gt;
řešení problému popsaném v předchozím odstavci můžeme (díky klauzuli USING) použít&lt;br /&gt;
dynamické SQL bezpečně (pokud řetězec SQL příkazu neskládáme, tak se SQL injection&lt;br /&gt;
nemusíme obávat):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CREATE OR REPLACE FUNCTION foo(jmeno text)&lt;br /&gt;
RETURNS void AS $$&lt;br /&gt;
DECLARE r record;&lt;br /&gt;
BEGIN&lt;br /&gt;
  FOR r IN SELECT *&lt;br /&gt;
              FROM obcane&lt;br /&gt;
             WHERE obcane.jmeno = foo.jmeno&lt;br /&gt;
  LOOP&lt;br /&gt;
    ..&amp;lt;/pre&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
Prováděcí plán pro výše uvedený SELECT se vygeneruje při prvním zpracování &lt;br /&gt;
konstrukce &amp;lt;tt&amp;gt;FOR SELECT&amp;lt;/tt&amp;gt;. V ten okamžik vím a db ví, že proměnná &amp;lt;i&amp;gt;jmeno&amp;lt;/i&amp;gt; obsahuje řetězec&lt;br /&gt;
&amp;quot;Stěhule&amp;quot;. Tato znalost se nezužitkuje, protože je možné, že příště bude proměnná&lt;br /&gt;
&amp;lt;i&amp;gt;jmeno&amp;lt;/i&amp;gt; obsahovat řetězec &amp;quot;Novák&amp;quot;. Pokud bych měl s funkcí &amp;lt;i&amp;gt;foo&amp;lt;/i&amp;gt; výkonnostní problém,&lt;br /&gt;
mohu použít cyklus &amp;lt;i&amp;gt;FOR EXECUTE&amp;lt;/i&amp;gt; (pozor na předčasnou optimalizaci).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CREATE OR REPLACE FUNCTION foo(jmeno text)&lt;br /&gt;
RETURNS void AS $$&lt;br /&gt;
DECLARE r record;&lt;br /&gt;
BEGIN&lt;br /&gt;
  FOR r IN EXECUTE &amp;#039;SELECT *&lt;br /&gt;
                       FROM obcane&lt;br /&gt;
                      WHERE jmeno = $1&amp;#039;&lt;br /&gt;
                   USING jmeno&lt;br /&gt;
  LOOP&lt;br /&gt;
    ..&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;FOR EXECUTE&amp;lt;/tt&amp;gt; nepoužívá cache pro prováděcí plány a tudíž prováděcí plán bude vždy&lt;br /&gt;
odpovídat hodnotám parametrů. Dynamické SQL by se mělo používat pouze tehdy, když&lt;br /&gt;
zjistíte výkonnostní problém. Vím pouze o jedné situaci, kdy dopředu vím, že je &lt;br /&gt;
vhodné použít dynamické SQL. NULL lze použít i jako příznak pro zanedbání některého&lt;br /&gt;
z parametru funkce (je to čistší než použití magických konstant). Kód funkce &amp;lt;i&amp;gt;foo&amp;lt;/i&amp;gt;&lt;br /&gt;
by mohl vypadat následovně:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CREATE OR REPLACE FUNCTION foo(jmeno text)&lt;br /&gt;
RETURNS void AS $$&lt;br /&gt;
DECLARE r record;&lt;br /&gt;
BEGIN&lt;br /&gt;
  IF jmeno IS NULL THEN&lt;br /&gt;
    FOR r IN SELECT *&lt;br /&gt;
                FROM obcane&lt;br /&gt;
    LOOP&lt;br /&gt;
      ..&lt;br /&gt;
    END IF;&lt;br /&gt;
  ELSE&lt;br /&gt;
    FOR r IN EXECUTE &amp;#039;SELECT *&lt;br /&gt;
                       FROM obcane&lt;br /&gt;
                      WHERE jmeno = $1&amp;#039;&lt;br /&gt;
                   USING jmeno&lt;br /&gt;
    LOOP&lt;br /&gt;
      ..&lt;br /&gt;
    END LOOP;&lt;br /&gt;
  END IF;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Je zřejmé, že takový přístup vede k redundantnímu kódu. Lze použít starý trik, pro&lt;br /&gt;
který doporučuji vždy použít dynamické SQL.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CREATE OR REPLACE FUNCTION foo(jmeno text)&lt;br /&gt;
RETURNS void AS $$&lt;br /&gt;
DECLARE r record;&lt;br /&gt;
BEGIN&lt;br /&gt;
  FOR r IN EXECUTE &amp;#039;SELECT *&lt;br /&gt;
                       FROM obcane&lt;br /&gt;
                      WHERE (jmeno = $1 OR $1 IS NULL)&amp;#039;&lt;br /&gt;
             USING jmeno&lt;br /&gt;
  LOOP&lt;br /&gt;
    ..&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pro tento trik není v PostgreSQL žádná optimalizace (která je např. ve Firebirdu).&lt;br /&gt;
Pokud se nepoužívají prepared statements nebo nebo nakešované prováděcí plány, tak&lt;br /&gt;
se nic neděje. Optimalizátor na základě známé hodnoty parametru upraví predikát do&lt;br /&gt;
tvaru: &amp;quot;&amp;lt;tt&amp;gt;jmeno = $1&amp;lt;/tt&amp;gt;&amp;quot; (pokud je $1 not NULL) nebo &amp;quot;&amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt;&amp;quot; (pokud je $1 NULL). PL/pgSQL&lt;br /&gt;
používá cache pro prováděcí plány, a tak se vytvoří plán pro obecnější případ, což je &lt;br /&gt;
zanedbání predikátu a tudíž vždy sekvenční čtení. Dynamické SQL je perfektním řešením:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CREATE OR REPLACE FUNCTION foo(jmeno text, prijmeni text, vek int[])&lt;br /&gt;
RETURNS void AS $$&lt;br /&gt;
DECLARE r record;&lt;br /&gt;
BEGIN&lt;br /&gt;
  FOR r IN EXECUTE &amp;#039;SELECT *&lt;br /&gt;
                       FROM obcane&lt;br /&gt;
                      WHERE (jmeno = $1 OR $1 IS NULL)&lt;br /&gt;
                        AND (prijmeni = $2 OR $2 IS NULL)&lt;br /&gt;
                        AND (vek = ANY($3) OR $3 IS NULL)&amp;#039;&lt;br /&gt;
             USING jmeno, &lt;br /&gt;
                   prijmeni, &lt;br /&gt;
                   vek&lt;br /&gt;
  LOOP&lt;br /&gt;
    ..&amp;lt;/pre&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
PL/pgSQL není komplikovaný jazyk a není ani nijak záludný. Tento článek pokrývá&lt;br /&gt;
velkou většinu výkonnostních problémů, na které můžeme narazit. Základem je zužitkovat&lt;br /&gt;
optimalizovaný kód Postgresu a dát si pozor na pomalé SQL příkazy (užitečným pomocníkem je contrib modul [http://www.postgresql.org/docs/9.0/interactive/auto-explain.html auto_explain]).&lt;/div&gt;</summary>
		<author><name>imported&gt;Pavel</name></author>
	</entry>
</feed>