Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit a294726

Browse files
committed
Incorporate examples and doc patches from Mark Kirkwood and David Fetter.
1 parent 29e5833 commit a294726

File tree

3 files changed

+203
-5
lines changed

3 files changed

+203
-5
lines changed

doc/src/sgml/plpgsql.sgml

Lines changed: 180 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.55 2005/01/08 22:13:34 tgl Exp $
2+
$PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.56 2005/01/14 01:16:22 tgl Exp $
33
-->
44

55
<chapter id="plpgsql">
@@ -2304,6 +2304,32 @@ SELECT reffunc2();
23042304

23052305
FETCH ALL IN "&lt;unnamed cursor 1&gt;";
23062306
COMMIT;
2307+
</programlisting>
2308+
</para>
2309+
2310+
<para>
2311+
The following example shows one way to return multiple cursors
2312+
from a single function:
2313+
2314+
<programlisting>
2315+
CREATE FUNCTION myfunc(refcursor, refcursor) RETURNS SETOF refcursor AS $$
2316+
BEGIN
2317+
OPEN $1 FOR SELECT * FROM table_1;
2318+
RETURN NEXT $1;
2319+
OPEN $2 FOR SELECT * FROM table_2;
2320+
RETURN NEXT $2;
2321+
RETURN;
2322+
END;
2323+
$$ LANGUAGE plpgsql;
2324+
2325+
-- need to be in a transaction to use cursors.
2326+
BEGIN;
2327+
2328+
SELECT * FROM myfunc('a', 'b');
2329+
2330+
FETCH ALL FROM a;
2331+
FETCH ALL FROM b;
2332+
COMMIT;
23072333
</programlisting>
23082334
</para>
23092335
</sect3>
@@ -2585,8 +2611,6 @@ $emp_stamp$ LANGUAGE plpgsql;
25852611
CREATE TRIGGER emp_stamp BEFORE INSERT OR UPDATE ON emp
25862612
FOR EACH ROW EXECUTE PROCEDURE emp_stamp();
25872613
</programlisting>
2588-
2589-
25902614
</example>
25912615

25922616
<para>
@@ -2646,6 +2670,159 @@ AFTER INSERT OR UPDATE OR DELETE ON emp
26462670
FOR EACH ROW EXECUTE PROCEDURE process_emp_audit();
26472671
</programlisting>
26482672
</example>
2673+
2674+
<para>
2675+
One use of triggers is to maintain a summary table
2676+
of another table. The resulting summary can be used in place of the
2677+
original table for certain queries &mdash; often with vastly reduced run
2678+
times.
2679+
This technique is commonly used in Data Warehousing, where the tables
2680+
of measured or observed data (called fact tables) can be extremely large.
2681+
<xref linkend="plpgsql-trigger-summary-example"> shows an example of a
2682+
trigger procedure in <application>PL/pgSQL</application> that maintains
2683+
a summary table for a fact table in a data warehouse.
2684+
</para>
2685+
2686+
2687+
<example id="plpgsql-trigger-summary-example">
2688+
<title>A <application>PL/pgSQL</application> Trigger Procedure For Maintaining A Summary Table</title>
2689+
2690+
<para>
2691+
The schema detailed here is partly based on the <emphasis>Grocery Store
2692+
</emphasis> example from <emphasis>The Data Warehouse Toolkit</emphasis>
2693+
by Ralph Kimball.
2694+
</para>
2695+
2696+
<programlisting>
2697+
--
2698+
-- Main tables - time dimension and sales fact.
2699+
--
2700+
CREATE TABLE time_dimension (
2701+
time_key integer NOT NULL,
2702+
day_of_week integer NOT NULL,
2703+
day_of_month integer NOT NULL,
2704+
month integer NOT NULL,
2705+
quarter integer NOT NULL,
2706+
year integer NOT NULL
2707+
);
2708+
CREATE UNIQUE INDEX time_dimension_key ON time_dimension(time_key);
2709+
2710+
CREATE TABLE sales_fact (
2711+
time_key integer NOT NULL,
2712+
product_key integer NOT NULL,
2713+
store_key integer NOT NULL,
2714+
amount_sold numeric(12,2) NOT NULL,
2715+
units_sold integer NOT NULL,
2716+
amount_cost numeric(12,2) NOT NULL
2717+
);
2718+
CREATE INDEX sales_fact_time ON sales_fact(time_key);
2719+
2720+
--
2721+
-- Summary table - sales by time.
2722+
--
2723+
CREATE TABLE sales_summary_bytime (
2724+
time_key integer NOT NULL,
2725+
amount_sold numeric(15,2) NOT NULL,
2726+
units_sold numeric(12) NOT NULL,
2727+
amount_cost numeric(15,2) NOT NULL
2728+
);
2729+
CREATE UNIQUE INDEX sales_summary_bytime_key ON sales_summary_bytime(time_key);
2730+
2731+
--
2732+
-- Function and trigger to amend summarized column(s) on UPDATE, INSERT, DELETE.
2733+
--
2734+
CREATE OR REPLACE FUNCTION maint_sales_summary_bytime() RETURNS TRIGGER AS $maint_sales_summary_bytime$
2735+
DECLARE
2736+
delta_time_key integer;
2737+
delta_amount_sold numeric(15,2);
2738+
delta_units_sold numeric(12);
2739+
delta_amount_cost numeric(15,2);
2740+
BEGIN
2741+
2742+
-- Work out the increment/decrement amount(s).
2743+
IF (TG_OP = 'DELETE') THEN
2744+
2745+
delta_time_key = OLD.time_key;
2746+
delta_amount_sold = -1 * OLD.amount_sold;
2747+
delta_units_sold = -1 * OLD.units_sold;
2748+
delta_amount_cost = -1 * OLD.amount_cost;
2749+
2750+
ELSIF (TG_OP = 'UPDATE') THEN
2751+
2752+
-- forbid updates that change the time_key -
2753+
-- (probably not too onerous, as DELETE + INSERT is how most
2754+
-- changes will be made).
2755+
IF ( OLD.time_key != NEW.time_key) THEN
2756+
RAISE EXCEPTION 'Update of time_key : % -&gt; % not allowed', OLD.time_key, NEW.time_key;
2757+
END IF;
2758+
2759+
delta_time_key = OLD.time_key;
2760+
delta_amount_sold = NEW.amount_sold - OLD.amount_sold;
2761+
delta_units_sold = NEW.units_sold - OLD.units_sold;
2762+
delta_amount_cost = NEW.amount_cost - OLD.amount_cost;
2763+
2764+
ELSIF (TG_OP = 'INSERT') THEN
2765+
2766+
delta_time_key = NEW.time_key;
2767+
delta_amount_sold = NEW.amount_sold;
2768+
delta_units_sold = NEW.units_sold;
2769+
delta_amount_cost = NEW.amount_cost;
2770+
2771+
END IF;
2772+
2773+
2774+
-- Update the summary row with the new values.
2775+
UPDATE sales_summary_bytime
2776+
SET amount_sold = amount_sold + delta_amount_sold,
2777+
units_sold = units_sold + delta_units_sold,
2778+
amount_cost = amount_cost + delta_amount_cost
2779+
WHERE time_key = delta_time_key;
2780+
2781+
2782+
-- There might have been no row with this time_key (e.g new data!).
2783+
IF (NOT FOUND) THEN
2784+
BEGIN
2785+
INSERT INTO sales_summary_bytime (
2786+
time_key,
2787+
amount_sold,
2788+
units_sold,
2789+
amount_cost)
2790+
SELECT f.time_key,
2791+
sum(f.amount_sold),
2792+
sum(f.units_sold),
2793+
sum(f.amount_cost)
2794+
FROM sales_fact f
2795+
WHERE f.time_key = delta_time_key
2796+
GROUP BY f.time_key;
2797+
-- This query can potentially be very expensive if the trigger
2798+
-- is created on sales_fact without the time_key indexes.
2799+
-- Some care is needed to ensure that this situation does
2800+
-- *not* occur.
2801+
EXCEPTION
2802+
--
2803+
-- Catch race condition when two transactions are adding data
2804+
-- for a new time_key.
2805+
--
2806+
WHEN UNIQUE_VIOLATION THEN
2807+
UPDATE sales_summary_bytime
2808+
SET amount_sold = amount_sold + delta_amount_sold,
2809+
units_sold = units_sold + delta_units_sold,
2810+
amount_cost = amount_cost + delta_amount_cost
2811+
WHERE time_key = delta_time_key;
2812+
2813+
END;
2814+
END IF;
2815+
RETURN NULL;
2816+
2817+
END;
2818+
$maint_sales_summary_bytime$ LANGUAGE plpgsql;
2819+
2820+
CREATE TRIGGER maint_sales_summary_bytime
2821+
AFTER INSERT OR UPDATE OR DELETE ON sales_fact
2822+
FOR EACH ROW EXECUTE PROCEDURE maint_sales_summary_bytime();
2823+
</programlisting>
2824+
</example>
2825+
26492826
</sect1>
26502827

26512828
<!-- **** Porting from Oracle PL/SQL **** -->

doc/src/sgml/ref/alter_table.sgml

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.76 2005/01/10 00:04:43 tgl Exp $
2+
$PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.77 2005/01/14 01:16:52 tgl Exp $
33
PostgreSQL documentation
44
-->
55

@@ -537,6 +537,17 @@ ALTER TABLE distributors
537537
</programlisting>
538538
</para>
539539

540+
<para>
541+
To change an integer column containing UNIX timestamps to <type>timestamp
542+
with time zone</type> via a <literal>USING</literal> clause:
543+
<programlisting>
544+
ALTER TABLE foo
545+
ALTER COLUMN foo_timestamp TYPE timestamp with time zone
546+
USING
547+
timestamp with time zone 'epoch' + foo_timestamp * interval '1 second';
548+
</programlisting>
549+
</para>
550+
540551
<para>
541552
To rename an existing column:
542553
<programlisting>

doc/src/sgml/ref/create_opclass.sgml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<!--
2-
$PostgreSQL: pgsql/doc/src/sgml/ref/create_opclass.sgml,v 1.12 2003/11/29 19:51:38 pgsql Exp $
2+
$PostgreSQL: pgsql/doc/src/sgml/ref/create_opclass.sgml,v 1.13 2005/01/14 01:16:52 tgl Exp $
33
PostgreSQL documentation
44
-->
55

@@ -205,6 +205,16 @@ CREATE OPERATOR CLASS <replaceable class="parameter">name</replaceable> [ DEFAUL
205205
</para>
206206
</refsect1>
207207

208+
<refsect1>
209+
<title>Notes</title>
210+
211+
<para>
212+
The operators should not be defined by SQL functions. A SQL function
213+
is likely to be inlined into the calling query, which will prevent
214+
the optimizer from recognizing that the query matches an index.
215+
</para>
216+
</refsect1>
217+
208218
<refsect1>
209219
<title>Examples</title>
210220

0 commit comments

Comments
 (0)