<?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=Procedura_pro_generov%C3%A1n%C3%AD_kontingen%C4%8Dn%C3%AD_tabulky_v_PL%2FpgSQL</id>
	<title>Procedura pro generování kontingenční tabulky 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=Procedura_pro_generov%C3%A1n%C3%AD_kontingen%C4%8Dn%C3%AD_tabulky_v_PL%2FpgSQL"/>
	<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=Procedura_pro_generov%C3%A1n%C3%AD_kontingen%C4%8Dn%C3%AD_tabulky_v_PL/pgSQL&amp;action=history"/>
	<updated>2026-05-12T22:44:44Z</updated>
	<subtitle>Historie editací této stránky</subtitle>
	<generator>MediaWiki 1.43.3</generator>
	<entry>
		<id>http://postgres.cz/index.php?title=Procedura_pro_generov%C3%A1n%C3%AD_kontingen%C4%8Dn%C3%AD_tabulky_v_PL/pgSQL&amp;diff=433&amp;oldid=prev</id>
		<title>89.233.175.62 v 25. 8. 2008, 08:11</title>
		<link rel="alternate" type="text/html" href="http://postgres.cz/index.php?title=Procedura_pro_generov%C3%A1n%C3%AD_kontingen%C4%8Dn%C3%AD_tabulky_v_PL/pgSQL&amp;diff=433&amp;oldid=prev"/>
		<updated>2008-08-25T08:11:48Z</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;Donedávna jsem žil v omylu, že v PostgreSQL nelze napsat uloženou proceduru, která by vracela předem nedefinovanou tabulku. To proto, že v fakticky lze procedury v Pg implementovat pouze jako funkce - ty lze volat pouze z příkazu SELECT a u příkazu SELECT musí být již v době plánování jasné, jak bude vypadat výstupní tabulka (v podstatě jazyk SQL je v PostgreSQL přísně typový a statický). Mýlil jsem se - určitá možnost tu je, a tou je použití kurzoru. Příkaz FETCH slouží pouze k přesunu tabulky ze serveru na klienta, k ničemu jinému. A lze jej použít k zobrazení dynamicky vytvořeného obsahu. Jako příklad použiji generování kontingenční tabulky převzatý od Rolanda Baumana použitý v článku [[MySQL5:Jemný_úvod_do_uložených_procedur_MySQL5#Generování křížové sestavy|Jemný úvod do uložených procedur - generování křížové sestavy]].&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE TABLE employees (&lt;br /&gt;
  id serial PRIMARY KEY,&lt;br /&gt;
  shop_id int,&lt;br /&gt;
  gender char(1),&lt;br /&gt;
  name varchar(32),&lt;br /&gt;
  salary int&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
CREATE TABLE shops (&lt;br /&gt;
  id serial PRIMARY KEY,&lt;br /&gt;
  shop varchar(32)&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
INSERT INTO shops(shop) VALUES(&amp;#039;Zurich&amp;#039;),(&amp;#039;New York&amp;#039;),(&amp;#039;London&amp;#039;);&lt;br /&gt;
&lt;br /&gt;
INSERT INTO employees (shop_id, gender, name, salary)&lt;br /&gt;
VALUES &lt;br /&gt;
(1, &amp;#039;m&amp;#039;, &amp;#039;Jon Simpson&amp;#039;, 4500),&lt;br /&gt;
(1, &amp;#039;f&amp;#039;, &amp;#039;Barbara Breitenmoser&amp;#039;,4700),&lt;br /&gt;
(2, &amp;#039;f&amp;#039;, &amp;#039;KirstenRuegg&amp;#039;,5600),&lt;br /&gt;
(3, &amp;#039;m&amp;#039;, &amp;#039;Ralp Teller&amp;#039;,5100),&lt;br /&gt;
(3, &amp;#039;m&amp;#039;, &amp;#039;Peter Jonson&amp;#039;,4700);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Jádro procedury je v generování SELECTu podle vzoru:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SELECT shop, &lt;br /&gt;
		SUM(CASE gender WHEN &amp;#039;f&amp;#039; THEN salary ELSE 0 END) AS f, &lt;br /&gt;
		SUM(CASE gender WHEN &amp;#039;m&amp;#039; THEN salary ELSE 0 END) AS m, &lt;br /&gt;
		SUM(salary) AS total &lt;br /&gt;
	FROM employees INNER JOIN shops USING (shop_id)&lt;br /&gt;
	GROUP BY shop&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Vlastní funkce je zde - vrací pojmenovaný kurzor:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE OR REPLACE FUNCTION do_cross_cursor(dimx_name varchar, dimx_source varchar,&lt;br /&gt;
       				dimy_name varchar, dimy_source varchar,&lt;br /&gt;
				expr varchar)&lt;br /&gt;
RETURNS refcursor AS $$&lt;br /&gt;
DECLARE &lt;br /&gt;
  col_list text[] := &amp;#039;{}&amp;#039;;&lt;br /&gt;
  query text;&lt;br /&gt;
  r RECORD;&lt;br /&gt;
  result refcursor := &amp;#039;result&amp;#039;;&lt;br /&gt;
BEGIN&lt;br /&gt;
  FOR r IN EXECUTE &amp;#039;SELECT DISTINCT &amp;#039; || dimx_name || &amp;#039;::text AS val &amp;#039; || dimx_source &lt;br /&gt;
  LOOP&lt;br /&gt;
    col_list := array_append(col_list, &amp;#039;SUM(CASE &amp;#039; || dimx_name || &amp;#039; WHEN &amp;#039; &lt;br /&gt;
    	     			       || quote_literal(r.val) || &amp;#039; THEN &amp;#039; || expr || &amp;#039; ELSE 0 END) AS &amp;#039; &lt;br /&gt;
				       || quote_ident(r.val) || &amp;#039;&amp;#039;);&lt;br /&gt;
  END LOOP;&lt;br /&gt;
  query := &amp;#039;SELECT &amp;#039; || dimy_name || &amp;#039;, &amp;#039; || array_to_string(col_list, &amp;#039;,&amp;#039;) &lt;br /&gt;
  	   	     || &amp;#039;, SUM(&amp;#039; || expr || &amp;#039;) AS Total &amp;#039; || dimy_source || &amp;#039; GROUP BY &amp;#039; || dimy_name;&lt;br /&gt;
  OPEN result NO SCROLL FOR EXECUTE query;&lt;br /&gt;
  RETURN result;&lt;br /&gt;
END;&lt;br /&gt;
$$ LANGUAGE plpgsql STRICT;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Použití kurzorů je omezeno transakcí, musím tedy zapouzdřit funkci do explicitní transakce:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
BEGIN;&lt;br /&gt;
  SELECT do_cross_cursor(&amp;#039;gender&amp;#039;, &amp;#039;FROM employees&amp;#039;,&amp;#039;shop&amp;#039;,&amp;#039;FROM employees e JOIN shops s ON s.id = e.shop_id&amp;#039;, &amp;#039;salary&amp;#039;); &lt;br /&gt;
  FETCH ALL FROM result;&lt;br /&gt;
END;&lt;br /&gt;
&lt;br /&gt;
BEGIN;&lt;br /&gt;
  SELECT do_cross_cursor(&amp;#039;shop&amp;#039;, &amp;#039;FROM shops&amp;#039;,&amp;#039;gender&amp;#039;,&amp;#039;FROM employees e JOIN shops s ON s.id = e.shop_id&amp;#039;, &amp;#039;salary&amp;#039;); &lt;br /&gt;
  FETCH ALL FROM result;&lt;br /&gt;
END;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Výsledky:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
postgres=# BEGIN;&lt;br /&gt;
BEGIN&lt;br /&gt;
postgres=#   SELECT do_cross_cursor(&amp;#039;gender&amp;#039;, &amp;#039;FROM employees&amp;#039;,&amp;#039;shop&amp;#039;,&amp;#039;FROM employees e JOIN shops s ON s.id = e.shop_id&amp;#039;, &amp;#039;salary&amp;#039;);&lt;br /&gt;
 do_cross_cursor &lt;br /&gt;
-----------------&lt;br /&gt;
 result&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
postgres=#   FETCH ALL FROM result;&lt;br /&gt;
   shop   |  m   |  f   | total &lt;br /&gt;
----------+------+------+-------&lt;br /&gt;
 New York |    0 | 5600 |  5600&lt;br /&gt;
 Zurich   | 4500 | 4700 |  9200&lt;br /&gt;
 London   | 9800 |    0 |  9800&lt;br /&gt;
(3 rows)&lt;br /&gt;
&lt;br /&gt;
postgres=# END;BEGIN;&lt;br /&gt;
COMMIT&lt;br /&gt;
BEGIN&lt;br /&gt;
postgres=#   SELECT do_cross_cursor(&amp;#039;shop&amp;#039;, &amp;#039;FROM shops&amp;#039;,&amp;#039;gender&amp;#039;,&amp;#039;FROM employees e JOIN shops s ON s.id = e.shop_id&amp;#039;, &amp;#039;salary&amp;#039;);&lt;br /&gt;
 do_cross_cursor &lt;br /&gt;
-----------------&lt;br /&gt;
 result&lt;br /&gt;
(1 row)&lt;br /&gt;
&lt;br /&gt;
postgres=#   FETCH ALL FROM result;&lt;br /&gt;
 gender | New York | Zurich | London | total &lt;br /&gt;
--------+----------+--------+--------+-------&lt;br /&gt;
 m      |        0 |   4500 |   9800 | 14300&lt;br /&gt;
 f      |     5600 |   4700 |      0 | 10300&lt;br /&gt;
(2 rows)&lt;br /&gt;
&lt;br /&gt;
postgres=# END;&lt;br /&gt;
COMMIT&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>89.233.175.62</name></author>
	</entry>
</feed>