PostgreSQL - Insert data into multiple tables simultaneously - sql

I am having a data insertion problem in tables linked by foreign key. I have read in some places that there is a "with" command that helps in these situations, but I do not quite understand how it is used.
I would like to put together four tables that will be used to make a record, however, that all the data were inserted at once, in a single query, and that they were associated with the last table, to facilitate future consultations. Here is the code for creating the tables:
CREATE TABLE participante
(
id serial NOT NULL,
nome character varying(56) NOT NULL,
CONSTRAINT participante_pkey PRIMARY KEY (id),
);
CREATE TABLE venda
(
id serial NOT NULL,
inicio date NOT NULL,
CONSTRAINT venda_pkey PRIMARY KEY (id)
);
CREATE TABLE item
(
id serial NOT NULL,
nome character varying(256) NOT NULL,
CONSTRAINT item_pkey PRIMARY KEY (id)
);
CREATE TABLE lances_vendas
(
id serial NOT NULL,
venda_id integer NOT NULL,
item_id integer NOT NULL,
participante_id integer NOT NULL,
valor numeric NOT NULL,
CONSTRAINT lance_vendas_pkey PRIMARY KEY (id),
CONSTRAINT lances_vendas_venda_id_fkey FOREIGN KEY (venda_id)
REFERENCES venda (id),
CONSTRAINT lances_vendas_item_id_fkey FOREIGN KEY (item_id)
REFERENCES item (id),
CONSTRAINT lances_vendas_participante_id_fkey FOREIGN KEY (participante_id)
REFERENCES participante (id)
);

The idea is to write WITH clauses that contain INSERT ... RETRUNING to return the generated keys. Then these “views for a single query” can be used to insert those keys into the referencing tables.
WITH par_key AS
(INSERT INTO participante (nome) VALUES ('Laurenz') RETURNING id),
ven_key AS
(INSERT INTO venda (inicio) VALUES (current_date) RETURNING id),
item_key AS
(INSERT INTO item (nome) VALUES ('thing') RETURNING id)
INSERT INTO lances_vendas (venda_id, item_id, participante_id, valor)
SELECT ven_key.id, item_key.id, par_key.id, numeric '3.1415'
FROM par_key, ven_key, item_key;

I know that you requested a single query, but you may still want to consider using a transaction:
BEGIN;
INSERT INTO participante (nome) VALUES ('Laurenz');
INSERT INTO venda (inicio) VALUES (current_date);
INSERT INTO item (nome) VALUES ('thing');
INSERT INTO lances_vendas (venda_id, item_id, participante_id, valer)
VALUES (currval('venda_id_seq'), currval('item_id_seq'), currval('participante_id_seq'), 3.1415);
COMMIT;
The transaction ensures that any new row in participante, venda and item leave the value of currval('X') unchanged.

You could create a function to do that job. Take a look at this example:
CREATE OR REPLACE FUNCTION import_test(p_title character varying, p_last_name character varying, p_first_name character varying, p_house_num integer, p_street character varying, p_zip_code character varying, p_city character varying, p_country character varying)
RETURNS integer
LANGUAGE plpgsql
AS
$body$
DECLARE
address_id uuid;
parent_id uuid;
ts timestamp;
BEGIN
address_id := uuid_generate_v4();
parent_id := uuid_generate_v4();
ts := current_timestamp;
insert into address (id, number, street, zip_code, city, country, date_created) values (address_id, p_house_num, p_street, p_zip_code, p_city, p_country, ts);
insert into person (id, title, last_name, first_name, home_address, date_created) values (parent_id, p_title, p_last_name, p_first_name, address_id, ts);
RETURN 0;
END;
$body$
VOLATILE
COST 100;
COMMIT;
Note how the generated UUID for the address (first insert) is used in the person-record (second insert)
Usage:
SELECT import_test('MR', 'MUSTERMANN', 'Peter', 'john2#doe.com', 54, 'rue du Soleil', '1234', 'Arlon', 'be');
SELECT import_test('MS', 'MUSTERMANN', 'Peter 2', 'peter2#yahoo.com', 55, 'rue de la Lune', '56789', 'Amnéville', 'fr');

Related

How do i insert necessarily different values in 2 tables that have only one attribute and share the same foreign key?

So i have a table 'category' that disjoints into 'simple_category' and 'super_category'. category has only one attribute which is its name. Same thing happens with the other 2 tables, which have a foreign key constraint to name. Super_category and simple_category cannot have the same name. How do i do that?
create table category(
name varchar(20) not null,
unique(name),
primary key(name));
create table simple_category(
name varchar(20),
unique(name),
primary key(name),
foreign key(name) references categoria(name));
create table super_category(
name varchar(20),
unique(name),
primary key(name),
foreign key(name) references categoria(name));
I expect an error message when i try to insert equal values into super_category and simple_category
Well, one method is a trigger. Another method is to put the type into the category table and include it in the foreign key relationships:
create table category (
category_id serial primary key,
name varchar(20) not null,
type varchar(20) not null,
check type in ('super', 'simple'),
unique (name),
unique (type, name) -- redundant but needed for foreign key reference
);
create table simple_category (
category_id serial primary key,
type varchar(20) default 'simple',
foreign key (type, category_id) references categoria (type, category_id)
);
create table super_category (
category_id serial primary key,
type varchar(20) default 'super',
foreign key (type, category_id) references categoria (type, category_id)
);
Note that I also modified the tables to have auto-incrementing primary keys, so names are not repeated everywhere.
How does this work? First insert the category into categoria with the appropriate type. Then insert the reference in the appropriate table. You could do the second using a trigger. However, I am guessing that there is other information you want, which is why they are split into different tables.
That would be constraint triggers checking for the existence of the name in the other table.
CREATE FUNCTION tf_simple_category_biu ()
RETURNS TRIGGER
AS
$$
BEGIN
IF EXISTS (SELECT *
FROM super_category sc
WHERE sc.name = new.name) THEN
RAISE EXCEPTION '%', 'Duplicate names are not allowed.';
RETURN NULL;
END IF;
RETURN new;
END;
$$
LANGUAGE plpgsql;
CREATE CONSTRAINT TRIGGER simple_category_biu
AFTER INSERT
OR UPDATE
OF name
ON simple_category
FOR EACH ROW
EXECUTE PROCEDURE tf_simple_category_biu();
CREATE FUNCTION tf_super_category_biu ()
RETURNS TRIGGER
AS
$$
BEGIN
IF EXISTS (SELECT *
FROM simple_category sc
WHERE sc.name = new.name) THEN
RAISE EXCEPTION '%', 'Duplicate names are not allowed.';
RETURN NULL;
END IF;
RETURN new;
END;
$$
LANGUAGE plpgsql;
CREATE CONSTRAINT TRIGGER super_category_biu
AFTER INSERT
OR UPDATE
OF name
ON super_category
FOR EACH ROW
EXECUTE PROCEDURE tf_super_category_biu();
db<>fiddle

How to achieve nested INSERT statement in PostgreSQL?

I have two tables, group and groupmembers. On insertion of a row in the group table, I also want to insert two values, groupid (id from group table) and userid (id of the user that created the group) into the groupmembers table. These are the tables:
CREATE TABLE groups (
id SERIAL PRIMARY KEY NOT NULL,
name CHARACTER VARYING(255) NOT NULL,
creator CHARACTER VARYING(255) NOT NULL,
role CHARACTER VARYING(100) NOT NULL DEFAULT ('admin'),
createdon TIMESTAMP WITH TIME ZONE DEFAULT now(),
FOREIGN KEY (creator) references users (email) on delete CASCADE
);
CREATE TABLE groupmembers (
id SERIAL PRIMARY KEY NOT NULL,
groupid INTEGER NOT NULL,
userid INTEGER NOT NULL,
createdon TIMESTAMP WITH TIME ZONE DEFAULT now(),
FOREIGN KEY (groupid) references groups (id) on delete CASCADE,
FOREIGN KEY (userid) references users (id) on delete CASCADE
);
CREATE TABLE users (
id SERIAL PRIMARY KEY NOT NULL,
firstname CHARACTER VARYING(255) NOT NULL,
lastname CHARACTER VARYING(255) NOT NULL,
email CHARACTER VARYING(50) UNIQUE NOT NULL,
password CHARACTER VARYING(255) NOT NULL,
registeredon TIMESTAMP WITH TIME ZONE DEFAULT now()
);
The insert statement into the group table is:
INSERT INTO groups (name, creator) VALUES ($1, $2) RETURNING *;
How do I add another insert statement that inserts values into groupid and userid columns of groupmembers table?
I have seen this, but it doesn't seem to answer my question:
PostgreSQL nested INSERTs / WITHs for foreign key insertions
I suggest to wrap both inserts in a single query with a data-modifying CTE, like:
WITH grp_ins AS (
INSERT INTO groups (name, creator)
VALUES ($1, $2)
RETURNING id, creator
)
INSERT INTO groupmembers (groupid, userid)
SELECT g.id, u.id
FROM grp_ins g
JOIN users u ON u.email = g.creator;
Since groups.creator is defined NOT NULL with an FK constraint enforcing referential integrity, an [INNER] JOIN is good. Else I'd consider a LEFT JOIN.
Much like the answer you referred to. Or these ones:
Insert data in 3 tables at a time using Postgres
Insert inserted id to another table
If, for some reason, you cannot enforce above command (like nested in a function that has to be used for inserts to groups), the next best thing is a trigger AFTER INSERT to execute the second INSERT. A bit more complex and costly. But does the job:
CREATE OR REPLACE FUNCTION trg_ins_row_in_groupmembers()
RETURNS trigger AS
$func$
BEGIN
INSERT INTO groupmembers (groupid, userid)
SELECT NEW.id, (SELECT u.id FROM users u WHERE u.email = NEW.creator);
RETURN NEW; -- doesn't matter much what you return here
END
$func$ LANGUAGE plpgsql;
And a trigger AFTER INSERT on groups:
CREATE TRIGGER groups_ins_aft
AFTER INSERT ON groups
FOR EACH ROW EXECUTE PROCEDURE trg_ins_row_in_groupmembers();
Related:
On INSERT to a table INSERT data in connected tables

Store PL/SQL Statement in a CLOB value, escape characters not working

I've got this statement which inserts some information plus a full trigger inside of a CLOB. However, I receive this error: ORA-01756: quoted string not properly terminated
I have added the escape character (') in front of every quote in the trigger. Yet I don't understand why I keep getting this error.
This is the Table creation query:
CREATE TABLE Template (id number(10) GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH 2), triggerEvent clob NOT NULL, triggerCode clob NOT NULL, TemplateTypeid number(10) NOT NULL, PRIMARY KEY (id));
And this is the whole insert query:
drop table template CASCADE CONSTRAINTS PURGE;
drop table TemplateType CASCADE CONSTRAINTS PURGE;
drop table BusinessRuleType CASCADE CONSTRAINTS PURGE;
drop table Operator CASCADE CONSTRAINTS PURGE;
drop table Category CASCADE CONSTRAINTS PURGE;
drop table BusinessRule CASCADE CONSTRAINTS PURGE;
drop table TargetDatabase CASCADE CONSTRAINTS PURGE;
drop table BusinessRuleType_Operator CASCADE CONSTRAINTS PURGE;
drop table DatabaseType CASCADE CONSTRAINTS PURGE;
drop table RuleValues CASCADE CONSTRAINTS PURGE;
drop sequence ARNG_seq;
CREATE SEQUENCE ARNG_seq;
CREATE TABLE Template (id number(10) GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH 2), triggerEvent clob NOT NULL, triggerCode clob NOT NULL, TemplateTypeid number(10) NOT NULL, PRIMARY KEY (id));
CREATE TABLE TemplateType (id number(10) GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH 2), category varchar2(255) NOT NULL, DatabaseTypeid number(10) NOT NULL, PRIMARY KEY (id));
CREATE TABLE BusinessRuleType (id number(10) GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH 2), name varchar2(255), Categoryid number(10) NOT NULL, Templateid number(10) NOT NULL, PRIMARY KEY (id));
CREATE TABLE Operator (id number(10) GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH 5), operatorType varchar2(255) NOT NULL, name varchar2(255) NOT NULL, value varchar2(255) NOT NULL, PRIMARY KEY (id));
CREATE TABLE Category (id number(10) GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH 2), name varchar2(255) NOT NULL, PRIMARY KEY (id));
CREATE TABLE BusinessRule (id number(10) GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH 2), generatedCode clob, name varchar2(255) NOT NULL, errorMessage varchar2(255) NOT NULL, validationFailureSeverity varchar2(50) NOT NULL, targetColumn varchar2(255) NOT NULL, targetTable varchar2(255) NOT NULL, BusinessRuleTypeid number(10) NOT NULL, SavedTargetDatabaseid number(10) NOT NULL, Operatorid number(10) NOT NULL, PRIMARY KEY (id));
CREATE TABLE TargetDatabase (id number(10) GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH 2), name varchar2(255) NOT NULL, url varchar2(255) NOT NULL, username varchar2(255) NOT NULL, password varchar2(255) NOT NULL, DatabaseTypeid number(10) NOT NULL, PRIMARY KEY (id));
CREATE TABLE BusinessRuleType_Operator (BusinessRuleTypeid number(10) GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH 2), Operatorid number(10) NOT NULL, PRIMARY KEY (BusinessRuleTypeid, Operatorid));
CREATE TABLE DatabaseType (id number(10) GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH 2), databaseType varchar2(255) NOT NULL, PRIMARY KEY (id));
CREATE TABLE RuleValues (id number(10) GENERATED BY DEFAULT ON NULL AS IDENTITY (START WITH 2), name varchar2(255) NOT NULL, stringValue varchar2(255), intValue number(10), clobValue clob, BusinessRuleid number(10) NOT NULL, PRIMARY KEY (id));
ALTER TABLE BusinessRuleType ADD CONSTRAINT FKBusinessRu863439 FOREIGN KEY (Categoryid) REFERENCES Category (id);
ALTER TABLE BusinessRule ADD CONSTRAINT FKBusinessRu277804 FOREIGN KEY (BusinessRuleTypeid) REFERENCES BusinessRuleType (id);
ALTER TABLE BusinessRuleType ADD CONSTRAINT FKBusinessRu443116 FOREIGN KEY (Templateid) REFERENCES Template (id);
ALTER TABLE Template ADD CONSTRAINT FKTemplate586491 FOREIGN KEY (TemplateTypeid) REFERENCES TemplateType (id);
ALTER TABLE BusinessRuleType_Operator ADD CONSTRAINT FKBusinessRu447733 FOREIGN KEY (BusinessRuleTypeid) REFERENCES BusinessRuleType (id);
ALTER TABLE BusinessRuleType_Operator ADD CONSTRAINT FKBusinessRu382431 FOREIGN KEY (Operatorid) REFERENCES Operator (id);
ALTER TABLE BusinessRule ADD CONSTRAINT FKBusinessRu873463 FOREIGN KEY (SavedTargetDatabaseid) REFERENCES TargetDatabase (id);
ALTER TABLE TemplateType ADD CONSTRAINT FKTemplateTy236989 FOREIGN KEY (DatabaseTypeid) REFERENCES DatabaseType (id);
ALTER TABLE TargetDatabase ADD CONSTRAINT FKTargetData209438 FOREIGN KEY (DatabaseTypeid) REFERENCES DatabaseType (id);
ALTER TABLE BusinessRule ADD CONSTRAINT FKBusinessRu447639 FOREIGN KEY (Operatorid) REFERENCES Operator (id);
ALTER TABLE RuleValues ADD CONSTRAINT FKRuleValues255277 FOREIGN KEY (BusinessRuleid) REFERENCES BusinessRule (id);
INSERT INTO Category (id, name) VALUES (1, 'Static');
INSERT INTO Category (id, name) VALUES (2, 'Dynamic');
INSERT INTO Operator (id, operatortype, name, value) VALUES (1, 'Compare', 'Equals', '=');
INSERT INTO Operator (id, operatortype, name, value) VALUES (2, 'Compare', 'Not Equals', '!=');
INSERT INTO Operator (id, operatortype, name, value) VALUES (3, 'Range', 'Between', 'BETWEEN');
INSERT INTO Operator (id, operatortype, name, value) VALUES (4, 'Range', 'Not Between', 'NOTBETWEEN');
INSERT INTO DatabaseType (id, databaseType) VALUES (1, 'Oracle');
INSERT INTO TargetDatabase (id, name, url, username, password, DatabaseTypeid) VALUES (1, 'School Database', 'XXXX', 'XXX', 'XX', 1);
INSERT INTO TemplateType (id, category, DatabaseTypeid) VALUES (1, 'Constraint', 1);
INSERT INTO Template (id, triggerevent, triggercode, templatetypeid) VALUES (1, 'insert, update <<column>> | <<table>>', q'[CREATE OR REPLACE TRIGGER BRG_{code}_{attribute_table}_TRG
BEFORE DELETE OR INSERT OR UPDATE
ON {attribute_table}
FOR EACH ROW
DECLARE
L_OPER VARCHAR2(3);
L_ERROR_STACK VARCHAR2(4000);
BEGIN
IF INSERTING
THEN
L_OPER := 'INS';
ELSIF UPDATING
THEN
L_OPER := 'UPD';
ELSIF DELETING
THEN
L_OPER := 'DEL';
END IF;
DECLARE
L_PASSED BOOLEAN := TRUE;
BEGIN
IF L_OPER IN ('INS', 'UPD')
THEN
IF '{operand}' = '=' THEN
L_PASSED := :NEW.{attribute_column} >= {range_min} AND :NEW.{attribute_column} <= {range_max};
ELSE
L_PASSED := :NEW.{attribute_column} < {range_min} OR :NEW.{attribute_column} > {range_max};
END IF;
IF NOT L_PASSED
THEN
raise_application_error(-20000, {error});
END IF;
END IF;
END;
END;]' , 1 );
INSERT INTO BusinessRuleType (id, name, categoryid, templateid) VALUES (1, 'Attribute Range Rule', 1, 1);
INSERT INTO BusinessRule (id, name, errorMessage, validationFailureSeverity, targetColumn, targetTable, BusinessRuleTypeid, SavedTargetDatabaseid, Operatorid) VALUES (1, 'BRG_NMR_CNS_RNG_01', 'Zit niet in de range', 'Informational Warning', 'cijfer', 'rapport', 1, 1, 1);
INSERT INTO RuleValues (id, name, intValue, BusinessRuleid) VALUES (1, 'MinValue', 1, 1);
INSERT INTO RuleValues (id, name, intValue, BusinessRuleid) VALUES (2, 'MaxValue', 10, 1);
INSERT INTO BusinessRuleType_operator (businessruletypeid, operatorid) VALUES (1,1);
The reason I need this entire trigger saved in the database, as a CLOB, is because my Java backend will work on it.
I had no trouble executing these two statements. Also, you might consider using the Q character to avoid the need to escape your quotes. As in:
q'[insert into xxx values ('a', 'b', 'c')]'
But as I said above, there were no problems with the code you posted, so far as I can tell.
CREATE TABLE template
(
id NUMBER (10)
GENERATED BY DEFAULT ON NULL AS IDENTITY ( START WITH 2),
triggerevent CLOB NOT NULL,
triggercode CLOB NOT NULL,
templatetypeid NUMBER (10) NOT NULL,
PRIMARY KEY (id)
)
/
BEGIN
INSERT INTO template (id,
triggerevent,
triggercode,
templatetypeid)
VALUES (
1,
'insert, update <<column>> | <<table>>',
'CREATE OR REPLACE TRIGGER BRG_{code}_{attribute_table}_TRG
BEFORE DELETE OR INSERT OR UPDATE
ON {attribute_table}
FOR EACH ROW
DECLARE
L_OPER VARCHAR2(3);
L_ERROR_STACK VARCHAR2(4000);
BEGIN
IF INSERTING
THEN
L_OPER := ''INS'';
ELSIF UPDATING
THEN
L_OPER := ''UPD'';
ELSIF DELETING
THEN
L_OPER := ''DEL'';
END IF;
DECLARE
L_PASSED BOOLEAN := TRUE;
BEGIN
IF L_OPER IN (''INS'', ''UPD'')
THEN
IF ''{operand}'' = ''BETWEEN'' THEN
L_PASSED := :NEW.{attribute_column} >= {range_min} AND :NEW.{attribute_column} <= {range_max};
ELSE
L_PASSED := :NEW.{attribute_column} < {range_min} OR :NEW.{attribute_column} > {range_max};
END IF;
IF NOT L_PASSED
THEN
raise_application_error(-20000, {error});
END IF;
END IF;
END;
END;',
1);
END;
For this quoting mechanism I prefer to use q'~blbelbe~' - ~ is very rare char.
And your problem is solved here Oracle 12c . Text literals
If the opening quote_delimiter is one of [, {, <, or (, then the closing quote_delimiter must be the corresponding ], }, >, or ). In all other cases, the opening and closing quote_delimiter must be the same character.

SQL constraint: two attributes, at least one foreign key match on same table

I have a table of phone numbers owned by a company, and a table of phone call records. Every call record includes (non-null) source and destination numbers. I am given the integrity constraint that either the source number or the destination number, but not both, are allowed to be numbers that are not in the phone number table (because they are numbers not owned by this company). In other words, I need to ensure that at least one of them is a foreign key to the phone number table.
create table phonenumber (
phonenum numeric(10,0) not null,
primary key (phonenum)
);
create table call_record (
URID varchar(20) not null,
c_src numeric(10,0) not null,
c_dst numeric(10,0) not null,
primary key (URID)
);
The following sounds like what I want but isn't valid SQL:
constraint call_constraint check (
foreign key (c_src) references phonenumber (phonenum) or
foreign key (c_dst) references phonenumber (phonenum)
)
Is there a way to specify this in DDL? If not, how would I write a trigger to enforce this?
Edited:
Here is another idea using DDL and not using trigger:
create table phonenumber (
phonenum numeric(10,0) not null,
primary key (phonenum)
);
Create a function to validate foreign key "by hand".
CREATE OR REPLACE FUNCTION call_check(p_src NUMBER, p_dst NUMBER) RETURN VARCHAR2 DETERMINISTIC IS
BEGIN
FOR x IN (SELECT COUNT(*) c
FROM (SELECT 1
FROM phonenumber
WHERE phonenum = p_src
UNION ALL
SELECT 1
FROM phonenumber
WHERE phonenum = p_dst)) LOOP
IF x.c>=1 AND x.c <= 2 THEN
RETURN 'OK';
END IF;
END LOOP;
RETURN 'NOK';
END;
If you're on 11g and up, then add virtual column and add check on that column
--drop table call_record
create table call_record (
URID varchar(20) not null,
c_src numeric(10,0) not null,
c_dst numeric(10,0) not null,
call_check_col GENERATED ALWAYS AS (call_check(c_src, c_dst)),
primary key (URID)
);
ALTER TABLE call_record ADD CONSTRAINT call_check_con CHECK (call_check_col='OK');
Let's test
SQL> INSERT INTO phonenumber VALUES ('123');
1 row inserted
SQL> INSERT INTO call_record (urid, c_src, c_dst) VALUES ('C1', '123', '321');
1 row inserted
SQL> INSERT INTO call_record (urid, c_src, c_dst) VALUES ('C3', '123', '123');
1 row inserted
SQL> INSERT INTO call_record (urid, c_src, c_dst) VALUES ('C2', '321', '321');
INSERT INTO call_record (urid, c_src, c_dst) VALUES ('C2', '321', '321')
ORA-02290: check constraint (TST.CALL_CHECK_CON) violated

Need Help Writing SQL Trigger

Been trying to write this trigger but I can't really work it out..
What I need to do:
Delete an item from the item table but at the same time delete any questions which are associated with the item as well as any questionupdates associated with that question. These deleted records then need to be stored in archived tables with a time of deletion as well as the ID of the operator that deleted them.
A question may have several updates associated with it as may an item have many questions relating to it.
I've put all the schema in the SQL fiddle as it's a lot easier to work on in there but I'll put it in here if needed.
The link to the SQL fiddle:
http://sqlfiddle.com/#!1/1bb25
EDIT: Thought I might as well put it here..
Tables:
CREATE TABLE Operator
(
ID INTEGER NOT NULL PRIMARY KEY,
Name VARCHAR(40) NOT NULL
);
CREATE TABLE Item
(
ID INTEGER NOT NULL PRIMARY KEY,
Name VARCHAR(40) NOT NULL
);
CREATE TABLE Faq
(
ID INTEGER NOT NULL PRIMARY KEY,
Question VARCHAR(150) NOT NULL,
Answer VARCHAR(2500) NOT NULL,
ItemID INTEGER,
FOREIGN KEY (ItemID) REFERENCES Item(ID)
);
CREATE TABLE Customer
(
ID INTEGER NOT NULL PRIMARY KEY,
Name VARCHAR(20) NOT NULL,
Email VARCHAR(20) NOT NULL
);
CREATE TABLE Question
(
ID INTEGER NOT NULL PRIMARY KEY,
Problem VARCHAR(1000),
AskedTime TIMESTAMP NOT NULL,
CustomerID INTEGER NOT NULL,
ItemID INTEGER NOT NULL,
FOREIGN KEY (ItemID) REFERENCES Item(ID),
FOREIGN KEY (CustomerID) REFERENCES Customer(ID)
);
CREATE TABLE qUpdate
(
ID INTEGER NOT NULL PRIMARY KEY,
Message VARCHAR(1000) NOT NULL,
UpdateTime TIMESTAMP NOT NULL,
QuestionID INTEGER NOT NULL,
OperatorID INTEGER,
FOREIGN KEY (OperatorID) REFERENCES Operator(ID),
FOREIGN KEY (QuestionID) REFERENCES Question(ID)
);
-- Archive Tables
CREATE TABLE DeletedQuestion
(
ID INTEGER NOT NULL PRIMARY KEY,
Problem VARCHAR(1000),
AskedTime TIMESTAMP NOT NULL,
CustomerID INTEGER NOT NULL,
ItemID INTEGER NOT NULL
);
CREATE TABLE DeletedqUpdate
(
ID INTEGER NOT NULL PRIMARY KEY,
Message VARCHAR(1000) NOT NULL,
UpdateTime TIMESTAMP NOT NULL,
Question INTEGER NOT NULL
);
CREATE TABLE DeletedItem
(
ID INTEGER NOT NULL PRIMARY KEY,
Name VARCHAR(40) NOT NULL,
OperatorDeleteID INTEGER NOT NULL,
FOREIGN KEY (OperatorDeleteID) REFERENCES Operator(ID)
);
Some samples inserts for testing
--Product Inserts
INSERT INTO Item (ID, Name) VALUES (1, 'testitem1');
INSERT INTO Item (ID, Name) VALUES (2, 'testitem2');
--Operator Inserts
INSERT INTO Operator (ID, Name) VALUES (1, 'testname1');
INSERT INTO Operator (ID, Name) VALUES (2, 'testname2');
--Faq Inserts
INSERT INTO Faq (ID, Question, Answer, ItemID) VALUES (1, 'testq1', 'testa1', 1);
INSERT INTO Faq (ID, Question, Answer, ItemID) VALUES (2, 'testq2', 'testa2', 2);
-- Customer Inserts
INSERT INTO Customer (ID, Name, Email) VALUES (1, 'testcust1', 'testemail1');
INSERT INTO Customer (ID, Name, Email) VALUES (2, 'testcust2', 'testemail2');
-- Question Inserts
INSERT INTO Question (ID, Problem, AskedTime, CustomerID, ItemID) VALUES (1,'testproblem1','2012-03-14 09:30',1,1);
INSERT INTO Question (ID, Problem, AskedTime, CustomerID, ItemID) VALUES (2,'testproblem1','2012-07-14 09:30',2,1);
INSERT INTO qUpdate (ID, Message, UpdateTime, OperatorID, QuestionID) VALUES (1, 'test1','2012-05-14 09:30', 1, 1);
INSERT INTO qUpdate (ID, Message, UpdateTime, OperatorID, QuestionID) VALUES (2, 'test2','2012-08-14 09:30', 2, 1);
The first thing to do is to understand that in PostgreSQL, a CREATE TRIGGER statement binds a trigger function to one or more operations on a table, so let's start with the syntax of the function. You can write trigger functions in various scripting languages, but the most common is plpgsql. A simple function might look like this:
CREATE OR REPLACE FUNCTION Question_delete_trig_func()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
INSERT INTO DeletedQuestion
SELECT OLD.*;
RETURN OLD;
END;
$$;
To run this after deletes:
CREATE TRIGGER Question_delete_trig
AFTER DELETE ON Question
FOR EACH ROW EXECUTE PROCEDURE Question_delete_trig_func();
That should be enough to get you started.
You should have a trigger like this for each table from which deleted rows should be saved. Then you need to determine how you will make the deletes happen. You could just define the appropriate foreign keys as ON DELETE CASCADE and let PostgreSQL do it for you.