I am trying to create a trigger that when a user inserts into a table, the trigger will join with another table and insert into the primary one. Here is the trigger I have created so far. When I insert it into the database I get "Warning: Trigger Created with Compilation Errors."
CREATE OR REPLACE TRIGGER TBI_PERSONS_IDM_STAGING
AFTER INSERT
ON PERSONS_IDM_STAGING
FOR EACH ROW
BEGIN
IF :new.PERSON NOT IN (SELECT PERSON FROM PERSONS) AND INSERTING
THEN
INSERT INTO PERSONS (
PERSON,
DBLOGIN_ID,
LAST_NAME,
FIRST_NAME,
DEPARTMENT,
JOBTITLE,
USERLEVEL,
FACILITY_ID,
USER_OPTIONS,
PASSWORD)
VALUES(:new.PERSON,
:new.PERSON,
:new.LAST_NAME,
:new.FIRST_NAME,
:new.DEPARTMENT,
:new.JOBTITLE,
:new.USERLEVEL,
(SELECT KMS_FACILITY_ID FROM FACILITY_IDM_STAGING WHERE FACILITY_IDM_STAGING.IDM_CODE = :new.IDM_CODE),
:new.USER_OPTIONS,
:new.PASSWORD);
END IF;
END;
/
Can anyone see what I'm doing wrong?
Update: I was able to fix the code. Here is the correct statement if anyone else has this issue:
CREATE OR REPLACE TRIGGER TAI_PERSONS_IDM_STAGING
AFTER INSERT
ON PERSONS_IDM_STAGING
FOR EACH ROW
BEGIN
INSERT INTO PERSONS (
PERSON,
DBLOGIN_ID,
LAST_NAME,
FIRST_NAME,
DEPARTMENT,
JOBTITLE,
USERLEVEL,
FACILITY_ID,
USER_OPTIONS,
PASSWORD)
VALUES(:new.PERSON,
:new.PERSON,
:new.LAST_NAME,
:new.FIRST_NAME,
:new.DEPARTMENT,
:new.JOBTITLE,
:new.USERLEVEL,
(SELECT KMS_FACILITY_ID FROM FACILITY_IDM_STAGING WHERE FACILITY_IDM_STAGING.IDM_CODE = :new.IDM_CODE),
:new.USER_OPTIONS,
:new.PASSWORD);
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN NULL;
END;
/
Related
This is the code but I'm getting errors while compiling :
The condition table permissions can be changed if pleased that's why I'm adding the permissions column, the result table needs a permission column for all the users according to the values of condition table
-- create
CREATE TABLE EMPLOYEE (
empId INTEGER PRIMARY KEY,
username TEXT NOT NULL,
userrole TEXT NOT NULL,
roles TEXT NOT NULL,
accesses TEXT NOT NULL
);
-- insert
INSERT INTO EMPLOYEE VALUES (0001, 'Clark','President', 'Admin','privileged');
INSERT INTO EMPLOYEE VALUES (0002, 'Dave','sales rep', 'Operational role','not privileged');
INSERT INTO EMPLOYEE VALUES (0003, 'Ava','finance manager', 'Managerial role','privileged');
-- fetch
SELECT * FROM EMPLOYEE;
--mastertable
CREATE TABLE CONDITION (
userrole TEXT NOT NULL,
accesses TEXT NOT NULL,
permissions TEXT NOT NULL
);
--insertintomastertable
INSERT INTO CONDITION VALUES ('admin','privileged','granted');
INSERT INTO CONDITION VALUES ('admin','privileged','revoked');
INSERT INTO CONDITION VALUES ('Managerial role','not privileged','granted');
INSERT INTO CONDITION VALUES ('Managerial role','privileged','revoked');
INSERT INTO CONDITION VALUES ('Operational role','not privileged','granted');
INSERT INTO CONDITION VALUES ('Operational role','privileged','revoked');
--resulttable
CREATE TABLE RESULT (
empId INTEGER PRIMARY KEY,
username TEXT NOT NULL,
userrole TEXT NOT NULL,
roles TEXT NOT NULL,
permission TEXT
);
--insertintoresult
INSERT INTO RESULT (empId, username, userrole, roles)
SELECT empId, username, userrole, roles
FROM EMPLOYEE;
DO
$do$
BEGIN
IF (EMPLOYEE.roles and EMPLOYEE.accesses) == (CONDITION.roles and CONDITION.accesses) THEN
RESULT.permission := 'GRANTED';
WHERE (EMPLOYEE.roles and EMPLOYEE.accesses) == (CONDITION.roles and CONDITION.accesses)
FROM EMPLOYEE
FROM CONDITION
ELSE
RESULT.permission := 'REVOKED';
END IF;
END
$do$;
SELECT * FROM RESULT
You can update the result table with join also, while insert i changed the query a little bit.
--insertintoresult
INSERT INTO RESULT (empId, username, userrole, roles,permission)
SELECT empId, e.username, e.userrole, roles,
c.permissions
FROM EMPLOYEE e,CONDITION c where e.roles=c.userrole and e.accesses= c.accesses order by empId;
SELECT * FROM RESULT;
INSERT INTO EMPLOYEE VALUES
(0004, 'Dave new','sales rep', 'admin','privileged');
INSERT INTO EMPLOYEE VALUES
(0005, 'Ava new','finance manager', 'Managerial role','not privileged');
INSERT INTO RESULT (empId, username, userrole, roles)
SELECT empId, username, userrole, roles
FROM EMPLOYEE ON CONFLICT (empId) DO NOTHING;
UPDATE
RESULT p
SET
permission = c.permissions
FROM
EMPLOYEE e,CONDITION c
WHERE
e.roles=c.userrole and e.accesses= c.accesses and p.empId=e.empid;
SELECT * FROM RESULT order by empid;
I have a table people with columns: first_name, last_name, phone_number, address. Is it possible to have a function, that on insert checks if phone_number already exists and if so, it inserts existing values (first_name and last_name) instead of what user tried to insert? It would allow new address to be insert, only existing first_name and last_name would be copied. It is quite opposite to upsert, which would update the existing row. None of these columns have constraints.
I tried to use IF EXISTS, but I actually don't know how to pass the existing values into NEW. It works with fixed values, e.g. THEN NEW.first_name = 'Jack'.
BEGIN
IF EXISTS (SELECT 1 FROM people p WHERE p.phone_number = NEW.phone_number) THEN
NEW.first_name = ?;
END IF;
RETURN NEW;
I think you are looking for
INSERT INTO people(phone_number, first_name, last_name, address)
VALUES ($1,
COALESCE((SELECT first_name FROM people WHERE phone_number = $1), $2),
COALESCE((SELECT last_name FROM people WHERE phone_number = $1), $3),
$4);
I am writing migration script to migrate database. I have to duplicate the row by incrementing primary key considering that different database can have n number of different columns in the table. I can't write each and every column in query. If i simply just copy the row then, I am getting duplicate key error.
Query: INSERT INTO table_name SELECT * FROM table_name WHERE id=255;
ERROR: duplicate key value violates unique constraint "table_name_pkey"
DETAIL: Key (id)=(255) already exist
Here, It's good that I don't have to mention all column names. I can select all columns by giving *. But, same time I am also getting duplicate key error.
What's the solution of this problem? Any help would be appreciated. Thanks in advance.
If you are willing to type all column names, you may write
INSERT INTO table_name (
pri_key
,col2
,col3
)
SELECT (
SELECT MAX(pri_key) + 1
FROM table_name
)
,col2
,col3
FROM table_name
WHERE id = 255;
Other option (without typing all columns , but you know the primary key ) is to CREATE a temp table, update it and re-insert within a transaction.
BEGIN;
CREATE TEMP TABLE temp_tab ON COMMIT DROP AS SELECT * FROM table_name WHERE id=255;
UPDATE temp_tab SET pri_key_col = ( select MAX(pri_key_col) + 1 FROM table_name );
INSERT INTO table_name select * FROM temp_tab;
COMMIT;
This is just a DO block but you could create a function that takes things like the table name etc as parameters.
Setup:
CREATE TABLE public.t1 (a TEXT, b TEXT, c TEXT, id SERIAL PRIMARY KEY, e TEXT, f TEXT);
INSERT INTO public.t1 (e) VALUES ('x'), ('y'), ('z');
Code to duplicate values without the primary key column:
DO $$
DECLARE
_table_schema TEXT := 'public';
_table_name TEXT := 't1';
_pk_column_name TEXT := 'id';
_columns TEXT;
BEGIN
SELECT STRING_AGG(column_name, ',')
INTO _columns
FROM information_schema.columns
WHERE table_name = _table_name
AND table_schema = _table_schema
AND column_name <> _pk_column_name;
EXECUTE FORMAT('INSERT INTO %1$s.%2$s (%3$s) SELECT %3$s FROM %1$s.%2$s', _table_schema, _table_name, _columns);
END $$
The query it creates and runs is: INSERT INTO public.t1 (a,b,c,e,f) SELECT a,b,c,e,f FROM public.t1. It's selected all the columns apart from the PK one. You could put this code in a function and use it for any table you wanted, or just use it like this and edit it for whatever table.
I have these three tables for services, executed_services and a rating table, which rates the service. My rating table has a foreign key to executed_services, and so, I wanted to "copy" the service to be rated to the executed_services relation. I tried using the following procedure, but this select won't work, as it has to return only one result.
BEGIN
INSERT INTO servico_executado (id, data_abertura, id_solicitacao, id_profissional)
VALUES (SELECT id, data_abertura, id_solicitacao, id_profissional FROM servico WHERE id = NEW.id_servico);
DELETE FROM servico WHERE id = NEW.id_servico;
RETURN NEW;
END;
So, what should I do to get all the values from one table and insert on another? Or is there other way to do that?
You can do it very easily in one step with a CTE with the following simple query:
WITH deleted AS (DELETE FROM servico WHERE id = $1 RETURNING *)
INSERT INTO servico_executado (id, data_abertura, id_solicitacao, id_profissional)
SELECT id, data_abertura, id_solicitacao, id_profissional FROM deleted
RETURNING *;
There is no need to use PL/pgSQL. The id of the service moved is denoted by the placeholder $1 in the above query.
INSERT INTO servico_executado
SELECT id, data_abertura, id_solicitacao, id_profissional
FROM servico WHERE id = NEW.id_servico;
Do not use 'VALUES'. If you have more columns in the table, you can use null in the select statement for those missing columns.
In a web game with PostgreSQL 9.3 backend I sometimes have to ban users, by putting them into the following table:
create table pref_ban (
id varchar(32) primary key,
first_name varchar(64),
last_name varchar(64),
city varchar(64),
last_ip inet,
reason varchar(128),
created timestamp default current_timestamp
);
I ban a user by running the following procedure, so that he/she can not enter the game at next login time (i.e. not the offender is not banned immediately):
create or replace function pref_delete_user(_id varchar,
_reason varchar) returns void as $BODY$
begin
insert into pref_ban --THIS FAILS WITH "duplicate key"
(id, first_name, last_name, city, last_ip, reason)
select id, first_name, last_name, city, last_ip, _reason
from pref_users where id = _id;
create temporary table temp_gids (gid int not null) on commit drop;
insert into temp_gids (gid) select gid from pref_scores where id=_id;
delete from pref_games p
using temp_gids t
where p.gid = t.gid;
create temporary table temp_rids (rid int not null) on commit drop;
insert into temp_rids (rid) select rid from pref_cards where id=_id;
delete from pref_rounds r
using temp_rids t
where r.rid = t.rid;
delete from pref_users where id=_id;
end;
$BODY$ language plpgsql;
However the INSERT statement in the above procedure sometimes fails with:
SQLSTATE[23505]: Unique violation: 7 ERROR: duplicate key value violates unique constraint "pref_ban_pkey" DETAIL: Key (id)=(GC1121680399) already exists.
CONTEXT: SQL statement "insert into pref_ban (id, first_name, last_name, city, last_ip, reason) select id, first_name, last_name, city, last_ip, _reason from pref_users where id = _id" PL/pgSQL function pref_delete_user(character varying,character varying) line 4 at SQL statement
This happens when some game stats have been written after I have banned the user for the 1st time (because users are not banned immediately and game stats are sometimes still being written into the database).
This is okay for me, but I wonder, how could I ignore the INSERT failure?
I know that typically the following "UPSERT" scheme is being used with PostgreSQL:
update pref_ban set
first_name = _first_name,
last_name = _last_name,
city = _city,
last_ip = _last_ip,
...
where id = _id;
if not found then
insert into pref_ban(id,
first_name,
last_name,
city,
last_ip,
...)
values (_id,
_first_name,
_last_name,
_city,
_last_ip,
...
now());
end if;
but this is NOT what I am after here: because I don't need to update the banned user details. I would just like to exit the procedure on the INSERT failure.