How-to suggestions to views

From: KÖPFERL Robert <robert(dot)koepferl(at)sonorys(dot)at>
To: pgsql-sql(at)postgresql(dot)org
Subject: How-to suggestions to views
Date: 2005-06-10 10:12:43
Message-ID: ED4E30DD9C43D5118DFB00508BBBA76EB16759@neptun.sonorys.at
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-sql

Hi,

in the course of my investigation on how to agglomerate or concat several
tables using a view or functions the following little HOW-TO felt out. It is
kind of a full fledged example of how to coalesce two tables using a view.
However it is as it is and I am more less new to writing rules and thus
making faults. As the PG-doc says: There's a mega example but not some
simple so I had a try in making something simple.
So short story long: I want sou to have a look at my SQL-style example and
either learn from it and/or give comments and suggestions (what didn't I
see, since I am beginner and what hidden issues exist, what did I interpret
wrong)

textfile
----------------8<--------------------------------
HOW-TO concatinate two tables in Postgres using a view and rules

This is a full-example of how to agglomerate two tables, connected via a 1:1
relation into one view on which DELETE, UPDATE and INSERT can be used.
So our goal is to have two tables like id|a|b and id|x|y coalesced into
one
table or view id|a|b|x|y which by itself allows INSERT, UPDATE and DELETE.

-- We start with creation of the tables
-- In order to connect them afterwards, we need a primary key and some
columns:

CREATE TABLE tbla
(
id int4 NOT NULL,
a int4,
b varchar(12),
CONSTRAINT tbla_pk PRIMARY KEY (id)
)
WITHOUT OIDS;

-- One table is kind of master table, whilst the other(s) are/is
-- slave table. This just means that the master table defines what
-- (new) values are valid for the primary key and thus for the
-- foreign keys of the slave tables.
-- So a second table will at least have the same key as the master table.
-- On the one hand as foreign key to allow just values in tbla and to
-- retain referential integrity and on the other hand as primary key to
-- keep values unique and thus make a 1:n relation become a 1:1 relation:

CREATE TABLE tblb
(
id int4 NOT NULL,
x bool,
y timestamp,
CONSTRAINT tblb_pk PRIMARY KEY (id),
CONSTRAINT tblb_fk FOREIGN KEY (id) REFERENCES tbla (id) ON UPDATE CASCADE
ON DELETE CASCADE
)
WITHOUT OIDS;

-- Note that we specified ON DELETE CASCADE. This will get us handy, later

-- Now let's already test our new tables plus their constraints by inserting
some records:

INSERT INTO tbla VALUES ( 3, 9034, 'F dabiu' );
INSERT INTO tbla VALUES ( 6, -23, 'Moosi llap' );

-- we have to use same keys and qty 0..1
INSERT INTO tblb VALUES ( 3, false, now() );
INSERT INTO tblb VALUES ( 6, true, now() );

-- Now we can create such a agglomerating view. However it will just allow
-- data to be viewed - maybe thus the name.

CREATE OR REPLACE VIEW a_and_b AS
SELECT tbla.id, tbla.a, tbla.b, tblb.x, tblb.y
FROM tbla
NATURAL LEFT OUTER JOIN tblb;

-- This creates a new 'table' (internally Postgres views are tables with no
data
-- but a bunch of rules) with all the columns we expected earlier.
-- Note that since we used the name id in both tables, we could use a
NATURAL
-- JOIN. Depending on your column names and intentions, other joins are
required.
-- As you can see, this resembles just the concatenated table we had in
mind:

SELECT * FROM a_and_b;

-- In order to make INSERT, UPDATE and DELETE work o the view, the next step
-- is to define some rules. Note that there already exists one rule: The
-- 'standard-rule' named _RETURN. This makes a table to a view in Postgres.
-- Since the SELECT is covered by a rule, no actual data is required.
-- Let's start with a rule for inserting records:

CREATE OR REPLACE RULE a_b_insert AS
ON INSERT TO a_and_b DO INSTEAD (
INSERT INTO tbla (id, a, b) VALUES (new.id, new.a, new.b);
INSERT INTO tblb (id, x, y) VALUES (new.id, new.x, new.y);
);

-- What we do here is: Instead of issuing the users insert, we issue two of
-- our own inserts. Thereby reusing values we got from the users insert.
-- This is the pseudo relation NEW. NEW has the same structure as the view
-- the rule is written for. The two INSERTs (or whatever other
SQL-statements)
-- are coalesced by putting them into parentheses. Note that coming from our

-- definition, we have to fill tbla before tblb.

-- So it's now possible to insert records via tha view:

INSERT INTO a_and_b VALUES (99, 123, 'text', false, now() );
SELECT * FROM a_and_b WHERE id=99;

-- In order to be able to also delete records, a delete-rule is needed
-- This is also a INSTEAD-rule, since it is intended to be a rather
-- generic rule (any DELETE-WHERE-clause shall be possible), there are
-- no restricting expressions and it is a INSTEAD but not a ALSO-rule.
-- So in this case we want to delete records from just tbla. Since we
enabled
-- DELETE CASCADE Postgres will clean all dependent records in tblb as well.
-- One could think that the WHERE clause of the view's DELETE will be
applied
-- to this rule as well, but that's not true. a 'DELETE FROM tbla' ends up
-- in purging all records. So to restrict the rule's DELETE we can make use
-- of the pseudo relation OLD. This relation has the same form as the view
-- and delivers us the terms given in a WHERE-clause. That leads us to:

CREATE OR REPLACE RULE a_and_b_del AS
ON DELETE TO a_and_b DO INSTEAD
DELETE FROM tbla WHERE tbla.id = OLD.id;

-- which now allows us to safely delete selected records:

DELETE FROM a_and_b WHERE id=99;
SELECT * FROM a_and_b WHERE id=99;

-- Last but not least there's still the UPDATE to the view. A rule is
required,
-- too. It works almost the same pattern as before. Since a update is like a
-- combination of DELETE and INSERT, there exist two pseudo relations: OLD
and NEW.
-- One can refer to the unchanged record, to terms of the current statement
and
-- to the user's new values (he wants to set). In our case, we do only need
the
-- NEW-relation. As at the INSERT rule before, we coalesce several
SQL-statements
-- by using parentheses. This is again a 'default' and INSTEAD-rule. Instead
-- of the big update, we do two updates on each table. Thereby restricting
-- updates again of using just the primary key:

CREATE OR REPLACE RULE a_and_b_upd AS
ON UPDATE TO a_and_b DO INSTEAD
(
UPDATE tbla SET a = new.a, b = new.b WHERE tbla.id = new.id;
UPDATE tblb SET x = new.x, y = new.y WHERE tblb.id = new.id;
);

UPDATE a_and_b SET a=155, b='t1e5xt', x=false, y='2005-6-6' WHERE id=1;
SELECT * FROM a_and_b;

-- Note that this may fail if not all slave records exist. If for example
-- there exists just a master record for id=1
INSERT INTO tbla VALUES ( 1, -12399, 'solo master' );
-- a UPDATE on the view will update or try to update both tables, it will
-- have success however in jus one table and thus the second half columns
-- do not get set:

UPDATE a_and_b SET a=-333, b='neotext', x=false, y='2005-6-6' WHERE id=1;
SELECT * FROM a_and_b;

-- The values for x and y are lost.

SELECT * FROM a_and_b;

-- Maybe in this case it is more recommendable to define the view not as
-- with LEFT OUTER JOIN but with a normal equal join. This is up to you.

Browse pgsql-sql by date

  From Date Subject
Next Message Richard Huxton 2005-06-10 10:16:43 Re: [SQL] Permission denied for language pltclu
Previous Message Dinesh Pandey 2005-06-10 09:51:46 Re: [SQL] Permission denied for language pltclu