3

I currently have a parent table:

CREATE TABLE members (
    member_id SERIAL NOT NULL, UNIQUE, PRIMARY KEY
    first_name varchar(20)
    last_name varchar(20)
    address address (composite type)
    contact_numbers varchar(11)[3]
    date_joined date
    type varchar(5)
);

and two related tables:

CREATE TABLE basic_member (
    activities varchar[3])
    INHERITS (members)
);

CREATE TABLE full_member ( 
    activities varchar[])
    INHERITS (members)
);

If the type is full the details are entered to the full_member table or if type is basic into the basic_member table. What I want is that if I run an update and change the type to basic or full the tuple goes into the corresponding table.

I was wondering if I could do this with a rule like:

 CREATE RULE tuple_swap_full
 AS ON UPDATE TO full_member
 WHERE new.type = 'basic'
 INSERT INTO basic_member VALUES (old.member_id, old.first_name, old.last_name,
 old.address, old.contact_numbers, old.date_joined, new.type, old.activities);

... then delete the record from the full_member

Just wondering if my rule is anywhere near or if there is a better way.

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
Danny Calladine
  • 321
  • 1
  • 2
  • 13

1 Answers1

1
  • You don't need

    member_id SERIAL NOT NULL, UNIQUE, PRIMARY KEY
    

    A PRIMARY KEY implies UNIQUE NOT NULL automatically:

    member_id SERIAL PRIMARY KEY
    
  • I wouldn't use hard coded max length of varchar(20). Just use text and add a check constraint if you really must enforce a maximum length. Easier to change around.

  • Syntax for INHERITS is mangled. The key word goes outside the parens around columns.

    CREATE TABLE full_member ( 
        activities text[]
    ) INHERITS (members);
    
  • Table names are inconsistent (members <-> member). I use the singular form everywhere in my test case.

  • Finally, I would not use a RULE for the task. A trigger AFTER UPDATE seems preferable.

Consider the following

Test case:

Tables:

CREATE SCHEMA x;  -- I put everything in a test schema named "x".

-- DROP TABLE x.members CASCADE;
CREATE TABLE x.member (
     member_id SERIAL PRIMARY KEY
    ,first_name text
    -- more columns ...
    ,type text);

CREATE TABLE x.basic_member (
    activities text[3]
) INHERITS (x.member);

CREATE TABLE x.full_member ( 
    activities text[]
) INHERITS (x.member);

Trigger function:

Data-modifying CTEs (WITH x AS ( DELETE ..) are the best tool for the purpose. Requires PostgreSQL 9.1 or later.
For older versions, first INSERT then DELETE.

CREATE OR REPLACE FUNCTION x.trg_move_member()
  RETURNS trigger AS
$BODY$
BEGIN

CASE NEW.type
WHEN 'basic' THEN
    WITH x AS (
        DELETE FROM x.member
        WHERE member_id = NEW.member_id
        RETURNING *
        )
    INSERT INTO x.basic_member (member_id, first_name, type) -- more columns
    SELECT member_id, first_name, type -- more columns
    FROM   x;

WHEN 'full' THEN
    WITH x AS (
        DELETE FROM x.member 
        WHERE member_id = NEW.member_id
        RETURNING *
        )
    INSERT INTO x.full_member (member_id, first_name, type) -- more columns
    SELECT member_id, first_name, type -- more columns
    FROM   x;
END CASE;

RETURN NULL;

END;
$BODY$
  LANGUAGE plpgsql VOLATILE;

Trigger:

Note that it is an AFTER trigger and has a WHEN condition. WHEN condition requires PostgreSQL 9.0 or later. For earlier versions, you can just leave it away, the CASE statement in the trigger itself takes care of it.

CREATE TRIGGER up_aft
  AFTER UPDATE
  ON x.member
  FOR EACH ROW
  WHEN (NEW.type IN ('basic ','full')) -- OLD.type cannot be IN ('basic ','full')
  EXECUTE PROCEDURE x.trg_move_member();

Test:

INSERT INTO x.member (first_name, type) VALUES ('peter', NULL);

UPDATE x.member SET type = 'full' WHERE first_name = 'peter';
SELECT * FROM ONLY x.member;
SELECT * FROM x.basic_member;
SELECT * FROM x.full_member;
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
  • 1
    Totally agree on using a trigger instead of a rule. Rules have a lot of counter-intuitive behaviors and are more likely to burn you in surprising ways than triggers. I anyone is wondering why they would include a redundant `WHEN` clause -- by dodging the overhead of firing the trigger it helps performance a little. My preferences on NOT NULL for primary key columns and varchar(n) versus text with constraint match the question, not the answer, but those points have reasonable arguments on both sides. – kgrittn Apr 22 '12 at 15:51
  • Thank you for your answer but it doesn't work the Fuction and Trigger can be entered without error but it doesn't work, I can get to the part where i enter a member into x.member that works, but when I run the update i get UPDATE 0 , which obviously means nothing happens.and it doesn't update the x.member table or move it to the x.full_member table :( – Danny Calladine Apr 23 '12 at 11:48
  • as well im looking to do the edits on either the basic_member table or full_member table. as i add member by either using INSERT INTO basic_member VALUES (ect..) if they are basic member and INSERT INTO full_member VALUES (ect...) if they are full. I just need them to swap if i change type in the basic table to full type or change full to basic in the full table – Danny Calladine Apr 23 '12 at 11:50
  • @DannyCalladine: There must be a misunderstanding. I tested the code in PostgreSQL 9.1 and it works as per your original question - you only mentioned UPDATE. I would consider to prevent non-matching type in INSERT altogether (add a trigger BEFORE INSERT). And add similar triggers for AFTER UPDATE on the child tables. – Erwin Brandstetter Apr 23 '12 at 12:28
  • i manag to get it to work basically, i change the first x.member into x.full_member and the second x.member to x.basic_member. then create the same tirgger but the both table instad of just the x.member table. the only thing im wondering now is how i get this to work without the schema or is the schema a must have. – Danny Calladine Apr 23 '12 at 13:15
  • @DannyCalladine: The schema is just for testing. Remove the schema-qualification `x.` to create / use objects in default schema. – Erwin Brandstetter Apr 23 '12 at 14:01