<?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=Pole_v_PostgreSQL</id>
	<title>Pole v PostgreSQL - Historie editací</title>
	<link rel="self" type="application/atom+xml" href="http://postgres.cz/index.php?action=history&amp;feed=atom&amp;title=Pole_v_PostgreSQL"/>
	<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=Pole_v_PostgreSQL&amp;action=history"/>
	<updated>2026-05-12T23:40:20Z</updated>
	<subtitle>Historie editací této stránky</subtitle>
	<generator>MediaWiki 1.43.3</generator>
	<entry>
		<id>http://postgres.cz/index.php?title=Pole_v_PostgreSQL&amp;diff=463&amp;oldid=prev</id>
		<title>imported&gt;Pavel: /* Pole a dynamické SQL */</title>
		<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=Pole_v_PostgreSQL&amp;diff=463&amp;oldid=prev"/>
		<updated>2009-09-15T14:36:28Z</updated>

		<summary type="html">&lt;p&gt;&lt;span class=&quot;autocomment&quot;&gt;Pole a dynamické SQL&lt;/span&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;
Pole a relační databáze nejdou dost dobře dohromady - alespoň na první pohled (viz první normální forma a požadavek nedělitelnosti ukládaných hodnot). Rozhodně podpora polí v relačních databázích není běžná, a myslím si, že je to možná i dobře. V databázovém systému, který pole podporuje, lze k databázi přistupovat skutečně jako k pouhému úložišti dat. V systémech, které podporují vícerozměrná pole, může být serializace (a zrovna tak deserializace) objektů, když ne triviální, tak jednoduchá. Ovšem riskujeme výkonnostní problémy - SQL databáze jsou optimalizované na jiný (normalizovaný) datový model. A to nemluvím o obtížích, které bychom s tímto datovým modelem měli, pokud bychom chtěli navrhovat ad-hoc SQL dotazy. Je to paradox - ORM systémy pole prakticky nepoužívají. &lt;br /&gt;
&lt;br /&gt;
Výjimkou, která potvrzuje pravidlo o ukládání polí v databázích, jsou časové řady. Minimálně v PostgreSQL jsou pole jediným efektivním prostředkem pro ukládání časových řad (jediným, který je dostupný běžnému uživateli). S výjimkou časových řad to opravdu skoro vypadá tak, že pole a relační databáze nejdou k sobě. Opak je pravdou. Podpora polí je zásadní pro SQL uložené procedury. Obecně - v kterémkoliv procedurálním jazyce se bez polí neobejdeme - a jazyky uložených SQL procedur nejsou výjimkou. Pokud podpora polí chybí (např. T-SQL), tak je to na úkor funkčnosti prostředí - chybějící funkčnost se musí [http://www.sommarskog.se/arrays-in-sql-2005.html všelijak obcházet] - což se zákonitě musí projevit na efektivitě vývojáře, čitelnosti kódu i výkonu aplikace. &lt;br /&gt;
&lt;br /&gt;
Implementace polí v PostgreSQL je poměrně unikátní a to jak v porovnání s ostatními OSS databázemi, tak v porovnání s proprietárními databázovými systémy. S použitím několika málo funkcí můžeme řešit úlohy, které bychom v jiných prostředích řešili pracněji nebo méně efektivně. &lt;br /&gt;
&lt;br /&gt;
==Datový typ pole==&lt;br /&gt;
PostgreSQL podporuje vícerozměrná (tedy i jednorozměrná) pole hodnot skalárních nebo složených typů. V PostgreSQL jsou [http://www.postgresql.org/docs/8.4/interactive/arrays.html pole] dynamická. Proměnná (sloupec) typu pole se deklaruje pomocí dvojice hranatých závorek &amp;quot;[]&amp;quot; zapsaných za libovolný skalární typ. Specifikovat lze i velikost pole, nicméně tato hodnota se později ignoruje:&lt;br /&gt;
&amp;lt;pre&amp;gt;CREATE TABLE test(a varchar[]);&lt;br /&gt;
&lt;br /&gt;
-- starý zapis&lt;br /&gt;
INSERT INTO test VALUES(&amp;#039;{a,b,c}&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
-- novější zápis s konstruktorem pole&lt;br /&gt;
INSERT INTO test VALUES(ARRAY[&amp;#039;a&amp;#039;,&amp;#039;b&amp;#039;,&amp;#039;c&amp;#039;]); &lt;br /&gt;
&lt;br /&gt;
--Pozor - horní index pole se lze zapsat, ale nikam se neuloží a stejně tak se nepoužívá&lt;br /&gt;
postgres=# CREATE TABLE test(a varchar[2]);&lt;br /&gt;
CREATE TABLE&lt;br /&gt;
&lt;br /&gt;
postgres=# INSERT INTO test VALUES(ARRAY[&amp;#039;a&amp;#039;,&amp;#039;b&amp;#039;,&amp;#039;c&amp;#039;]);&lt;br /&gt;
INSERT 0 1&lt;br /&gt;
&lt;br /&gt;
postgres=# \d test&lt;br /&gt;
           Table &amp;quot;public.test&amp;quot;&lt;br /&gt;
 Column |        Type         | Modifiers &lt;br /&gt;
--------+---------------------+-----------&lt;br /&gt;
 a      | character varying[] | &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Pole se indexují od jedné (pokud neurčíme jinak). Prostřednictvím indexů můžeme přistupovat k jednotlivým prvkům pole, případně, pomocí intervalu, k podpoli:&lt;br /&gt;
&amp;lt;pre&amp;gt;postgres=# SELECT * FROM test;&lt;br /&gt;
    a    &lt;br /&gt;
---------&lt;br /&gt;
 {a,b,c}&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT a[1], a[2:3] FROM test;&lt;br /&gt;
 a |   a   &lt;br /&gt;
---+-------&lt;br /&gt;
 a | {b,c}&lt;br /&gt;
(1 row)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Základní funkce pro operace s datovým typem pole==&lt;br /&gt;
PostgreSQL je, jako ostatně každá SQL databáze, silná v operacích nad množinami (tabulkami). Proto je častým trikem převedení pole na tabulku, provedení určité operace, a převod výsledné množiny zpět na pole. K transformaci pole na tabulku slouží funkce &amp;lt;i&amp;gt;unnest&amp;lt;/i&amp;gt;. Agregační funkcí &amp;lt;i&amp;gt;array_agg&amp;lt;/i&amp;gt; získáme pole z (pod)množiny hodnot. &lt;br /&gt;
&lt;br /&gt;
Typickým vstupem je seznam hodnot oddělených vybraným znakem (separátorem - oddělovačem). K operacím nad seznamy hodnot uložených v řetězci můžeme použít funkce &amp;lt;i&amp;gt;string_to_array&amp;lt;/i&amp;gt; (transformuje řetězec na pole) a &amp;lt;i&amp;gt;array_to_string&amp;lt;/i&amp;gt; (generuje řetězec z pole). Pokud na pole převádíme kompletní výsledek dotazu, můžeme použít konstruktor pole z poddotazu - &amp;lt;i&amp;gt;ARRAY&amp;lt;/i&amp;gt;(subselect).&lt;br /&gt;
&amp;lt;pre&amp;gt;postgres=# SELECT unnest(a) FROM test;&lt;br /&gt;
 unnest &lt;br /&gt;
--------&lt;br /&gt;
 a&lt;br /&gt;
 b&lt;br /&gt;
 c&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE jmena(a varchar);&lt;br /&gt;
INSERT INTO jmena VALUES(&amp;#039;Pavel&amp;#039;),(&amp;#039;Petr&amp;#039;),(&amp;#039;Jan&amp;#039;),(&amp;#039;Zbyšek&amp;#039;),(&amp;#039;Bohuslav&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT * FROM jmena;&lt;br /&gt;
    a     &lt;br /&gt;
----------&lt;br /&gt;
 Pavel&lt;br /&gt;
 Petr&lt;br /&gt;
 Jan&lt;br /&gt;
 Zbyšek&lt;br /&gt;
 Bohuslav&lt;br /&gt;
(5 rows)&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT array_agg(a) FROM jmena;&lt;br /&gt;
            array_agg             &lt;br /&gt;
----------------------------------&lt;br /&gt;
 {Pavel,Petr,Jan,Zbyšek,Bohuslav}&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT array_to_string(array_agg(a),&amp;#039;|&amp;#039;) FROM jmena;&lt;br /&gt;
        array_to_string         &lt;br /&gt;
--------------------------------&lt;br /&gt;
 Pavel|Petr|Jan|Zbyšek|Bohuslav&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
-- seřazený generovaný seznam&lt;br /&gt;
postgres=# SELECT array_to_string(ARRAY(SELECT a FROM jmena ORDER BY a),&amp;#039;|&amp;#039;);&lt;br /&gt;
        array_to_string         &lt;br /&gt;
--------------------------------&lt;br /&gt;
 Bohuslav|Jan|Pavel|Petr|Zbyšek&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE prefixes(country varchar, p varchar);&lt;br /&gt;
INSERT INTO prefixes VALUES(&amp;#039;cs&amp;#039;,&amp;#039;724,777,728&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT * FROM prefixes ;&lt;br /&gt;
 country |      p      &lt;br /&gt;
---------+-------------&lt;br /&gt;
 cs      | 724,777,728&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
-- normalizace&lt;br /&gt;
postgres=# SELECT country, unnest(string_to_array(p,&amp;#039;,&amp;#039;)) FROM prefixes ;&lt;br /&gt;
 country | unnest &lt;br /&gt;
---------+--------&lt;br /&gt;
 cs      | 724&lt;br /&gt;
 cs      | 777&lt;br /&gt;
 cs      | 728&lt;br /&gt;
(3 rows)&lt;br /&gt;
&lt;br /&gt;
-- přepis funkce unnest do SQL&lt;br /&gt;
CREATE OR REPLACE FUNCTION myunnest(anyarray)&lt;br /&gt;
RETURNS SETOF anyelement AS $$&lt;br /&gt;
SELECT $1[i] FROM generate_subscripts($1,1) g(i)&lt;br /&gt;
$$ LANGUAGE sql;&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT myunnest(ARRAY[3,4,5]);&lt;br /&gt;
 myunnest &lt;br /&gt;
----------&lt;br /&gt;
        3&lt;br /&gt;
        4&lt;br /&gt;
        5&lt;br /&gt;
(3 rows)&amp;lt;/pre&amp;gt;&lt;br /&gt;
Výše uvedené funkce jsou dostačující. Představme si, že dostaneme soubor ve formátu xls, který obsahuje registrovaná předčíslí národních telefonních operátorů ve tvaru kód země a seznamu předčíslí (prefixů) oddělených čárkou. Takový soubor skutečně existuje. Je docela dobře možné, že  xls-ko je nejpoužívanějším formátem pro přenos databázových dat - bohužel nebo bohudík. Díky xls nemáme problémy s kódováním - kdo pamatuje FoxPro, ví co mám na mysli. Na druhou stranu - data z dokumentů ve formátu xls lze jen výjimečně použít bez předchozího čištění.&lt;br /&gt;
&lt;br /&gt;
Vlastní převod dat do PostgreSQL je otázkou několika minut. V prvním kroku vyčistíme tabulku od komentářů, nadpisů a případného dalšího balastu a soubor převedeme do formátu [http://cs.wikipedia.org/wiki/CSV csv]. Příkaz [http://www.postgresql.org/docs/8.4/interactive/sql-copy.html &amp;lt;i&amp;gt;COPY&amp;lt;/i&amp;gt;] formát csv podporuje (Pozor - v případě importu csv vytvořeném v Microsoft Excelu s nastaveným českým prostředím je nezbytné použít klauzuli &amp;lt;i&amp;gt;DELIMITER&amp;lt;/i&amp;gt; (výchozí oddělovač formátu csv je čárka, která je (v české mutaci Excelu) nahrazena středníkem)). &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Pokud bychom měli ze svých dat generovat data v podobném tvaru, pak stačí použít funkce: array_agg a array_to_string. Jako bonus můžeme prefixy seřadit:&lt;br /&gt;
&amp;lt;pre&amp;gt;postgres=# SELECT country, array_to_string(ARRAY(SELECT unnest(string_to_array(p,&amp;#039;,&amp;#039;)) &lt;br /&gt;
                                                        ORDER BY 1),&amp;#039;,&amp;#039;) &lt;br /&gt;
                   FROM prefixes ;&lt;br /&gt;
 country | array_to_string &lt;br /&gt;
---------+-----------------&lt;br /&gt;
 cs      | 724,728,777&lt;br /&gt;
(1 row)&amp;lt;/pre&amp;gt;&lt;br /&gt;
případně v kombinaci s příkazem COPY:&lt;br /&gt;
&amp;lt;pre&amp;gt;postgres=# COPY (SELECT country, array_to_string(ARRAY(SELECT unnest(string_to_array(p,&amp;#039;,&amp;#039;)) &lt;br /&gt;
                                                         ORDER BY 1&lt;br /&gt;
                                                      ),&lt;br /&gt;
                                                 &amp;#039;,&amp;#039;) &lt;br /&gt;
                    FROM prefixes&lt;br /&gt;
                ) &lt;br /&gt;
              TO stdout CSV;&lt;br /&gt;
cs,&amp;quot;724,728,777&amp;quot;&amp;lt;/pre&amp;gt;&lt;br /&gt;
Postup: vstupní seznam hodnot oddělených čárkou byl převeden na pole, dále na tabulku, seřazen  prostřednictvím standardní klauzule ORDER BY. Seřazená tabulka se převedla opět na pole a pole se převedlo zpět na text. V Microsoft Excelu nebo v Open Office Calcu můžeme exportované csv-čko převést do formátu xls.&lt;br /&gt;
&lt;br /&gt;
Fantazii se meze nekladou. Složitější SQL dotazy si můžeme zjednodušit zapouzdřením bloků SQL do tzv vlastních SQL funkcí. Příkladem může být funkce &amp;lt;i&amp;gt;unpack_domains&amp;lt;/i&amp;gt;. Tato funkce generuje hierarchii doménových jmen. Např. pro kix.fsv.cvut.cz - (kix.fsv.cvut.cz, fsv.cvut.cz, cvut.cz, cz). &lt;br /&gt;
&amp;lt;pre&amp;gt;CREATE OR REPLACE FUNCTION unpack_domains(text) &lt;br /&gt;
RETURNS SETOF text AS $$ &lt;br /&gt;
SELECT array_to_string(a.f[ i : array_upper(a.f,1) ],&amp;#039;.&amp;#039;) &lt;br /&gt;
   FROM generate_subscripts(string_to_array($1,&amp;#039;.&amp;#039;),1,true) g(i), &lt;br /&gt;
        (SELECT string_to_array($1,&amp;#039;.&amp;#039;)) a(f)&lt;br /&gt;
$$ LANGUAGE sql;&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT unpack_domains(&amp;#039;kix.fsv.cvut.cz&amp;#039;);&lt;br /&gt;
 unpack_domains  &lt;br /&gt;
-----------------&lt;br /&gt;
 cz&lt;br /&gt;
 cvut.cz&lt;br /&gt;
 fsv.cvut.cz&lt;br /&gt;
 kix.fsv.cvut.cz&lt;br /&gt;
(4 rows)&amp;lt;/pre&amp;gt;&lt;br /&gt;
K čemu je to dobré? Představme si, že máme zpracovat log přístupů obsahující doménové adresy klientů, přičemž chceme vědět z jakých domén a v jakém počtu se na naše zařízení přistupovalo.&lt;br /&gt;
&amp;lt;pre&amp;gt;CREATE TABLE log(a varchar);&lt;br /&gt;
INSERT INTO log VALUES(&amp;#039;kix.fsv.cvut.cz&amp;#039;),(&amp;#039;lmc.eu&amp;#039;),(&amp;#039;inway.cz&amp;#039;),(&amp;#039;gmail.com&amp;#039;),(&amp;#039;josef.fsv.cvut.cz&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT * FROM log;&lt;br /&gt;
        a        &lt;br /&gt;
------------------&lt;br /&gt;
 kix.fsv.cvut.cz&lt;br /&gt;
 lmc.eu&lt;br /&gt;
 inway.cz&lt;br /&gt;
 gmail.com&lt;br /&gt;
 josef.fsv.cvut.cz&lt;br /&gt;
(4 rows)&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT count(*), unpack_domains(a) as domain&lt;br /&gt;
              FROM log &lt;br /&gt;
             GROUP BY unpack_domains(a) &lt;br /&gt;
             ORDER BY 1 desc;&lt;br /&gt;
 count |      domain       &lt;br /&gt;
-------+-------------------&lt;br /&gt;
     3 | cz&lt;br /&gt;
     2 | cvut.cz&lt;br /&gt;
     2 | fsv.cvut.cz&lt;br /&gt;
     1 | eu&lt;br /&gt;
     1 | josef.fsv.cvut.cz&lt;br /&gt;
     1 | gmail.com&lt;br /&gt;
     1 | inway.cz&lt;br /&gt;
     1 | com&lt;br /&gt;
     1 | lmc.eu&lt;br /&gt;
     1 | kix.fsv.cvut.cz&lt;br /&gt;
(10 rows)&amp;lt;/pre&amp;gt;&lt;br /&gt;
S takovým reportem bychom nejspíš neuspěli. Chtělo by to lépe jej uspořádat a to alespoň podle obráceného názvu domény:&lt;br /&gt;
&lt;br /&gt;
Zde nastane první problém. V PostgreSQL nemáme funkci pro zrcadlové prohození znaků v řetězci. Můžeme si ji však napsat v [http://www.postgresql.org/docs/8.4/interactive/plpgsql.html &amp;lt;i&amp;gt;PL/pgSQL&amp;lt;/i&amp;gt;], [http://www.postgresql.org/docs/8.4/interactive/plperl.html &amp;lt;i&amp;gt;Perlu&amp;lt;/i&amp;gt;], [http://www.postgresql.org/docs/8.4/interactive/plpython.html &amp;lt;i&amp;gt;Pythonu&amp;lt;/i&amp;gt;], v [http://www.postgres.cz/index.php/N%C3%A1vrh_a_realizace_UDF_v_c_pro_PostgreSQL jazyce &amp;lt;i&amp;gt;C&amp;lt;/i&amp;gt;], a nebo v jazyce SQL:&lt;br /&gt;
&amp;lt;pre&amp;gt;CREATE OR REPLACE FUNCTION rvrs(text) &lt;br /&gt;
RETURNS text AS $$ &lt;br /&gt;
SELECT array_to_string(array_agg(a.f[i]),&amp;#039;&amp;#039;) &lt;br /&gt;
   FROM generate_subscripts(regexp_split_to_array($1,&amp;#039;&amp;#039;),1, true) g(i), &lt;br /&gt;
        (SELECT regexp_split_to_array($1,&amp;#039;&amp;#039;)) a(f) &lt;br /&gt;
$$ LANGUAGE sql;&lt;br /&gt;
&lt;br /&gt;
postgres=# select rvrs(&amp;#039;ahoj&amp;#039;);&lt;br /&gt;
 rvrs &lt;br /&gt;
------&lt;br /&gt;
 joha&lt;br /&gt;
(1 row)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Funkce &amp;lt;i&amp;gt;generate_subscripts&amp;lt;/i&amp;gt; generuje indexy pro zadené pole. Pokud je třetí (volitelný) parametr roven hodnotě true, pak jsou indexy generovány v sestupném pořadí. Funkce &amp;lt;i&amp;gt;regexp_split_to_array&amp;lt;/i&amp;gt; generuje pole na základě shody s regulárním výrazem. Pokud není regulární výraz zadán, pak prvek pole odpovídá znaku v řetězci.&lt;br /&gt;
&amp;lt;pre&amp;gt;postgres=# SELECT count(*), unpack_domains(a) as domain &lt;br /&gt;
              FROM log &lt;br /&gt;
             GROUP BY unpack_domains(a) &lt;br /&gt;
             ORDER BY rvrs(unpack_domains(a));&lt;br /&gt;
 count |      domain       &lt;br /&gt;
-------+-------------------&lt;br /&gt;
     1 | com&lt;br /&gt;
     1 | gmail.com&lt;br /&gt;
     1 | eu&lt;br /&gt;
     1 | lmc.eu&lt;br /&gt;
     4 | cz&lt;br /&gt;
     3 | cvut.cz&lt;br /&gt;
     3 | fsv.cvut.cz&lt;br /&gt;
     1 | josef.fsv.cvut.cz&lt;br /&gt;
     1 | kix.fsv.cvut.cz&lt;br /&gt;
     1 | inway.cz&lt;br /&gt;
(10 rows)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Už je to skoro ono - jen je tu určité riziko - řadí se od konce názvů, nikoliv od začátku. Chtělo by to spíše reverz celého pole:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;/* ukázka polymorfní funkce - pro libovolné pole */&lt;br /&gt;
CREATE OR REPLACE FUNCTION rvrs(anyarray) &lt;br /&gt;
RETURNS anyarray AS $$ &lt;br /&gt;
SELECT ARRAY(SELECT $1[i] &lt;br /&gt;
   FROM generate_subscripts($1, 1, true) g(i)) &lt;br /&gt;
$$ LANGUAGE sql;&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT rvrs(string_to_array(&amp;#039;kix.fsv.cvut.cz&amp;#039;,&amp;#039;.&amp;#039;));&lt;br /&gt;
       rvrs        &lt;br /&gt;
-------------------&lt;br /&gt;
 {cz,cvut,fsv,kix}&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT rvrs(&amp;#039;kix.fsv.cvut.cz&amp;#039;);&lt;br /&gt;
      rvrs       &lt;br /&gt;
-----------------&lt;br /&gt;
 zc.tuvc.vsf.xik&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
/* + ukázka přetížení funkce rvrs */&lt;br /&gt;
postgres=# SELECT count(*), unpack_domains(a) as domain &lt;br /&gt;
   FROM log &lt;br /&gt;
  GROUP BY unpack_domains(a) &lt;br /&gt;
  ORDER BY (rvrs(string_to_array(unpack_domains(a),&amp;#039;.&amp;#039;)))[1],&lt;br /&gt;
           (rvrs(string_to_array(unpack_domains(a),&amp;#039;.&amp;#039;)))[2] nulls first,&lt;br /&gt;
           (rvrs(string_to_array(unpack_domains(a),&amp;#039;.&amp;#039;)))[3] nulls first,&lt;br /&gt;
           (rvrs(string_to_array(unpack_domains(a),&amp;#039;.&amp;#039;)))[4] nulls first;&lt;br /&gt;
 count |      domain       &lt;br /&gt;
-------+-------------------&lt;br /&gt;
     1 | com&lt;br /&gt;
     1 | gmail.com&lt;br /&gt;
     4 | cz&lt;br /&gt;
     3 | cvut.cz&lt;br /&gt;
     3 | fsv.cvut.cz&lt;br /&gt;
     1 | josef.fsv.cvut.cz&lt;br /&gt;
     1 | kix.fsv.cvut.cz&lt;br /&gt;
     1 | inway.cz&lt;br /&gt;
     1 | eu&lt;br /&gt;
     1 | lmc.eu&lt;br /&gt;
(10 rows)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
S tímto pořadím již můžeme být spokojeni.&lt;br /&gt;
&lt;br /&gt;
==Pole a dynamické SQL==&lt;br /&gt;
Při sestavování dynamického SQL příkazu je vhodné na SQL identifikátory aplikovat funkci &amp;lt;i&amp;gt;quote_ident&amp;lt;/i&amp;gt;. Tím zabezpečujeme své aplikace proti [http://en.wikipedia.org/wiki/SQL_injection &amp;lt;i&amp;gt;SQL injektáži&amp;lt;/i&amp;gt;] a případně i proti syntaktickým chybám, pokud je identifikátor nevhodně navržen (např. obsahuje mezery, tečky a pod.). &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;postgres=# SELECT quote_ident(&amp;#039;aaaaa&amp;#039;);&lt;br /&gt;
 quote_ident &lt;br /&gt;
-------------&lt;br /&gt;
 aaaaa&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT quote_ident(&amp;#039;aaa&amp;#039;&amp;#039;aa&amp;#039;);&lt;br /&gt;
 quote_ident &lt;br /&gt;
-------------&lt;br /&gt;
 &amp;quot;aaa&amp;#039;aa&amp;quot;&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT quote_ident(&amp;#039;aaa aa&amp;#039;);&lt;br /&gt;
 quote_ident &lt;br /&gt;
-------------&lt;br /&gt;
 &amp;quot;aaa aa&amp;quot;&lt;br /&gt;
(1 row)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Bohužel funkce quote_ident si neporadí se schématy. Viz výsledek volání funkce,&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;postgres=# SELECT quote_ident(&amp;#039;tabulka.schema&amp;#039;);&lt;br /&gt;
   quote_ident    &lt;br /&gt;
------------------&lt;br /&gt;
 &amp;quot;tabulka.schema&amp;quot;&lt;br /&gt;
(1 row)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
který je nepoužitelný. Korektní identifikátor je v tomto případě &amp;quot;tabulka&amp;quot;.&amp;quot;schema&amp;quot;. Řešením, které ovšem není 100% (má problémy s tečkou uvnitř identifikátoru), je transformace do pole, a aplikace funkce quote_ident na každý prvek pole.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CREATE OR REPLACE FUNCTION quote_schema_ident(text) &lt;br /&gt;
RETURNS text AS $$ &lt;br /&gt;
SELECT array_to_string(ARRAY(SELECT quote_ident(unnest(string_to_array($1,&amp;#039;.&amp;#039;)))),&amp;#039;.&amp;#039;) &lt;br /&gt;
$$ LANGUAGE sql;&lt;br /&gt;
&lt;br /&gt;
postgres=# select quote_schema_ident(&amp;#039;hloupy nazev schematu.tabulka&amp;#039;);&lt;br /&gt;
       quote_schema_ident        &lt;br /&gt;
---------------------------------&lt;br /&gt;
 &amp;quot;hloupy nazev schematu&amp;quot;.tabulka&lt;br /&gt;
(1 row)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Přetypováním na typ &amp;lt;i&amp;gt;regclass&amp;lt;/i&amp;gt; lze jednoduše ověřit identifikátor tabulky. To je poměrně snadný způsob odhalení pokusů o SQL injektáž. Tím můžeme předejít standardním chybovým hlášením poskytujícím útočníkům  další cenné informace:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;postgres=# \dt&lt;br /&gt;
             List of relations&lt;br /&gt;
 Schema |       Name       | Type  | Owner &lt;br /&gt;
--------+------------------+-------+-------&lt;br /&gt;
 public | jmena            | table | pavel&lt;br /&gt;
 public | log              | table | pavel&lt;br /&gt;
 public | prefixes         | table | pavel&lt;br /&gt;
 public | test             | table | pavel&lt;br /&gt;
(12 rows)&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT &amp;#039;omega&amp;#039;::regclass;&lt;br /&gt;
 regclass &lt;br /&gt;
----------&lt;br /&gt;
 omega&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT &amp;#039;omega a&amp;#039;::regclass;&lt;br /&gt;
ERROR:  invalid name syntax&lt;br /&gt;
LINE 1: SELECT &amp;#039;omega a&amp;#039;::regclass;&lt;br /&gt;
               ^&lt;br /&gt;
postgres=# SELECT &amp;#039;omegaa&amp;#039;::regclass;&lt;br /&gt;
ERROR:  relation &amp;quot;omegaa&amp;quot; does not exist&lt;br /&gt;
LINE 1: SELECT &amp;#039;omegaa&amp;#039;::regclass;&lt;br /&gt;
               ^&amp;lt;/pre&amp;gt;&lt;br /&gt;
S ověřováním validity SQL identifikátorů a s použitím klauzule [http://www.postgresql.org/docs/8.4/interactive/plpgsql-statements.html#PLPGSQL-STATEMENTS-EXECUTING-DYN &amp;lt;i&amp;gt;USING&amp;lt;/i&amp;gt;] mohou být naše dynamické SQL příkazy neprůstřelné.&lt;br /&gt;
&lt;br /&gt;
==Pole, tabulka, pole==&lt;br /&gt;
Seznam funkcí pro práci s poli je poměrně omezený - nicméně potřebné funkce si můžeme jednoduše napsat sami. Základní strategie je převod pole na tabulku, provedení určité množinové operace a převod tabulky zpět na pole. Příklad: Zrušení duplicit v poli. V PostgreSQL neexistuje funkce, která by rušila duplicitní prvky pole. Ovšem příkaz &amp;lt;i&amp;gt;SELECT&amp;lt;/i&amp;gt; podporuje klauzuli &amp;lt;i&amp;gt;DISTINCT&amp;lt;/i&amp;gt;, která zajistí výpis pouze unikátních záznamů. Jsme na stopě:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CREATE OR REPLACE FUNCTION array_distinct(anyarray)&lt;br /&gt;
RETURNS anyarray AS $$&lt;br /&gt;
SELECT ARRAY(SELECT DISTINCT unnest($1))&lt;br /&gt;
$$ LANGUAGE sql;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Datový typ &amp;lt;i&amp;gt;anyarray&amp;lt;/i&amp;gt; představuje libovolné pole. Jedná se o tzv. &amp;lt;i&amp;gt;polymorfní typ&amp;lt;/i&amp;gt;. V okamžiku volání funkce se polymorfní typ nahradí skutečným typem, podle typu hodnoty parametru (tak trochu jako templates v C++). Další příklad - spojení dvou polí:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CREATE OR REPLACE FUNCTION array_union(anyarray, anyarray)&lt;br /&gt;
RETURNS anyarray AS $$&lt;br /&gt;
SELECT ARRAY(SELECT unnest($1) UNION ALL unnest($2))&lt;br /&gt;
$$ LANGUAGE sql;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nativní implementace spojení polí v C bude ještě o něco rychlejší, k ale tomu potřebujeme hodně dobré znalosti PostgreSQL a C. Implementace v SQL je rychlostně v pohodě. Jednak je řádově rychlejší než implementace v PL/pgSQL a hlavně, hrdlem databázových operací je přístup na disk - CPU se fláká. Další příklad - dohledání prvku v poli:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CREATE OR REPLACE FUNCTION indexof(anyarray, anyelement, pos int = NULL)&lt;br /&gt;
RETURNS int AS $$&lt;br /&gt;
SELECT i &lt;br /&gt;
   FROM generate_subscripts($1,1) g(i)&lt;br /&gt;
  WHERE $1[i] = $2 &lt;br /&gt;
    AND i &amp;gt;= COALESCE($1, array_lower($1,1))&lt;br /&gt;
$$ LANGUAGE sql;&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT indexof(array[1,3,4,2,3,4,1],1,2);&lt;br /&gt;
 indexof &lt;br /&gt;
---------&lt;br /&gt;
       7&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT indexof(array[1,3,4,2,3,4,1],1);&lt;br /&gt;
 indexof &lt;br /&gt;
---------&lt;br /&gt;
       1&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT indexof(array[1,3,4,2,3,4,1],2);&lt;br /&gt;
 indexof &lt;br /&gt;
---------&lt;br /&gt;
       4&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT indexof(array[1,3,4,2,3,4,1],1,2);&lt;br /&gt;
 indexof &lt;br /&gt;
---------&lt;br /&gt;
       7&lt;br /&gt;
(1 row)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Nejčastějším operací je seřazení pole:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CREATE OR REPLACE FUNCTION array_sort(anyarray)&lt;br /&gt;
RETURNS anyarray AS $$&lt;br /&gt;
SELECT ARRAY(SELECT unnest($1) ORDER BY 1)&lt;br /&gt;
$$ LANGUAGE sql;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pole převedeme na jednosloupcovou tabulku, necháme seřadit podle prvního sloupce a výsledek převedeme zpět na pole.  Díky tomu, že se použijí interní rutiny pro řazení (&amp;lt;i&amp;gt;quick sort&amp;lt;/i&amp;gt;) je funkce array_sort rychlá i pro velmi velká pole (nad 100 000 prvků):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;-- samotné generovaní pole o velikosti 100 000 prvků&lt;br /&gt;
postgres=# SELECT ARRAY(SELECT random()*10000 FROM generate_series(1,100000));&lt;br /&gt;
                                                                                                    &lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
 {2729.45704869926,13.0388513207436,2540.07804207504,5272.97182939947,270.577119663358,4648.89997150...&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
Time: 339,738 ms&lt;br /&gt;
&lt;br /&gt;
-- sort&lt;br /&gt;
postgres=# SELECT array_sort(ARRAY(SELECT random()*10000 FROM generate_series(1,100000)));&lt;br /&gt;
                                                                                                    &lt;br /&gt;
----------------------------------------------------------------------------------------------------&lt;br /&gt;
 {0.00012572854757309,0.16817357391119,0.260430388152599,0.391206704080105,0.494923442602158,0.69868....&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
Time: 560,945 ms&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Pole a statistické funkce==&lt;br /&gt;
Určení běžných statistik jako je &amp;lt;i&amp;gt;průměr&amp;lt;/i&amp;gt;, &amp;lt;i&amp;gt;minimum&amp;lt;/i&amp;gt;, &amp;lt;i&amp;gt;maximum&amp;lt;/i&amp;gt; je v SQL díky vestavěným agregačním funkcím jednoduché. Problematické jsou statistiky založené na pozici v seřazeném souboru dat - &amp;lt;i&amp;gt;kvantily&amp;lt;/i&amp;gt;, &amp;lt;i&amp;gt;percentily&amp;lt;/i&amp;gt; a například &amp;lt;i&amp;gt;medián&amp;lt;/i&amp;gt;. ANSI SQL 2001 obsahuje funkci [http://www.postgresql.org/docs/8.4/interactive/functions-window.html &amp;lt;i&amp;gt;row_number&amp;lt;/i&amp;gt;]. Tato funkce se nyní objevuje i v PostgreSQL - konkrétně ve verzi 8.4. Určení mediánu pak není problém ([http://www.simple-talk.com/sql/t-sql-programming/median-workbench/ metoda &amp;lt;i&amp;gt;Joe Celka&amp;lt;/i&amp;gt;]):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CREATE TABLE  x(a integer);&lt;br /&gt;
INSERT INTO x SELECT (random()*10000)::int FROM generate_series(1,10000);&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT avg(a)::float&lt;br /&gt;
              FROM (SELECT a, row_number() OVER (ORDER BY a asc) AS hi,&lt;br /&gt;
                              count(*) OVER () + 1 - row_number() OVER (ORDER BY a) AS lo&lt;br /&gt;
                       FROM x) qs&lt;br /&gt;
             WHERE hi IN (lo-1,lo,lo+1);&lt;br /&gt;
 avg  &lt;br /&gt;
------&lt;br /&gt;
 4936&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
Time: 112,469 ms&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Ve starších verzích PostgreSQL bylo několik možností:&lt;br /&gt;
&amp;lt;ol type=&amp;quot;a&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;použít SELF JOIN alchymii &lt;br /&gt;
&amp;lt;li&amp;gt;použít kurzor&lt;br /&gt;
&amp;lt;li&amp;gt;použít pole&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
Varianta b bude nejrychlejší, varianta c naopak nejjednodušší a k tomu řádově rychlejší než varianta a. Funkce pro určení mediánu z pole může vypadat následovně:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CREATE OR REPLACE FUNCTION array_median(float[]) &lt;br /&gt;
RETURNS float AS $$ &lt;br /&gt;
SELECT ((a.v[l/2+1] + a.v[(l+1)/2])/2.0) &lt;br /&gt;
   FROM (SELECT ARRAY(SELECT unnest($1) ORDER BY 1), &lt;br /&gt;
                array_upper($1,1) - array_lower($1,1) + 1) a(v,l)&lt;br /&gt;
$$ LANGUAGE sql;&lt;br /&gt;
&lt;br /&gt;
postgres=# select array_median(ARRAY(SELECT a FROM x));&lt;br /&gt;
 array_median&lt;br /&gt;
---------------&lt;br /&gt;
          4936&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
Time: 68,625 ms&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Pokud bychom dopředu znali velikost tabulky, pak medián určíme dotazem:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;postgres=# SELECT avg(a)::float FROM (SELECT a FROM x ORDER BY 1 OFFSET 5000-1 LIMIT 2) s;&lt;br /&gt;
 avg  &lt;br /&gt;
------&lt;br /&gt;
 4936&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
Time: 22,212 ms&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Pole a variadické funkce==&lt;br /&gt;
[http://www.postgresql.org/docs/8.4/interactive/xfunc-sql.html &amp;lt;i&amp;gt;Variadické funkce&amp;lt;/i&amp;gt;] jsou funkce, které nemají pevný počet parametrů. V PostgreSQL je několik takových funkcí  - &amp;lt;i&amp;gt;coalesce&amp;lt;/i&amp;gt;, &amp;lt;i&amp;gt;greatest&amp;lt;/i&amp;gt;, &amp;lt;i&amp;gt;least&amp;lt;/i&amp;gt;. Počínaje verzí 8.4 můžeme navrhovat vlastní variadické funkce. A jelikož se variadické parametry předávají funkci jako pole, můžeme uplatnit veškeré výše uvedené postupy. Začnu ukázkou dvou jednoduchých funkcí &amp;lt;i&amp;gt;concat&amp;lt;/i&amp;gt; a &amp;lt;i&amp;gt;myleast&amp;lt;/i&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;CREATE OR REPLACE FUNCTION concat(VARIADIC str text[])&lt;br /&gt;
RETURNS text AS $$&lt;br /&gt;
SELECT array_to_string($1,&amp;#039;&amp;#039;)&lt;br /&gt;
$$ LANGUAGE sql;&lt;br /&gt;
&lt;br /&gt;
CREATE OR REPLACE FUNCTION concat_ws(separator text, VARIADIC str text[])&lt;br /&gt;
RETURNS text AS $$&lt;br /&gt;
SELECT array_to_string($2,$1)&lt;br /&gt;
$$ LANGUAGE sql;&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT concat_ws(&amp;#039;.&amp;#039;, &amp;#039;kix&amp;#039;,&amp;#039;fsv&amp;#039;,&amp;#039;cvut&amp;#039;,&amp;#039;cz&amp;#039;);&lt;br /&gt;
    concat_ws    &lt;br /&gt;
-----------------&lt;br /&gt;
 kix.fsv.cvut.cz&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT concat_ws(&amp;#039;.&amp;#039;,&amp;#039;cvut&amp;#039;,&amp;#039;cz&amp;#039;);&lt;br /&gt;
 concat_ws &lt;br /&gt;
-----------&lt;br /&gt;
 cvut.cz&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
CREATE OR REPLACE FUNCTION myleast(VARIADIC anyarray) &lt;br /&gt;
RETURNS anyelement AS $$&lt;br /&gt;
SELECT min(v) FROM unnest($1) u(v)&lt;br /&gt;
$$ LANGUAGE sql;&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT myleast(1,2,3,-1);&lt;br /&gt;
 myleast &lt;br /&gt;
---------&lt;br /&gt;
      -1&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT myleast(&amp;#039;A&amp;#039;::text,&amp;#039;B&amp;#039;,&amp;#039;C&amp;#039;);&lt;br /&gt;
 myleast &lt;br /&gt;
---------&lt;br /&gt;
 A&lt;br /&gt;
(1 row)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
V MySQL je jedna docela zajímavá funkce [http://dev.mysql.com/doc/refman/5.0/en/string-functions.html#function_field &amp;lt;i&amp;gt;field&amp;lt;/i&amp;gt;]. Vrací pořadové číslo parametru, který se shoduje se zadanou hodnotou. Lze ji použít v klauzuli ORDER BY pro explicitní určení pořadí:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;mysql&amp;gt; SELECT FIELD(&amp;#039;ej&amp;#039;, &amp;#039;Hej&amp;#039;, &amp;#039;ej&amp;#039;, &amp;#039;Heja&amp;#039;, &amp;#039;hej&amp;#039;, &amp;#039;foo&amp;#039;);&lt;br /&gt;
        -&amp;gt; 2&lt;br /&gt;
mysql&amp;gt; SELECT FIELD(&amp;#039;fo&amp;#039;, &amp;#039;Hej&amp;#039;, &amp;#039;ej&amp;#039;, &amp;#039;Heja&amp;#039;, &amp;#039;hej&amp;#039;, &amp;#039;foo&amp;#039;);&lt;br /&gt;
        -&amp;gt; 0&lt;br /&gt;
&lt;br /&gt;
CREATE OR REPLACE FUNCTION field(str text, VARIADIC strn text[]) &lt;br /&gt;
RETURNS int AS $$&lt;br /&gt;
SELECT i &lt;br /&gt;
   FROM generate_subscripts($2,1) g(i) &lt;br /&gt;
  WHERE $2[i] = $1 &lt;br /&gt;
  UNION ALL &lt;br /&gt;
  SELECT 0 &lt;br /&gt;
  LIMIT 1$$ LANGUAGE sql;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Aby byla implementace úplná, je třeba ještě přidat číselnou variantu:&lt;br /&gt;
&amp;lt;pre&amp;gt;CREATE OR REPLACE FUNCTION field(str numeric, VARIADIC strn numeric[]) &lt;br /&gt;
RETURNS int AS $$&lt;br /&gt;
SELECT i &lt;br /&gt;
   FROM generate_subscripts($2,1) g(i) &lt;br /&gt;
  WHERE $2[i] = $1 &lt;br /&gt;
  UNION ALL &lt;br /&gt;
  SELECT 0 &lt;br /&gt;
  LIMIT 1$$ LANGUAGE sql;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Spuštěním příkladů z dokumentace MySQL si můžeme ověřit funkčnost naší funkce field:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;postgres=# SELECT field(&amp;#039;ej&amp;#039;, &amp;#039;Hej&amp;#039;, &amp;#039;ej&amp;#039;, &amp;#039;Heja&amp;#039;, &amp;#039;hej&amp;#039;, &amp;#039;foo&amp;#039;);&lt;br /&gt;
 field &lt;br /&gt;
-------&lt;br /&gt;
     2&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT field(&amp;#039;fo&amp;#039;, &amp;#039;Hej&amp;#039;, &amp;#039;ej&amp;#039;, &amp;#039;Heja&amp;#039;, &amp;#039;hej&amp;#039;, &amp;#039;foo&amp;#039;);&lt;br /&gt;
 field &lt;br /&gt;
-------&lt;br /&gt;
     0&lt;br /&gt;
(1 row)&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Funkce &amp;lt;i&amp;gt;elt&amp;lt;/i&amp;gt; je komplementární k funkci field. Vrací n-tý parametr funkce:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;mysql&amp;gt; SELECT ELT(1, &amp;#039;ej&amp;#039;, &amp;#039;Heja&amp;#039;, &amp;#039;hej&amp;#039;, &amp;#039;foo&amp;#039;);&lt;br /&gt;
        -&amp;gt; &amp;#039;ej&amp;#039;&lt;br /&gt;
mysql&amp;gt; SELECT ELT(4, &amp;#039;ej&amp;#039;, &amp;#039;Heja&amp;#039;, &amp;#039;hej&amp;#039;, &amp;#039;foo&amp;#039;);&lt;br /&gt;
        -&amp;gt; &amp;#039;foo&amp;#039;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Implementace této funkce je krásně triviální:&lt;br /&gt;
&amp;lt;pre&amp;gt;CREATE OR REPLACE FUNCTION elt(n int, VARIADIC strn text[]) &lt;br /&gt;
RETURNS text AS $$ &lt;br /&gt;
SELECT $2[$1]&lt;br /&gt;
$$ LANGUAGE sql;&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT elt(1, &amp;#039;ej&amp;#039;, &amp;#039;Heja&amp;#039;, &amp;#039;hej&amp;#039;, &amp;#039;foo&amp;#039;);&lt;br /&gt;
-----&lt;br /&gt;
 ej&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT elt(4, &amp;#039;ej&amp;#039;, &amp;#039;Heja&amp;#039;, &amp;#039;hej&amp;#039;, &amp;#039;foo&amp;#039;);&lt;br /&gt;
 elt &lt;br /&gt;
-----&lt;br /&gt;
 foo&lt;br /&gt;
(1 row)&amp;lt;/pre&amp;gt;&lt;br /&gt;
==Tabulka jako pole==&lt;br /&gt;
Pomocí polí můžeme simulovat vlastnost jiných databází, která se označuje jako &amp;#039;&amp;#039;nested tables&amp;#039;&amp;#039;. Ačkoliv nested tables vypadají lákavě, nepoužívejte je pro ukládání dat! Připravíte se o optimalizaci, indexy.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
postgres=# create table f(a int, b int, c int);&lt;br /&gt;
CREATE TABL&lt;br /&gt;
postgres=# create table ff(v f[]);&lt;br /&gt;
CREATE TABLE&lt;br /&gt;
postgres=# insert into  f values(10,20,30),(40,50,60);&lt;br /&gt;
INSERT 0 2&lt;br /&gt;
postgres=# insert into ff select array(select row(a,b,c)::f from f);&lt;br /&gt;
INSERT 0 1&lt;br /&gt;
postgres=# select * from ff;&lt;br /&gt;
              v&lt;br /&gt;
-----------------------------&lt;br /&gt;
 {&amp;quot;(10,20,30)&amp;quot;,&amp;quot;(40,50,60)&amp;quot;}&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
postgres=# select unnest(v) from ff;&lt;br /&gt;
   unnest&lt;br /&gt;
------------&lt;br /&gt;
 (10,20,30)&lt;br /&gt;
 (40,50,60)&lt;br /&gt;
(2 rows)&lt;br /&gt;
&lt;br /&gt;
postgres=# select (unnest(v)).* from ff;&lt;br /&gt;
 a  | b  | c&lt;br /&gt;
----+----+----&lt;br /&gt;
 10 | 20 | 30&lt;br /&gt;
 40 | 50 | 60&lt;br /&gt;
(2 rows)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Odkazy==&lt;br /&gt;
Některé ze zde uvedených příkladů můžeme najít na webu [http://www.postgres.cz/index.php/SQL_Triky]. Další příklady a další inspiraci najdeme v archivu fragmentů kódu [http://wiki.postgresql.org/wiki/Category:Snippets].&lt;/div&gt;</summary>
		<author><name>imported&gt;Pavel</name></author>
	</entry>
</feed>