<?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=SQL_Triky_III.</id>
	<title>SQL Triky III. - Historie editací</title>
	<link rel="self" type="application/atom+xml" href="http://postgres.cz/index.php?action=history&amp;feed=atom&amp;title=SQL_Triky_III."/>
	<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=SQL_Triky_III.&amp;action=history"/>
	<updated>2026-04-15T17:11:16Z</updated>
	<subtitle>Historie editací této stránky</subtitle>
	<generator>MediaWiki 1.43.3</generator>
	<entry>
		<id>http://postgres.cz/index.php?title=SQL_Triky_III.&amp;diff=461&amp;oldid=prev</id>
		<title>imported&gt;Pavel: /* Zjištění počtu výskytu subřetězce v řetězci */</title>
		<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=SQL_Triky_III.&amp;diff=461&amp;oldid=prev"/>
		<updated>2011-09-03T06:04:53Z</updated>

		<summary type="html">&lt;p&gt;&lt;span class=&quot;autocomment&quot;&gt;Zjištění počtu výskytu subřetězce v řetězci&lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Nová stránka&lt;/b&gt;&lt;/p&gt;&lt;div&gt;[[SQL Triky II.|Starších dvacet triků]] [[SQL Triky IV.|Novějších dvacet triků]]&lt;br /&gt;
&lt;br /&gt;
==Pomoc při migraci z TSearch2 na vestavěný fulltext==&lt;br /&gt;
Ve verzi 8.3 došlo k integraci modulu TSearch2 - bohužel integrovaný fulltext není plně kompatibilní s starším modulem, což působí problémy už při migraci databáze. Pro zjednodušení migraci vývojáři přepsali modul TSearch2 tak, aby maskoval rozdíly mezi integrovaným fulltextem a rozšiřujícím modulem. Modul se musí registrovat do databáze ještě před vlastním načtením dumpu - to v případě importu jednotlivých databází není problém. Problémy nastanou v případě, že dumpujete všechny db naráz. Dump se totiž odkazuje nikoliv na template1, kam můžete TSearch2 předinstalovat, ale na template0, s kterou nelze nijak pracovat. Pokud se setkáte s chybou:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
ERROR:  type &amp;quot;tsvector&amp;quot; already exists&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
musíte poeditovat dump (pomůže rozdělit dump na strukturu a data), a přidat za vytvořením databáze import registračního souboru TSearch2.sql:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DROP DATABASE brigady;&lt;br /&gt;
CREATE DATABASE brigady WITH TEMPLATE = template0  ENCODING = &amp;#039;UTF8&amp;#039;;&lt;br /&gt;
--&lt;br /&gt;
-- PostgreSQL database dump complete&lt;br /&gt;
--&lt;br /&gt;
&lt;br /&gt;
\connect brigady&lt;br /&gt;
\i /usr/local/pgsql/share/contrib/tsearch2.sql&lt;br /&gt;
&lt;br /&gt;
SET search_path = public, pg_catalog;&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Nepodceňujte SQL funkce==&lt;br /&gt;
Snad jediné pomalé operace, které jsou v PL/pgSQL je změna pole a změna řetězce. V obou případech se totiž nemění pouze jeden prvek (jeden znak), ale generuje se nové pole nebo nový řetězec. Pro malé objekty to není nijak zásadní režie, ale pro velké objekty, a velký počet změn, již může být tato režie znatelná. A proto je dobré se ji vyhnout. V následující ukázce porovnávám rychlost řazení v SQL a v PL/pgSQL (metoda quick sort):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION quicksort(l integer, r integer, a int[])&lt;br /&gt;
RETURNS int[] AS $$&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;
$$ LANGUAGE plpgsql STRICT IMMUTABLE;&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT array_upper(quicksort(1,10000,array_agg(a)),1) FROM test;&lt;br /&gt;
 array_upper&lt;br /&gt;
-------------&lt;br /&gt;
       10000&lt;br /&gt;
(1 row)&lt;br /&gt;
Time: 5918,531 ms&lt;br /&gt;
&lt;br /&gt;
CREATE OR REPLACE FUNCTION sort(anyarray) &lt;br /&gt;
RETURNS anyarray AS $$&lt;br /&gt;
  SELECT array(SELECT * FROM unnest($1) ORDER BY 1); &lt;br /&gt;
$$ language sql;&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT array_upper(sort(array_agg(a)),1) FROM test;&lt;br /&gt;
 array_upper&lt;br /&gt;
-------------&lt;br /&gt;
       10000&lt;br /&gt;
(1 row)&lt;br /&gt;
Time: 35,980 ms&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Na výsledku není nic překvapivého - řazení v SQL je implementováno přímo v C, podstatně efektivněji než kdy může být implementováno v PL/pgSQL (v obou případech je ovšem použit quicksort). Pro zajímavost Bublesort během 5min nedoběhl (pro 1000 prvků SQL 7ms, Quick 150ms, Buble 7000ms). Je důležité si stále připomínat, že jednoduchý kód může být velice rychlý, zvlášť pokud je nativně implementován.&lt;br /&gt;
&lt;br /&gt;
==Přístup ke změněným datům z statement triggerů==&lt;br /&gt;
V případě statement triggerů PostgreSQL nenabízí přímo žádnou podporu pro identifikaci řádků, které modifikoval SQL příkaz zodpovědný za aktivaci triggeru. Gurjeet Singh publikoval způsob, jak se v triggeru dotázat na nové hodnoty změněné v aktuální transakci:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE foo(a integer);&lt;br /&gt;
INSERT INTO foo VALUES(0),(0);&lt;br /&gt;
&lt;br /&gt;
CREATE OR REPLACE FUNCTION trghdx() &lt;br /&gt;
RETURNS trigger as $$ &lt;br /&gt;
DECLARE r record; &lt;br /&gt;
BEGIN &lt;br /&gt;
  FOR r IN &lt;br /&gt;
     SELECT * FROM foo &lt;br /&gt;
       WHERE xmin::text::bigint = txid_current() &lt;br /&gt;
  LOOP&lt;br /&gt;
    RAISE NOTICE &amp;#039;%&amp;#039;, r.a; &lt;br /&gt;
  END LOOP; &lt;br /&gt;
  RETURN NULL; &lt;br /&gt;
END&lt;br /&gt;
$$ LANGUAGE plpgsql;&lt;br /&gt;
&lt;br /&gt;
CREATE TRIGGER trg AFTER INSERT OR UPDATE &lt;br /&gt;
   ON foo FOR EACH STATEMENT EXECUTE PROCEDURE trghdx();&lt;br /&gt;
&lt;br /&gt;
postgres=# INSERT INTO FOO VALUES(10),(10);&lt;br /&gt;
NOTICE:  10&lt;br /&gt;
NOTICE:  10&lt;br /&gt;
INSERT 0 2&lt;br /&gt;
postgres=# UPDATE foo SET a = 5 WHERE a = 10;&lt;br /&gt;
NOTICE:  5&lt;br /&gt;
NOTICE:  5&lt;br /&gt;
UPDATE 2&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Pozor! V určitých okrajových případech tato metoda není funkční - v každém případě se jedná o nehezký hack.&lt;br /&gt;
&lt;br /&gt;
==Rychlý posun indexů na počátek v nule==&lt;br /&gt;
Tato funkce by se dala ideálně vyřešit v C, kdy stačí nastavit binárně jednu hodnotu ve struktuře popisující pole. PL/pgSQL tuto možnost nenabízí. Iterace po prvku je pomalá. Lze ale využít schopnost SQL parseru rozpoznat rozsah indexů pole:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION shift_idx(anyarray) &lt;br /&gt;
RETURNS anyarray AS $$&lt;br /&gt;
DECLARE &lt;br /&gt;
  mi int := array_upper($1,1); &lt;br /&gt;
  offset int := array_lower($1,1); &lt;br /&gt;
BEGIN&lt;br /&gt;
  RETURN &amp;#039;[0:&amp;#039; || mi - offset || &amp;#039;] = &amp;#039; || $1::varchar; &lt;br /&gt;
END &lt;br /&gt;
$$ LANGUAGE plpgsql IMMUTABLE STRICT;&lt;br /&gt;
CREATE FUNCTION&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT shift_idx(array[1,2,3]);&lt;br /&gt;
   shift_idx   &lt;br /&gt;
---------------&lt;br /&gt;
 [0:2]={1,2,3}&lt;br /&gt;
(1 row)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Autor: Pavel Stěhule&lt;br /&gt;
&lt;br /&gt;
==Rozvinutí hodnot typu record==&lt;br /&gt;
V případě, že používáme funkce, které vrací record a nemůžeme je umístit do klauzule FROM, musíme použít následující syntax pro rozvinutí hodnoty typu record ve výsledné tabulce:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE FUNCTION foo(OUT a int, OUT b int, IN c int)&lt;br /&gt;
RETURNS record AS $$&lt;br /&gt;
BEGIN&lt;br /&gt;
  a := c + 1; b := c + 2;&lt;br /&gt;
  RETURN;&lt;br /&gt;
END;&lt;br /&gt;
$$ LANGUAGE plpgsql;&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE t(c int);&lt;br /&gt;
INSERT INTO t VALUES(10),(20);&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT c, foo(c) FROM t;&lt;br /&gt;
 c  |   foo   &lt;br /&gt;
----+---------&lt;br /&gt;
 10 | (11,12)&lt;br /&gt;
 20 | (21,22)&lt;br /&gt;
(2 rows)&lt;br /&gt;
&lt;br /&gt;
postgres=# SELECT c, (foo(c)).* FROM t;&lt;br /&gt;
 c  | a  | b  &lt;br /&gt;
----+----+----&lt;br /&gt;
 10 | 11 | 12&lt;br /&gt;
 20 | 21 | 22&lt;br /&gt;
(2 rows)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Konverze mezi hexadecimální a dekadickými čísly==&lt;br /&gt;
Dekadické číslo do hexadecimálního tvaru převedeme velice snadno - PostgreSQL nabízí funkci &amp;#039;&amp;#039;to_hex&amp;#039;&amp;#039;. O něco obtížnější je převod v opačném směru - z hexadecimální do dekadické soustavy. PostgreSQL má podporu pro tuto transformaci na úrovni parseru (tj. snadno lze transformovat konstanty), nenabízí ovšem žádnou funkci. Tu si ovšem můžeme poměrně snadno napsat:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION to_dec(text) &lt;br /&gt;
RETURNS integer AS $$&lt;br /&gt;
DECLARE r int; &lt;br /&gt;
BEGIN &lt;br /&gt;
  EXECUTE &amp;#039;SELECT x&amp;#039;||quote_literal($1)|| &amp;#039;::integer&amp;#039; INTO r; &lt;br /&gt;
  RETURN r; &lt;br /&gt;
END&lt;br /&gt;
$$ LANGUAGE plpgsql IMMUTABLE STRICT;&lt;br /&gt;
&lt;br /&gt;
postgres=# select to_dec(&amp;#039;ff&amp;#039;);&lt;br /&gt;
 to_dec &lt;br /&gt;
--------&lt;br /&gt;
    255&lt;br /&gt;
(1 row)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Ještě rychlejší kód bude s použitím jazyka SQL (Michael Glaesemann):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE FUNCTION hex2dec(in_hex TEXT) &lt;br /&gt;
RETURNS INT &lt;br /&gt;
IMMUTABLE STRICT LANGUAGE sql AS $body$&lt;br /&gt;
SELECT CAST(CAST((&amp;#039;x&amp;#039; || CAST($1 AS text)) AS bit(8)) AS INT);&lt;br /&gt;
$body$;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Získání defaultních hodnot sloupců tabulky==&lt;br /&gt;
Jedním z méně častých požadavků je získání výchozích hodnot atributů záznamu. Definice tabulky včetně výchozích hodnot je uložena v systémových tabulek, takže není problém se k těmto hodnotám dostat - jen je s nimi nutné nakládat opatrně - zvlášť s hodnotamy typu timestamp, které se používají k zachycení okamžiku skutečného přidání záznamu do tabulky a nikoliv okamžiku zobrazení formuláře: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION eval(varchar) &lt;br /&gt;
RETURNS varchar AS $$&lt;br /&gt;
DECLARE result varchar;&lt;br /&gt;
BEGIN&lt;br /&gt;
 EXECUTE &amp;#039;SELECT &amp;#039; || $1 INTO result;&lt;br /&gt;
 RETURN result;&lt;br /&gt;
END;$$ LANGUAGE plpgsql STRICT;&lt;br /&gt;
&lt;br /&gt;
CREATE OR REPLACE FUNCTION defaults(text, &lt;br /&gt;
                                    OUT attname name, OUT type varchar, OUT default_val varchar)&lt;br /&gt;
RETURNS SETOF RECORD AS $$&lt;br /&gt;
SELECT a.attname,&lt;br /&gt;
       pg_catalog.format_type(a.atttypid, a.atttypmod),&lt;br /&gt;
       (SELECT eval(pg_catalog.pg_get_expr(d.adbin, d.adrelid))&lt;br /&gt;
           FROM pg_catalog.pg_attrdef d&lt;br /&gt;
          WHERE d.adrelid = a.attrelid AND d.adnum = a.attnum AND a.atthasdef)&lt;br /&gt;
   FROM pg_catalog.pg_attribute a&lt;br /&gt;
  WHERE a.attrelid = $1::regclass::oid AND a.attnum &amp;gt; 0 AND NOT a.attisdropped&lt;br /&gt;
  ORDER BY a.attnum&lt;br /&gt;
$$ LANGUAGE SQL STRICT;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Číslování řádků po x-té==&lt;br /&gt;
Opět inspirace z blogu Rolanda Baumana. Při číslování lze použít tzv. session variables (globální proměnné). Ty zatím nejsou implementovány a jejich neexistence se obchází několika způsoby. Takže prvním problémem je jejich implementace, druhým umístění inicializační části. Až v Rolandově blogu jsem se seznámil s trikem, kdy se proměnné inicializují CROSS JOIN poddotazem. Jelikož tento dotaz vrací vždy jeden řádek, není ovlivněn ani výsledek dotazu a ani náročnost dotazu:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
create or replace function set_var(varchar, int) &lt;br /&gt;
returns int as $$&lt;br /&gt;
select set_config($1, $2::text, false)::int&lt;br /&gt;
$$ language sql;&lt;br /&gt;
&lt;br /&gt;
create or replace function add_var(varchar, int) &lt;br /&gt;
returns int as $$&lt;br /&gt;
select set_config($1, (current_setting($1)::int + $2)::text, false)::int&lt;br /&gt;
$$ language sql;&lt;br /&gt;
&lt;br /&gt;
postgres=# select add_var(&amp;#039;myvar.i&amp;#039;, 1) as num, * &lt;br /&gt;
              from (select length, title, description &lt;br /&gt;
                       from film &lt;br /&gt;
                            cross join &lt;br /&gt;
                            (select set_var(&amp;#039;myvar.i&amp;#039;, 0)) p --&amp;lt; Inicializace!!!!&lt;br /&gt;
                      order by length ) p &lt;br /&gt;
             offset 100 &lt;br /&gt;
             limit 10;&lt;br /&gt;
&lt;br /&gt;
 num | length |         title         |                                                    description                                                    &lt;br /&gt;
-----+--------+-----------------------+-------------------------------------------------------------------------------------------------------------------&lt;br /&gt;
 101 |     60 | PITY BOUND            | A Boring Panorama of a Feminist And a Moose who must Defeat a Database Administrator in Nigeria&lt;br /&gt;
 102 |     60 | ROOM ROMAN            | A Awe-Inspiring Panorama of a Composer And a Secret Agent who must Sink a Composer in A Shark Tank&lt;br /&gt;
 103 |     60 | SHAKESPEARE SADDLE    | A Fast-Paced Panorama of a Lumberjack And a Database Administrator who must Defeat a Madman in A MySQL Convention&lt;br /&gt;
 104 |     60 | SMILE EARRING         | A Intrepid Drama of a Teacher And a Butler who must Build a Pastry Chef in Berlin&lt;br /&gt;
 105 |     61 | BIRDS PERDITION       | A Boring Story of a Womanizer And a Pioneer who must Face a Dog in California&lt;br /&gt;
 106 |     61 | BULWORTH COMMANDMENTS | A Amazing Display of a Mad Cow And a Pioneer who must Redeem a Sumo Wrestler in The Outback&lt;br /&gt;
 107 |     61 | CAMELOT VACATION      | A Touching Character Study of a Woman And a Waitress who must Battle a Pastry Chef in A MySQL Convention&lt;br /&gt;
 108 |     61 | CASSIDY WYOMING       | A Intrepid Drama of a Frisbee And a Hunter who must Kill a Secret Agent in New Orleans&lt;br /&gt;
 109 |     61 | CHINATOWN GLADIATOR   | A Brilliant Panorama of a Technical Writer And a Lumberjack who must Escape a Butler in Ancient India&lt;br /&gt;
 110 |     61 | DRIFTER COMMANDMENTS  | A Epic Reflection of a Womanizer And a Squirrel who must Discover a Husband in A Jet Boat&lt;br /&gt;
(10 rows)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Prefix myvar musí být zaregistrován v postgresql.conf.&lt;br /&gt;
&lt;br /&gt;
==Výpočet percentilů a mediánu==&lt;br /&gt;
Určení percentilů a mediánu založeném na funkcích SUBSTRING_INDEX a GROUP_CONCAT je myšlenka [http://rpbouman.blogspot.com/2008/07/calculating-nth-percentile-in-mysql.html Rolanda Boumana]. Tato metoda je určitě rychlejší než klasické řešení založené na spojení nebo poddotazů a o něco horší než při použití analytických funkcí. Ovšem MySQL, stejně tak PostgreSQL nemají (červenec 2008) analytické funkce, takže jde o jedno z nejlepších dostupných řešení. PostgreSQL nemá funkci GROUP_CONCAT. Sic by bylo možné použít funkci array_accum, v té ale nemáme možnost určit pořadí. Na druhou stranu v PostgreSQL máme pole, které lze s výhodou využít - neobejdeme se ale bez zákaznických funkcí:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
create or replace function nth_percentil(anyarray, int) &lt;br /&gt;
returns anyelement as $$&lt;br /&gt;
  select $1[$2/100.0 * array_upper($1,1) + 1];&lt;br /&gt;
$$ language sql immutable strict;&lt;br /&gt;
&lt;br /&gt;
pagila=# select nth_percentil(array(select length from film order by 1),90);&lt;br /&gt;
 nth_percentil &lt;br /&gt;
---------------&lt;br /&gt;
           173&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
-- ověření&lt;br /&gt;
pagila=# select count(case when length &amp;lt; 173 then 1 end)::float / count(*) * 100.0 &lt;br /&gt;
            from film;&lt;br /&gt;
 ?column? &lt;br /&gt;
----------&lt;br /&gt;
     89.6&lt;br /&gt;
(1 row)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Podobný postup lze uplatnit i pro výpočet mediánu:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
create or replace function median(anyarray) &lt;br /&gt;
returns float as $$&lt;br /&gt;
  select ($1[array_upper($1,1)/2+1]::double precision + $1[(array_upper($1,1)+1) / 2]::double precision) / 2.0; &lt;br /&gt;
$$ language sql immutable strict;&lt;br /&gt;
&lt;br /&gt;
pagila=# select median(array[1,2]), median(array[1,2,3]), median(array[1,2,3,4]);&lt;br /&gt;
 median | median | median &lt;br /&gt;
--------+--------+--------&lt;br /&gt;
    1.5 |      2 |    2.5&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
pagila=# select median(array(select length from film order by 1));&lt;br /&gt;
 median &lt;br /&gt;
--------&lt;br /&gt;
    114&lt;br /&gt;
(1 row)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Správa indexů==&lt;br /&gt;
Na svém [http://people.planetpostgresql.org/xzilla/index.php?/archives/351-Index-pruning-techniques.html blogu] zveřejnil Robert Treat několik užitečných dotazů,které mohou pomoci při správě indexů:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- nepoužité indexy&lt;br /&gt;
select indexrelid::regclass as index, relid::regclass as table&lt;br /&gt;
   from pg_stat_user_indexes&lt;br /&gt;
        JOIN &lt;br /&gt;
        pg_index USING (indexrelid)&lt;br /&gt;
  where idx_scan = 0 and indisunique is false;&lt;br /&gt;
&lt;br /&gt;
-- duplicitní indexy, array_accum z http://www.postgresql.org/docs/8.3/static/xaggr.html&lt;br /&gt;
-- POZOR, NEBERE V POTAZ ROZDÍLY MEZI PODMÍNĚNÝMI INDEXY, NEMAZAT ZBRKLE!&lt;br /&gt;
select indrelid::regclass, array_accum(indexrelid::regclass)&lt;br /&gt;
   from pg_index&lt;br /&gt;
  group by indrelid, indkey&lt;br /&gt;
  having count(*) &amp;gt; 1;&lt;br /&gt;
&lt;br /&gt;
-- zbytečné indexy&lt;br /&gt;
select starelid::regclass, indexrelid::regclass, array_accum(staattnum), relpages, reltuples, array_accum(stadistinct)&lt;br /&gt;
   from pg_index&lt;br /&gt;
        join &lt;br /&gt;
        pg_statistic &lt;br /&gt;
        on starelid=indrelid and staattnum = ANY(indkey)&lt;br /&gt;
        join &lt;br /&gt;
        pg_class &lt;br /&gt;
        on indexrelid=oid&lt;br /&gt;
  where case &lt;br /&gt;
            when stadistinct &amp;lt; 0 then stadistinct &amp;gt; -.8 &lt;br /&gt;
            else reltuples/stadistinct &amp;gt; .2 end&lt;br /&gt;
    and not (indisunique or indisprimary)&lt;br /&gt;
    and (relpages &amp;gt; 100 or reltuples &amp;gt; 1000)&lt;br /&gt;
  group by starelid, indexrelid, relpages, reltuples&lt;br /&gt;
  order by starelid ;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Před zrušením indexu je dobré se zamyslet - všechny tyto dotazy použijte pro orientaci.&lt;br /&gt;
&lt;br /&gt;
==Zrcadlové otočení pole==&lt;br /&gt;
aneb ukázka nové funkce &amp;#039;&amp;#039;generate_subscripts&amp;#039;&amp;#039; (PostgreSQL 8.4):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
postgres=# create or replace function array_reverse(anyarray) &lt;br /&gt;
           returns anyarray as $$&lt;br /&gt;
             select array(&lt;br /&gt;
                          select $1[i] &lt;br /&gt;
                             from generate_subscripts($1,1,true) g(i))&lt;br /&gt;
           $$ language sql immutable strict;&lt;br /&gt;
CREATE FUNCTION&lt;br /&gt;
postgres=# select array_reverse(array[10,20,30]);&lt;br /&gt;
 array_reverse &lt;br /&gt;
---------------&lt;br /&gt;
 {30,20,10}&lt;br /&gt;
(1 row)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Výpočet rozdílu po sobě jdoucích hodnot pomocí korelovaného poddotazu a klauzule LIMIT==&lt;br /&gt;
Jednou z možností, jak získat rozdíl z po sobě jdoucích hodnot je použití následujícího korelovaného dotazu. Tento způsob výpočtu je vhodný pouze pro malou výslednou množinu (do sta). Pro větší množiny je výhodnější použití SELF JOINu nebo uložené procedury generující tabulku (viz [[Korelované vnořené dotazy]])- Autor: Gurjeet Singh:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
       ts          | size&lt;br /&gt;
-------------------+-----&lt;br /&gt;
 2002-03-16        | 11 &lt;br /&gt;
 2002-03-17        | 16 &lt;br /&gt;
 2002-03-18        | 18&lt;br /&gt;
 2002-03-19        | 12 &lt;br /&gt;
&lt;br /&gt;
select ts, size,&lt;br /&gt;
       t1.size - (select t2.size&lt;br /&gt;
                     from foo.view as t2&lt;br /&gt;
                    where t2.ts &amp;lt; t1.ts&lt;br /&gt;
                    order by ts desc&lt;br /&gt;
                    limit 1) as diff&lt;br /&gt;
   from foo.view as t1&lt;br /&gt;
  order by ts asc;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Překlad modulů na platformě Win32==&lt;br /&gt;
PostgreSQL je primárně vyvíjen na platformě Linux s překladačem gcc. I to je jeden z důvodů, že vývoj rozšiřujících modulů je komplikovanější na platformě win32 než v systémech typu UNIX. Starší verze jsou přeložené v systému MinGW. Verze 8.3 již může být přeložená v prostředí Microsoft Visual Studio. Základ v úspěchu je ve správném linkování. Pokud je PostgreSQL slinkován s knihovnou MSCVR80.DLL, pak i rozšiřující moduly musí být linkovány proti této knihovně. Dalším problémem je správné určení atributů funkce. Doporučuje se použít makro:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
#if defined(_MSC_VER) || defined(__MINGW32__)&lt;br /&gt;
#define COPYTEXT_EXPORT __declspec (dllexport)&lt;br /&gt;
#else&lt;br /&gt;
#define COPYTEXT_EXPORT&lt;br /&gt;
#endif&lt;br /&gt;
&lt;br /&gt;
COPYTEXT_EXPORT Datum copytext2(PG_FUNCTION_ARGS) {&lt;br /&gt;
   // blah blah&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Použití domény a plperlu pro validaci emailové adresy==&lt;br /&gt;
Emailovou adresu lze v PostgreSQL kontrolovat poměrně jednoduše pomocí regulárních výrazů. Komplexnější kontrolu umožňuje knihovna Email::Valid. Opět příklad je ukázkou obsáhlosti archivu CPAN. Požití domény je ovšem v tomto případě diskutabilní. V průběhu let se mění mx záznamy - uložené emailové adresy se mohou stát nevalidní (což je na jednu stranu správné, na druhou stranu toto chování může způsobit více problémů než užitku). Osobně bych doporučil používat funkci check_email explicitně, než implicitně. Automatické volání může jednak opravdu značně zpomalit import (kontroluje se validita mx záznamů) a obnovu ze zálohy, dále pak veškeré operace s proměnnými tohoto typu. &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION check_email(varchar)&lt;br /&gt;
RETURNS boolean AS $$&lt;br /&gt;
use strict;&lt;br /&gt;
use Email::Valid;&lt;br /&gt;
my $address = $_[0];&lt;br /&gt;
my $checks = {&lt;br /&gt;
   -address =&amp;gt; $address,&lt;br /&gt;
   -mxcheck =&amp;gt; 1,&lt;br /&gt;
   -tldcheck =&amp;gt; 1,&lt;br /&gt;
   -rfc822 =&amp;gt; 1,&lt;br /&gt;
};&lt;br /&gt;
if (defined Email::Valid-&amp;gt;address( %$checks )) {&lt;br /&gt;
    return &amp;#039;true&amp;#039;&lt;br /&gt;
}&lt;br /&gt;
elog(WARNING, &amp;quot;address failed $Email::Valid::Details check.&amp;quot;);&lt;br /&gt;
return &amp;#039;false&amp;#039;;&lt;br /&gt;
$$ LANGUAGE plperlu IMMUTABLE STRICT;&lt;br /&gt;
&lt;br /&gt;
postgres=# CREATE DOMAIN email AS varchar CHECK(check_email(value));&lt;br /&gt;
CREATE DOMAIN&lt;br /&gt;
postgres=# SELECT &amp;#039;pavel@&amp;#039;::email;&lt;br /&gt;
WARNING:  address failed rfc822 check.&lt;br /&gt;
postgres=# select &amp;#039;stehule@kix.fsv.cvut.cz&amp;#039;::email;&lt;br /&gt;
          email&lt;br /&gt;
-------------------------&lt;br /&gt;
 stehule@kix.fsv.cvut.cz&lt;br /&gt;
(1 row)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Autor: David Fetter&lt;br /&gt;
&lt;br /&gt;
==Akcelerace výběru intervalů splňujících podmínku starti &amp;lt;= x &amp;lt;= endi==&lt;br /&gt;
Dotazy pro výběr intervalu (interval je určen hodnotami ve sloupcích starti and endi) jsou ve větších tabulkách pomalé. Jádro pudla je v závislosti mezi sloupci starti a endi a dost často i mezi řádky. Řešením je použití prostorových indexů:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
postgres=# EXPLAIN ANALYZE SELECT * FROM testip WHERE 19999999 BETWEEN startip AND endip;&lt;br /&gt;
                           QUERY PLAN&lt;br /&gt;
----------------------------------------------------------------&lt;br /&gt;
 Seq Scan on testip  (cost=0.00..19902.00 rows=200814 width=12) (actual time=3.457..434.218 rows=1 loops=1)&lt;br /&gt;
   Filter: ((19999999 &amp;gt;= startip) AND (19999999 &amp;lt;= endip))&lt;br /&gt;
 Total runtime: 434.299 ms&lt;br /&gt;
(3 rows)&lt;br /&gt;
&lt;br /&gt;
Time: 435,865 ms&lt;br /&gt;
&lt;br /&gt;
postgres=# CREATE INDEX ggg ON testip USING gist ((box(point(startip,startip),point(endip,endip))) box_ops);&lt;br /&gt;
CREATE INDEX&lt;br /&gt;
Time: 75530,079 ms&lt;br /&gt;
postgres=# EXPLAIN ANALYZE &lt;br /&gt;
              SELECT * &lt;br /&gt;
                 FROM testip &lt;br /&gt;
                WHERE box(point(startip,startip),point(endip,endip)) @&amp;gt; box(point (19999999,19999999), point(19999999,19999999));&lt;br /&gt;
                                                                                                QUERY PLAN                                                            &lt;br /&gt;
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------&lt;br /&gt;
 Bitmap Heap Scan on testip  (cost=60.50..2550.14 rows=1000 width=12) (actual time=0.169..0.172 rows=1 loops=1)&lt;br /&gt;
   Recheck Cond: (box(point((startip)::double precision, (startip)::double precision), point((endip)::double precision, (endip)::double precision)) @&amp;gt; &amp;#039;(19999999,19999999),(19999999,19999999)&amp;#039;::box)&lt;br /&gt;
   -&amp;gt;  Bitmap Index Scan on ggg  (cost=0.00..60.25 rows=1000 width=0) (actual time=0.152..0.152 rows=1 loops=1)&lt;br /&gt;
         Index Cond: (box(point((startip)::double precision, (startip)::double precision), point((endip)::double precision, (endip)::double precision)) @&amp;gt; &amp;#039;(19999999,19999999),(19999999,19999999)&amp;#039;::box)&lt;br /&gt;
 Total runtime: 0.285 ms&lt;br /&gt;
(5 rows)&lt;br /&gt;
&lt;br /&gt;
Time: 2,805 ms&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Více na [[Indexace intervalů - období příp. rozsah ip pomocí prostorových indexů]].&lt;br /&gt;
&lt;br /&gt;
==Zkrácený zápis pro vložení obsahu proměnných typu row do tabulky==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
postgres=# CREATE TABLE foo(a integer, b integer);&lt;br /&gt;
CREATE TABLE&lt;br /&gt;
postgres=# CREATE OR REPLACE FUNCTION fx() &lt;br /&gt;
           RETURNS void as $$&lt;br /&gt;
             DECLARE r foo; &lt;br /&gt;
           BEGIN &lt;br /&gt;
             SELECT INTO r * FROM foo; &lt;br /&gt;
             INSERT INTO foo VALUES(r.*); &lt;br /&gt;
             RETURN; &lt;br /&gt;
           END; &lt;br /&gt;
           $$ LANGUAGE plpgsql;&lt;br /&gt;
CREATE FUNCTION&lt;br /&gt;
postgres=# SELECT fx();&lt;br /&gt;
 fx &lt;br /&gt;
----&lt;br /&gt;
 &lt;br /&gt;
(1 row)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==ALTER TABLE ALTER COLUMN USING==&lt;br /&gt;
Přiznám se, že klauzuli USING v ALTER COLUMN jsem neznal. Žil jsem v domnění, že při změně typu musí být hodnota přetypovatelná na noý typ. Což může být problém např. pro česky zapsané logické konstanty (tj. ano, ne). Pokud bych stávající hodnoty nepřevedl pouhým přetypováním, mohu použít klauzuli USING:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
postgres=# CREATE TABLE foo(a varchar);&lt;br /&gt;
CREATE TABLE&lt;br /&gt;
postgres=# INSERT INTO foo VALUES (&amp;#039;ano&amp;#039;);&lt;br /&gt;
INSERT 0 1&lt;br /&gt;
postgres=# ALTER TABLE foo ALTER COLUMN a TYPE boolean ;&lt;br /&gt;
ERROR:  column &amp;quot;a&amp;quot; cannot be cast to type &amp;quot;pg_catalog.bool&amp;quot;&lt;br /&gt;
postgres=# ALTER TABLE foo &lt;br /&gt;
              ALTER COLUMN a TYPE boolean &lt;br /&gt;
             USING CASE a &lt;br /&gt;
                       WHEN &amp;#039;ano&amp;#039; THEN true &lt;br /&gt;
                       ELSE false END;&lt;br /&gt;
ALTER TABLE&lt;br /&gt;
postgres=# SELECT * FROM foo;&lt;br /&gt;
 a &lt;br /&gt;
---&lt;br /&gt;
 t&lt;br /&gt;
(1 row)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Quote_ident pro dvojici schéma.název==&lt;br /&gt;
Vložení identifikátorů mezi uvozovky je jedním ze způsobů ochrany proti SQL injektáži. Funkce &amp;#039;&amp;#039;qoute_ident&amp;#039;&amp;#039; projede zadaný řetězec. Pokud zjistí, že řetězec obsahuje nějaký potenciálně nebezpečný řetězec, tak tento řetězec zapouzdří bezpečně do uvozovek, jinak ponechá vstupní řetězec beze změn. Problém nastává ve chvíli, kdy je parametrem kvalifikovaný identifikátor, protože:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
postgres=# select quote_ident(&amp;#039;public.foo&amp;#039;);&lt;br /&gt;
 quote_ident  &lt;br /&gt;
--------------&lt;br /&gt;
 &amp;quot;public.foo&amp;quot;&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
postgres=# select * from &amp;quot;public.foo&amp;quot;;&lt;br /&gt;
ERROR:  relation &amp;quot;public.foo&amp;quot; does not exist&lt;br /&gt;
postgres=# select * from public.&amp;quot;foo&amp;quot;;&lt;br /&gt;
 a &lt;br /&gt;
---&lt;br /&gt;
(0 rows)&lt;br /&gt;
&lt;br /&gt;
postgres=# select * from &amp;quot;public&amp;quot;.&amp;quot;foo&amp;quot;;&lt;br /&gt;
 a &lt;br /&gt;
---&lt;br /&gt;
(0 rows)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Toto omezení funkce quote_ident, lze celkem snadno obejít vlastní funkcí:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION quote_array(text[]) &lt;br /&gt;
RETURNS text AS $$&lt;br /&gt;
SELECT array_to_string(array(SELECT quote_ident($1[i]) &lt;br /&gt;
                                FROM generate_series(1, array_upper($1,1)) g(i)),&lt;br /&gt;
                       &amp;#039;.&amp;#039;) &lt;br /&gt;
$$ LANGUAGE SQL IMMUTABLE;&lt;br /&gt;
&lt;br /&gt;
CREATE OR REPLACE FUNCTION quote_schema_ident(text) &lt;br /&gt;
RETURNS text AS $$&lt;br /&gt;
SELECT quote_array(string_to_array($1,&amp;#039;.&amp;#039;))&lt;br /&gt;
$$ LANGUAGE SQL IMMUTABLE;&lt;br /&gt;
&lt;br /&gt;
postgres=# select quote_schema_ident(&amp;#039;public.foo tab&amp;#039;);&lt;br /&gt;
 quote_schema_ident &lt;br /&gt;
--------------------&lt;br /&gt;
 public.&amp;quot;foo tab&amp;quot;&lt;br /&gt;
(1 row)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Autor: [[Pavel Stěhule]].&lt;br /&gt;
&lt;br /&gt;
==Rychlé dohledání nejdelšího prefixu==&lt;br /&gt;
Dohledání nejdelšího prefixu z množiny známých prefixů je poměrně častá úloha. Typicky v oblasti komunikací. Na základě shody prefixu se obvykle určuje tarifní pásmo. Tuto úlohu jsem před několika lety řešil uloženou procedurou a modifikovaným algoritmem pro vyhledávání metodou půlení intervalu. Možná o něco pomalejší, nicméně o dost jednodušší je technika generování prefixů ze zadaného řetězce a přímé dohledání těchto prefixů (autor Hubert Lubaciewski - [http://www.depesz.com/index.php/2008/03/04/searching-for-longest-prefix/#more-1176 Depesz]).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--tabulka obsahující známé prefixy cca 5000&lt;br /&gt;
postgres=# select * from prefixesx limit 5;&lt;br /&gt;
   costcode_name    | costcode &lt;br /&gt;
--------------------+----------&lt;br /&gt;
 Afghanistan        | 93&lt;br /&gt;
 Afghanistan Mobile | 9370&lt;br /&gt;
 Afghanistan Mobile | 9379&lt;br /&gt;
 Afghanistan Mobile | 9380&lt;br /&gt;
 Alaska (USA)       | 1907&lt;br /&gt;
(5 rows)&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
Nejjednodušší a nejhorší řešení je použití LIKE:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
postgres=# SELECT * &lt;br /&gt;
              FROM prefixesx &lt;br /&gt;
             WHERE &amp;#039;420724181000&amp;#039; LIKE costcode || &amp;#039;%&amp;#039; &lt;br /&gt;
             ORDER BY length(costcode) DESC &lt;br /&gt;
             LIMIT 1;&lt;br /&gt;
          costcode_name          | costcode &lt;br /&gt;
---------------------------------+----------&lt;br /&gt;
 Czech Republic Mobile - EuroTel | 42072&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Další možností je vygenerovat z každého čísla pole prefixů a tyto prefixy přímo dohledávat:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION prefixes(varchar) &lt;br /&gt;
RETURNS varchar[] AS $$&lt;br /&gt;
SELECT ARRAY(SELECT substring($1 FROM 1 FOR i) &lt;br /&gt;
                FROM generate_series(1, length($1)) g(i))::varchar[]; &lt;br /&gt;
$$ LANGUAGE sql IMMUTABLE;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
a použít dotaz:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
postgres=# SELECT * &lt;br /&gt;
              FROM prefixesx &lt;br /&gt;
             WHERE costcode = ANY (prefixes(&amp;#039;420724191000&amp;#039;)) &lt;br /&gt;
             ORDER BY length(costcode) DESC &lt;br /&gt;
             LIMIT 1;&lt;br /&gt;
          costcode_name          | costcode &lt;br /&gt;
---------------------------------+----------&lt;br /&gt;
 Czech Republic Mobile - EuroTel | 42072&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Na zhruba 5000 řádcích, které představují aktuální ceník, je tento dotaz zhruba 4x rychlejší nežli LIKE (který se bude s rostoucím počtem prefixů zpomalovat).&lt;br /&gt;
&lt;br /&gt;
O něco pomalejší (cca 1ms) je dotaz vynucující si index s LIKE:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SELECT * &lt;br /&gt;
   FROM prefixesx&lt;br /&gt;
  WHERE &amp;#039;420724181000&amp;#039; LIKE costcode || &amp;#039;%&amp;#039;&lt;br /&gt;
    AND costcode LIKE substring(&amp;#039;420724191000&amp;#039; FROM 1 FOR 1) || &amp;#039;%&amp;#039;  &lt;br /&gt;
  ORDER BY length(costcode) DESC &lt;br /&gt;
  LIMIT 1;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
==Zjištění počtu výskytu subřetězce v řetězci==&lt;br /&gt;
Následující funkce je ukázkou docela již zlidovělého triku pro operace s řetězci a to použítí funkcí replace a length, přičemž vlastní úlohu řešíme bez cyklu - to je typické pro interprety, kde impementace vestavěných funkcí je rychlejší než iterace:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION CountInString(text,text)&lt;br /&gt;
RETURNS integer AS $$&lt;br /&gt;
 SELECT(Length($1) - Length(REPLACE($1, $2, &amp;#039;&amp;#039;))) / Length($2) ;&lt;br /&gt;
$$ LANGUAGE SQL IMMUTABLE;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Tuto funkci zaslal Rodrigo E. De León Plicet. Napadlo mne ještě další řešení a to s využítím polí:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION CountInString(text, text) &lt;br /&gt;
RETURNS integer AS $$&lt;br /&gt;
  SELECT Array_upper(String_to_array($1,$2),1) - 1;&lt;br /&gt;
$$ LANGUAGE SQL IMMUTABLE;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
[[SQL Triky II.|Starších dvacet triků]] [[SQL Triky III.|Novějších dvacet triků]]&lt;/div&gt;</summary>
		<author><name>imported&gt;Pavel</name></author>
	</entry>
</feed>