PostgreSQL Create Sequence using new id - postgresql

I want to create a sequence for each row created in the table account, like os_1, os_2, etc...
How can I get the id of this new row and insert it on the name of the sequence?
CREATE OR REPLACE FUNCTION public.create_os_seq() RETURNS TRIGGER AS $$
#variable_conflict use_variable
BEGIN
--CREATE SEQUENCE seqname;
EXECUTE format('CREATE SEQUENCE os_', NEW.id);
return NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER create_os_seq AFTER INSERT ON account FOR EACH ROW EXECUTE PROCEDURE create_os_seq();
Table account
id INT AUTO_INCREMENT
nane VARCHAR
After creating a sequence i´ll put its number in the OS table
table os
id INT
account_id INT

From your question I assume you want to take care of self incrementing values?.. Postgres uses shortcut SERIAL instead of AUTO_INCREMENT, just create table like:
CREATE TABLE so79 (id bigserial primary key, col text);
That will automatically create sequence for you and assign its value as default for column id. Basically will make it smth like AUTO_INCREMENT. You don't have to use trigger to increment values...

There is no way that creating a sequence for each row is a good idea. Instead, tell us what you're trying to do.
My guess is you need a waterline indicator, a high point you intend to increment on some action. Just use an integer.
CREATE TABLE foo (
foo_id serial,
max_seen int
);
Now you can do whatever with triggers on other tables and such to increment max_seen.

Related

First values in an auto increment trigger

I am working with the following table in PostgreSQL 10.3:
CREATE TABLE s_etpta.tab1 (
Number VARCHAR(40) NOT NULL,
id VARCHAR(8),
CONSTRAINT i_tab1 PRIMARY KEY(Number)
)
I need to increment the column id by 1 with every insert. I can't alter the table because I'm not the owner so I have no other choice than to increment a varchar column.
The column is type varchar prefixed with zeros. How can I specify that I want to start with '00000001' if the table is empty? Because when I already have values in my table the trigger gets the last value and increment it for the next insert which is correct, but when my table is empty the id column stays empty since the trigger has no value to increment.
CREATE OR REPLACE FUNCTION schema."Num" (
)
RETURNS trigger AS
$body$
DECLARE
BEGIN
NEW.id := lpad(CAST(CAST(max (id) AS INTEGER)+1 as varchar),8, '0') from
schema.tab1;
return NEW;
END;
$body$
LANGUAGE 'plpgsql'
VOLATILE
RETURNS NULL ON NULL INPUT
SECURITY INVOKER
COST 100;
A trigger design is unsafe and expensive trickery that can easily fail under concurrent write load. Don't use a trigger. Use a serial or IDENTITY column instead:
Auto increment table column
Don't use text (or varchar) for a numeric value.
Don't pad leading zeroes. You can format your numbers any way you like for display with to_char():
How to auto increment id with a character
In Postgres 10 or later your table could look like this:
CREATE TABLE s_etpta.tab1 (
number numeric NOT NULL PRIMARY KEY, -- not VARCHAR(40)
id bigint GENERATED ALWAYS AS IDENTITY -- or just int?
);
No trigger.
Seems odd that number is the PK. Would seem like id should be. Maybe you do not need the id column in the table at all?
Gap-less sequence where multiple transactions with multiple tables are involved
If you need to get the underlying sequence in sync:
How to reset postgres' primary key sequence when it falls out of sync?
Postgres manually alter sequence
If you cannot fix your table, this trigger function works with the existing one (unreliably under concurrent write load):
CREATE OR REPLACE FUNCTION schema.tab1_number_inc()
RETURNS trigger AS
$func$
DECLARE
BEGIN
SELECT to_char(COALESCE(max(id)::int + 1, 0), 'FM00000000')
FROM schema.tab1
INTO NEW.id;
RETURN NEW;
END
$func$ LANGUAGE plpgsql;
Trigger:
CREATE TRIGGER tab1_before_insert
BEFORE INSERT ON schema.tab1
FOR EACH ROW EXECUTE PROCEDURE schema.tab1_number_inc();
The FM modifier removes leading blanks from to_char() output:
Remove blank-padding from to_char() output

postgres update NEW variable before INSERT in a TRIGGER

I've two tables accounts and projects:
create table accounts (
id bigserial primary key,
slug text unique
);
create table projects (
id bigserial primary key,
account_id bigint not null references accounts (id),
name text
);
I want to be able to insert a new row into projects by specifying only account.slug (not account.id). What I'm trying to achieve is something like:
INSERT into projects (account_slug, name) values ('account_slug', 'project_name');
I thought about using a trigger (unfortunately it doesn't work):
create or replace function trigger_projects_insert() returns trigger as $$
begin
if TG_OP = 'INSERT' AND NEW.account_slug then
select id as account_id
from accounts as account
where account.slug = NEW.account_slug;
NEW.account_id = account_id;
-- we should also remove NEW.account_slug but don't know how
end if;
return NEW;
end;
$$ LANGUAGE plpgsql;
create trigger trigger_projects_insert before insert on projects
for each row execute procedure trigger_projects_insert();
What is the best way to achieve what I'm trying to do?
Is a trigger a good idea?
Is there any other solution?
WITH newacc AS (
INSERT INTO accounts (slug)
VALUES ('account_slug')
RETURNING id
)
INSERT INTO projects (account_id, name)
SELECT id, 'project_name'
FROM newacct;
If you are limited in the SQL you can use, another idea might be to define a view over both tables and create an INSTEAD OF INSERT trigger on the view that performs the two INSERTs on the underlying tables. Then an INSERT statement like the one in your question would work.

Is it possible to pass data to postgreSQL trigger?

I need to log any changes made in some table by trigger which will insert older version of modified row to another table with some additional data like:
-which action was performed
-when this action was performed
-by who.
I have problem with last requirement. While performing SQL somewhere in java by JDBC. I need to somehow pass logged user id stored in variable to postgres table where all older versions of modified row will be stored.
Is it even possible?
It may be stupid question but I desperately try to avoid inserting data like that manually in java. Triggers done some work for me but not all I need.
Demonstrative code below (I've cut out some code for security reasons):
"notes" table:
CREATE TABLE my_database.notes
(
pk serial NOT NULL,
client_pk integer,
description text,
CONSTRAINT notes_pkey PRIMARY KEY (pk)
)
Table storing older versions of every row changed in "notes" table:
CREATE TABLE my_database_log.notes_log
(
pk serial NOT NULL,
note_pk integer,
client_pk integer,
description text,
who_changed integer DEFAULT 0, -- how to fill in this field?
action_date timestamp without time zone DEFAULT now(), --when action was performed
action character varying, --which action was performed
CONSTRAINT notes_log_pkey PRIMARY KEY (pk)
)
Trigger for "notes" table:
CREATE TRIGGER after_insert_or_update_note_trigger
AFTER INSERT OR UPDATE
ON database.notes
FOR EACH ROW
EXECUTE PROCEDURE my_database.notes_new_row_log();
Procedure executed by trigger:
CREATE OR REPLACE FUNCTION my_database.notes_new_row_log()
RETURNS trigger AS
$BODY$
BEGIN
INSERT INTO my_database_log.notes_log(
note_pk, client_pk, description, action)
VALUES (
NEW.pk, NEW.client_pk, NEW.description, TG_OP);
RETURN NEW;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION my_database.notes_new_row_log()
OWNER TO database_owner;
According to #Nick Barnes hint in comments, there is a need to declare a variable in postgresql.conf file:
...
#----------------------------------------------------------------------------
# CUSTOMIZED OPTIONS
#----------------------------------------------------------------------------
custom_variable_classes = 'myapp' # list of custom variable class names
myapp.user_id = 0
and call:
SET LOCAL customvar.user_id=<set_user_id_value_here>
before query that should be triggered.
To handle variable in trigger use:
current_setting('myapp.userid')

Insert trigger to Update another table using PostgreSQL

I have a table named awards. How can I mount a Trigger in PostgreSQL where each insert in the table awards updates a different table?
Here we have two tables named table1 and table2. Using a trigger I'll update table2 on insertion into table1.
Create the tables
CREATE TABLE table1
(
id integer NOT NULL,
name character varying,
CONSTRAINT table1_pkey PRIMARY KEY (id)
)
CREATE TABLE table2
(
id integer NOT NULL,
name character varying
)
The Trigger Function
CREATE OR REPLACE FUNCTION function_copy() RETURNS TRIGGER AS
$BODY$
BEGIN
INSERT INTO
table2(id,name)
VALUES(new.id,new.name);
RETURN new;
END;
$BODY$
language plpgsql;
The Trigger
CREATE TRIGGER trig_copy
AFTER INSERT ON table1
FOR EACH ROW
EXECUTE PROCEDURE function_copy();
You want the documenation for PL/PgSQL triggers, which discusses just this case among others. The general documentation on triggers may also be useful.
You can use either a BEFORE or AFTER trigger for this. I'd probably use an AFTER trigger so that my trigger saw the final version of the row being inserted, though. You want FOR EACH ROW, of course.

Create autoincrement field with trigger and sequence in Postgres

i'm triying to create an autoincrement field (like SERIAL) using a trigger and sequence. I know that only can use a sequence or SERIAL type on field, but i must resolve this using both methods (triggers and secuences)
CREATE SEQUENCE AlimentosSequencia;
CREATE OR REPLACE FUNCTION AlimentoFuncion()
RETURNS "trigger" AS
$BODY$
BEGIN
New.id:=nextval('AlimentosSequencia');
Return NEW;
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;
CREATE TRIGGER AlimentosTrigger
BEFORE INSERT
ON alimento
FOR EACH ROW
EXECUTE PROCEDURE AlimentoFuncion();
I try this combination but dosen't works, the table alimento has two fields only, integer id(the autoincrement with trigger and sequence) and the varchar name.
Any suggestion ?
Thanks
As others users have told you, you don't need to use a trigger. You can declare the table like this:
CREATE SEQUENCE AlimentosSequencia;
CREATE TABLE alimento (
id integer NOT NULL DEFAULT nextval('AlimentosSequencia') PRIMARY KEY
,name VARCHAR(255));
And when you insert a new record:
INSERT INTO alimento (name) VALUES ('lemon');
Another possibility is declared the id field as serial type, that it would create the sequence automatically.
UPDATE:
Ok, it's an exercise. Then I don't understand what's the problem? I have tested this code:
CREATE SEQUENCE AlimentosSequencia;
CREATE TABLE alimento (
id integer NOT NULL PRIMARY KEY
,name VARCHAR(255));
CREATE OR REPLACE FUNCTION AlimentoFuncion()
RETURNS "trigger" AS
$BODY$
BEGIN
New.id:=nextval('AlimentosSequencia');
Return NEW;
END;
$BODY$
LANGUAGE 'plpgsql' VOLATILE;
CREATE TRIGGER AlimentosTrigger
BEFORE INSERT
ON alimento
FOR EACH ROW
EXECUTE PROCEDURE AlimentoFuncion();
INSERT INTO alimento (name) VALUES ('lemon');
And it works without problems.