<?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=%C4%8Cast%C3%A9_chyby_v_PL%2FpgSQL</id>
	<title>Časté chyby v PL/pgSQL - Historie editací</title>
	<link rel="self" type="application/atom+xml" href="http://postgres.cz/index.php?action=history&amp;feed=atom&amp;title=%C4%8Cast%C3%A9_chyby_v_PL%2FpgSQL"/>
	<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=%C4%8Cast%C3%A9_chyby_v_PL/pgSQL&amp;action=history"/>
	<updated>2026-05-12T22:32:31Z</updated>
	<subtitle>Historie editací této stránky</subtitle>
	<generator>MediaWiki 1.43.3</generator>
	<entry>
		<id>http://postgres.cz/index.php?title=%C4%8Cast%C3%A9_chyby_v_PL/pgSQL&amp;diff=475&amp;oldid=prev</id>
		<title>imported&gt;Pavel v 26. 3. 2013, 11:16</title>
		<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=%C4%8Cast%C3%A9_chyby_v_PL/pgSQL&amp;diff=475&amp;oldid=prev"/>
		<updated>2013-03-26T11:16:41Z</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;Při auditu zdrojových kódů v PL/pgSQL se relativně často setkávám s následujícími programátorskými prohřešky:&lt;br /&gt;
&lt;br /&gt;
*nepoužívání vestavěných funkcí, C-čkový styl programování - v případě jakéhokoliv interpretovaného jazyku je zásadní používat vestavěné funkce. To platí pro PL/pgSQL dvojnásob. Snažte se zmenšit počet výrazů na rozumné minimum (snaha o absolutní minimum může být na škodu - runtime PL/pgSQL rozlišuje mezi jednoduchým výrazem a obecným výrazem. Jednoduché jsou vyhodnocovány v speciálním řádově rychlejším režimu).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- špatně&lt;br /&gt;
IF x1 IS NOT NULL THEN  &lt;br /&gt;
  s := s || x1 || &amp;#039;,&amp;#039;; &lt;br /&gt;
ELSE&lt;br /&gt;
  s := s || &amp;#039;NULL,&amp;#039;;&lt;br /&gt;
END IF;&lt;br /&gt;
IF x2 IS NOT NULL THEN&lt;br /&gt;
  s := s || x2 || &amp;#039;,&amp;#039;&lt;br /&gt;
ELSE &lt;br /&gt;
  s := s || &amp;#039;NULL,&amp;#039;;&lt;br /&gt;
END IF;&lt;br /&gt;
...&lt;br /&gt;
&lt;br /&gt;
-- správně&lt;br /&gt;
s := coalesce(x1 || &amp;#039;,&amp;#039;, &amp;#039;NULL,&amp;#039;) || coalesce(x2 || &amp;#039;,&amp;#039;,&amp;#039;NULL,&amp;#039;) || ...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Získávání měsíce, dnu, roku z datumové proměnné pomocí přetypování na text a výběrem podřetězce&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
--špatně&lt;br /&gt;
m = substring(current_date::text FROM 6 FOR 2)::int;&lt;br /&gt;
&lt;br /&gt;
-- lépe&lt;br /&gt;
m = substring(to_char(current_date, &amp;#039;YYYY-MM-DD&amp;#039;) FROM 6 FOR 2):: int;&lt;br /&gt;
&lt;br /&gt;
--správně&lt;br /&gt;
m = EXTRACT(month FROM current_date);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*&amp;#039;&amp;#039;Naprosto zásadní bezpečnostní chybou je nezabezpečené dynamické SQL&amp;#039;&amp;#039;:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- špatně&lt;br /&gt;
EXECUTE &amp;#039;SELECT &amp;#039; || column_name || &amp;#039; FROM &amp;#039; || table_name&lt;br /&gt;
           || &amp;#039; WHERE &amp;#039; || column_name || e&amp;#039;=\&amp;#039;&amp;#039; || some_variable || e&amp;#039;\&amp;#039;&amp;#039;&lt;br /&gt;
  INTO var;&lt;br /&gt;
&lt;br /&gt;
-- správně&lt;br /&gt;
EXECUTE &amp;#039;SELECT &amp;#039; || quote_ident(column_name) || &amp;#039; FROM &amp;#039; || quote_ident(table_name)&lt;br /&gt;
           || &amp;#039; WHERE &amp;#039; || quote_ident(column_name) || &amp;#039;=&amp;#039; || quote_literal(some_variable)&lt;br /&gt;
  INTO var;&lt;br /&gt;
&lt;br /&gt;
-- správně pro 8.4 a novější&lt;br /&gt;
EXECUTE &amp;#039;SELECT &amp;#039; || quote_ident(column_name) || &amp;#039; FROM &amp;#039; || table_name::regclass&lt;br /&gt;
           || &amp;#039; WHERE &amp;#039; || quote_ident(column_name) || &amp;#039;= $1&amp;#039;&lt;br /&gt;
  INTO var&lt;br /&gt;
  USING some_variable&lt;br /&gt;
&lt;br /&gt;
-- špatně&lt;br /&gt;
CREATE OR REPLACE FUNCTION create_table(schemaname varchar, tablename varchar)&lt;br /&gt;
RETURNS void AS $$&lt;br /&gt;
BEGIN&lt;br /&gt;
  EXECUTE &amp;#039;CREATE TABLE &amp;#039; || coalesce(schemaname || &amp;#039;.&amp;#039;,&amp;#039;&amp;#039;) || tablename;&lt;br /&gt;
  RETRUN;&lt;br /&gt;
END;&lt;br /&gt;
$$ LANGUAGE plpgsql;&lt;br /&gt;
&lt;br /&gt;
-- správně&lt;br /&gt;
CREATE OR REPLACE FUNCTION create_table(schemaname varchar, tablename varchar)&lt;br /&gt;
RETURNS void AS $$&lt;br /&gt;
BEGIN&lt;br /&gt;
  EXECUTE &amp;#039;CREATE TABLE &amp;#039; || coalesce(quote_ident(schemaname) || &amp;#039;.&amp;#039;,&amp;#039;&amp;#039;) || quote_ident(tablename);&lt;br /&gt;
  RETURN;&lt;br /&gt;
END;&lt;br /&gt;
$$ LANGUAGE plpgsql;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*Drobnější, nicméně častou chybou je generování intervalu z čísla pomocí přetypování:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- neefektivně&lt;br /&gt;
x := (d || &amp;#039; days&amp;#039;)::interval;&lt;br /&gt;
&lt;br /&gt;
-- efektivně&lt;br /&gt;
x := d * interval &amp;#039;1 day&amp;#039;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Konečně - zbytečné použití explicitních kurzorů &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- zbytečně složité&lt;br /&gt;
DECLARE &lt;br /&gt;
  curs CURSOR FOR SELECT * FROM tenk1;&lt;br /&gt;
  r record;&lt;br /&gt;
BEGIN&lt;br /&gt;
  open curs;&lt;br /&gt;
  FETCH curs INTO r;&lt;br /&gt;
  LOOP&lt;br /&gt;
    EXIT WHEN NOT found;&lt;br /&gt;
    ...&lt;br /&gt;
    FETCH curs INTO r;&lt;br /&gt;
  END LOOP;&lt;br /&gt;
  close curs;&lt;br /&gt;
END;&lt;br /&gt;
&lt;br /&gt;
-- jednodušeji&lt;br /&gt;
DECLARE r record;&lt;br /&gt;
BEGIN&lt;br /&gt;
  FOR r IN &lt;br /&gt;
          SELECT * FROM tenk1&lt;br /&gt;
  LOOP&lt;br /&gt;
    ...&lt;br /&gt;
  END LOOP;&lt;br /&gt;
END;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*Nevhodné použití PL/pgSQL - PL/pgSQL je nevhodný jazyk pro iterace s velkým počtem cyklů obsahující modifikaci polí nebo řetězců. Např. funkce pro seřazení pole v PL/pgSQL je šikovná pro výuku PL/pgSQL ale naprosto nevhodná pro produkční prostředí. Tam bude řádově rychlejší funkce v SQL právě proto, že používá vestavěnou funkcionalitu PostgreSQL.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&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;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* Zbytečné použití SELECT INTO, zbytečné použití PL/pgSQL&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- špatně&lt;br /&gt;
CREATE OR REPLACE FUNCTION foo(a float)&lt;br /&gt;
RETURNS float AS $$&lt;br /&gt;
DECLARE result float;&lt;br /&gt;
BEGIN&lt;br /&gt;
  SELECT 0.23 * a INTO result;&lt;br /&gt;
  return result;&lt;br /&gt;
END;&lt;br /&gt;
$$ LANGUAGE plpgsql;&lt;br /&gt;
&lt;br /&gt;
-- lépe&lt;br /&gt;
CREATE OR REPLACE FUNCTION foo(a float)&lt;br /&gt;
RETURNS float AS $$&lt;br /&gt;
BEGIN&lt;br /&gt;
  RETURN 0.23 * a;&lt;br /&gt;
END;&lt;br /&gt;
$$ LANGUAGE plpgsql;&lt;br /&gt;
&lt;br /&gt;
-- správně -- použít SQL proceduru&lt;br /&gt;
CREATE OR REPLACE FUNCTION foo(a float)&lt;br /&gt;
RETURNS float AS $$&lt;br /&gt;
  SELECT 0.23 * $1;&lt;br /&gt;
$$ LANGUAGE sql;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
*Jiný příklad na stejné téma - hledání minim a rychlost pro 100 000 volání:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- špatně (210 ms)&lt;br /&gt;
CREATE OR REPLACE FUNCTION min1(a int, b int)&lt;br /&gt;
RETURNS int AS $$&lt;br /&gt;
BEGIN&lt;br /&gt;
  IF a &amp;lt; b THEN&lt;br /&gt;
    RETURN a;&lt;br /&gt;
  ELSE&lt;br /&gt;
    RETURN b;&lt;br /&gt;
  END IF;&lt;br /&gt;
END;&lt;br /&gt;
$$ LANGUAGE plpgsql STRICT IMMUTABLE;&lt;br /&gt;
&lt;br /&gt;
-- lépe, ale stále špatně (190 ms)&lt;br /&gt;
CREATE OR REPLACE FUNCTION min1(a int, b int)&lt;br /&gt;
RETURNS int AS $$&lt;br /&gt;
BEGIN&lt;br /&gt;
  RETURN least(a,b);&lt;br /&gt;
END;&lt;br /&gt;
$$ LANGUAGE plpgsql STRICT IMMUTABLE;&lt;br /&gt;
&lt;br /&gt;
-- lépe (30 ms)&lt;br /&gt;
CREATE OR REPLACE FUNCTION min2(a int, b int)&lt;br /&gt;
RETURNS int AS $$&lt;br /&gt;
SELECT CASE WHEN $1 &amp;lt; $2 THEN $1 ELSE $2 END;&lt;br /&gt;
$$ LANGUAGE sql;&lt;br /&gt;
&lt;br /&gt;
-- nejrychlejší varianta (20 ms)&lt;br /&gt;
CREATE OR REPLACE FUNCTION min3(a int, b int)&lt;br /&gt;
RETURNS int AS $$&lt;br /&gt;
SELECT least($1,$2);&lt;br /&gt;
$$ LANGUAGE sql;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
PL/pgSQL není optimalizovaný na rekurzivní volání - nepoužívejte PL/pgSQL na hluboká rekurzivní volání, používejte nerekurzivní algoritmy&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION public.psqlfibnr(n 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 &lt;br /&gt;
  prev1 int = 0;&lt;br /&gt;
  prev2 int = 1;&lt;br /&gt;
  result int = 0;&lt;br /&gt;
BEGIN&lt;br /&gt;
  FOR i IN 1..n&lt;br /&gt;
  LOOP&lt;br /&gt;
    result := prev1 + prev2;&lt;br /&gt;
    prev2 := prev1;&lt;br /&gt;
    prev1 := result;&lt;br /&gt;
  END LOOP;&lt;br /&gt;
  RETURN result;&lt;br /&gt;
END;&lt;br /&gt;
$function$&lt;br /&gt;
&lt;br /&gt;
CREATE OR REPLACE FUNCTION public.psqlfibr(n 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 n &amp;lt; 2 THEN&lt;br /&gt;
    RETURN n;&lt;br /&gt;
  END IF;&lt;br /&gt;
  RETURN psqlfib(n-1) + psqlfib(n-2);&lt;br /&gt;
END;&lt;br /&gt;
$function$&lt;br /&gt;
&lt;br /&gt;
postgres=# select n, psqlfibnr(n)&lt;br /&gt;
              from generate_series(0,35,5) as n;&lt;br /&gt;
 n  | psqlfibnr &lt;br /&gt;
----+-----------&lt;br /&gt;
  0 |         0&lt;br /&gt;
  5 |         5&lt;br /&gt;
 10 |        55&lt;br /&gt;
 15 |       610&lt;br /&gt;
 20 |      6765&lt;br /&gt;
 25 |     75025&lt;br /&gt;
 30 |    832040&lt;br /&gt;
 35 |   9227465&lt;br /&gt;
(8 rows)&lt;br /&gt;
&lt;br /&gt;
Time: 1.178 ms&lt;br /&gt;
&lt;br /&gt;
postgres=# select n, psqlfibr(n)                                                                                                                                                                                                               &lt;br /&gt;
              from generate_series(0,35,5) as n;                                                                                                                                                                                                 &lt;br /&gt;
 n  | psqlfib                                                                                                                                                                                                                                 &lt;br /&gt;
----+---------                                                                                                                                                                                                                                &lt;br /&gt;
  0 |       0                                                                                                                                                                                                                                 &lt;br /&gt;
  5 |       5                                                                                                                                                                                                                                 &lt;br /&gt;
 10 |      55                                                                                                                                                                                                                                 &lt;br /&gt;
 15 |     610                                                                                                                                                                                                                                 &lt;br /&gt;
 20 |    6765                                                                                                                                                                                                                                 &lt;br /&gt;
 25 |   75025                                                                                                                                                                                                                                 &lt;br /&gt;
 30 |  832040                                                                                                                                                                                                                                 &lt;br /&gt;
 35 | 9227465                                                                                                                                                                                                                                 &lt;br /&gt;
(8 rows)                                                                                                                                                                                                                                      &lt;br /&gt;
                                                                                                                                                                                                                                              &lt;br /&gt;
Time: 282992.820 ms              &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>imported&gt;Pavel</name></author>
	</entry>
</feed>