How to use a postgresql trigger to set a UUID - database

I have a table called students that gets populated from an external source. The data comes in w/ composite keys that I then want to use to generate a UUID via uuid_generate_v5. I'm currently doing this via
UPDATE students SET id = uuid_generate_v5(uuid_ns_url(), CONCAT(composite1, composite2));
I'm interested in moving this into a trigger whenever a row gets inserted into this table. How would I go about doing this?

Something like the untested:
CREATE OR REPLACE FUNCTION my_trigger()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
NEW.uuid_col := uuid_generate_v5(uuid_ns_url(), CONCAT(NEW.composite1, NEW.composite2));
RETURN NEW;
END;
$$;
CREATE TRIGGER mytable_my_trigger
BEFORE INSERT ON mytable
FOR EACH ROW EXECUTE PROCEDURE my_trigger();
but really, the manual on PL/PgSQL triggers and the CREATE TRIGGER docs should really get you there...

Related

Way to Capture result set from instead of insert trigger in SQL Server 2016

I have a table that utilizes an instead of insert trigger. The trigger manipulates the data for each inserted row before inserting it. Because the inserted table is not modifiable, it was implemented using a temp (#) table. At the end of the trigger a select from the temp table is done to return the inserted data to the calling client. When I do a an insert in SSMS, I can see the data that is returned and the columns all have names and values. My Trigger looks like this:
Create TRIGGER [dbo].[RealTableInsteadOfInsert] on [dbo].[RealTable]
INSTEAD OF INSERT
AS
BEGIN
set nocount on;
declare #lastDigit int;
if exists (select * from inserted)
Begin
Select *
into #tempInserted
from inserted
.... Logic to add and manipulate column data .....
INSERT INTO RealTAble(id, col1, col2, col3,....)
Select *
from #tempInserted;
Select id as insertId, *
from #tempInserted;
END
End
My question is how can I capture the output of the instead of trigger into a table for further processing using the returned data. I can't use an output clause on the insert statement as the temp table no longer exists and the data that was calculated/modified on the insert was not done in the inserted table. Is my only option to update the trigger to use a global temp table instead of a local?
You have two main options:
Send data through service broker. This is complicated and potentially slow. On the plus side, it does let you do your further processing work decoupled from the original transaction, which is nice in certain use cases.
Write the data to a real table. This can also give you transactional decoupling, but you don't get automatic activation of the decoupled processing logic if you want that.
OK, but if you write to a real table, and a lot of processes are causing the trigger to fire, how do you find "your" data? Putting aside the fact that a global temp table has the same problem unless you want to get dynamic, one solution is to use a process keyed table, as described by Erland Sommarskog in his data sharing article.
Returning data from triggers (ie, to a client) is a bad idea. The ability to do this at all will soon be removed I know you don't need to do this in your solution, just giving you a heads up.

why results of function save late?

today i faced with a strange problem. i want to invoke a plpgsql function inside a view(i know it is a bad and strange way to call a function so please don't mention it in your answers). i do that but the results of function apply after showing the results of view!!!
i want to know how it is possible? and how i can force pgpgsql function to store its result before finishing the query?
some information about my code:
plan: this is a table about travel plans including date_of_travel, price etc.
travel_check(): this is a function that deletes those records from plan table that their date_of_travel is less than current_date and store them to another table and return an integer.(this is not important so much just remember this function delete some records from plan table) but i write exact definition below:
create or replace function finish_travel(todelete integer) returns void as
$body$
begin
create table if not exists history_of_travel(
id integer,
traveldate date,
source_id char(3),
destination_id char(3),
timeofday time
);
insert into history_of_travel
select id,traveldate,source_id,destination_id,timeofday
from plan
where id=todelete;
delete from plan where id=todelete;
end;
$body$
language plpgsql volatile;
create or replace function travel_check() returns int as
$body$
declare
trip record;
begin
for trip in select *
from plan
loop
if(trip.traveldate<current_date) then
perform finish_travel(trip.id);
end if;
if(trip.traveldate=current_date and trip.timeofday=now()::timestamp(0)::time) then
perform finish_travel(trip.id);
end if;
end loop;
return 1;
end;
$body$
language plpgsql volatile;
i want to create a view containing two step:
call function and update plan.
show the plan's records.
i tried below code
create view clients_select_query as
select plan.*
from plan,(select travel_check()) as d
where exists(select * from plan)
when i run:
select * from clients_select_query
unfortunately it shows the contents of plan table without any change
but if i run again:
select * from clients_select_query
or
select * from plan
i see that the changes have been applied.
how i can see changes after first running query without changing method?
if it's not clear, tell me to put exact definition of function,table and view
The result isn't "saved late", but Postgres hides the changes from you, so that queries behave predictably.
Postgres' concurrency control means that a query won't see any changes after it started running. This is true even if your own query is making the changes, and there is no way to avoid it.
Instead, you could create a set-returning function which does the deletion first, and then returns the contents of the table using a second query. Then, create a view which selects from that function.
create function clients_select_function() returns setof plan as $$
select travel_check();
select * from plan;
$$
language sql;
create view clients_select_query as
select * from clients_select_function();

Add temporary column for insert stored procedure in oracle

I am trying to add a temporary column to a target table and use that column in a where clause to insert new data into a parent table via stored procedure that I am using for a one-to-one relationship from parent to target table (see code below). I am getting an error with the alter table add column statement thus resulting in the IMPORT_NUMBER being an invalid identifier. Any help would be much appreciated.
EXECUTE IMMEDIATE
'ALTER TABLE TARGET_TABLE ADD IMPORT_NUMBER NUMBER';
INSERT
INTO
TARGET_TABLE(
existing_col_1,
existing_col_2,
existing_col_3,
IMPORT_NUMBER
)
SELECT
STAGED_TABLE.value1,
STAGED_TABLE.value2,
STAGED_TABLE.value3,
STAGED_TABLE.IMPORT_NUMBER
FROM
STAGED_TABLE
GROUP BY
IMPORT_NUMBER;
UPDATE
PARENT_TABLE
SET
target_table_id =(
SELECT
TARGET_TABLE.id
FROM
TARGET_TABLE
WHERE
TARGET_TABLE.IMPORT_NUMBER = PARENT_TABLE.IMPORT_NUMBER
)
WHERE
PARENT_TABLE.IMPORT_NUMBER IS NOT NULL;
EXECUTE IMMEDIATE 'ALTER TABLE TARGET_TABLE DROP COLUMN IMPORT NUMBER';
If this is a stored procedure, the entire procedure is parsed and validated at the time of create or replace procedure. At the time the procedure is created the column IMPORT_NUMBER does not exist, so the insert and update statements can not be validated.
I would try to find a solution that does not include DDL if possible. Can the column be made a permanent part of the table?
If you must follow this path, the insert and update statements will need to be in strings and passed to execute immediate or DBMS_SQL so that they are parsed and validated at run time, after the column is created.

MSSQL After Update Trigger: Multi Row Stored Prc

I've written a stored procedure which I would like to invoke on row updates.
It could look something like this:
Create PROCEDURE [TraceChange]
#Id uniqueidentifier,
AS
BEGIN
/*do some inserts, updates, etc*/
END
Now I create a trigger for a Table, which works fine for a single row update:
Create TRIGGER T1_TraceInsert
on T1
AFTER UPDATE
AS
BEGIN
SET NOCOUNT ON;
declare #id uniqueidentifier
exec TraceChange #id
END
This works fine - but just for a single Row.
How do I rewrite it to execute on multi row updates?
AFAIK its not a great approach to use a cursor within such triggers.
Thanks!
There are two options:
1) Process the data in while loop - get the data from inserted table, and execute your SP X times - one per row.
2) Rewrite SP (or create a new one) which accepts table as input parameter. Process all the rows in one go if possible. Depends on the code of SP. So, in the trigger, you execute that SP and pass some predefined table which is based on inserted table
See https://msdn.microsoft.com/en-AU/library/bb510489.aspx with example.

SQL Server: pause a trigger

I am working with SQL Server 2005 and I have trigger on a table that will copy an deletions into another table. I cannot remove this trigger completely. My problem is that we have now developed an archiving strategy for this table. I need a way of "pausing" a trigger when the stored proc that does the archiving runs.
A little more detail would be useful on how the procedure is accessing the data, but assuming you are just getting the data, then deleting it from the table and wish to disable the trigger for this process, you can do the following
DISABLE TRIGGER trg ON tbl;
then
ENABLE TRIGGER trg ON tbl;
for the duration of the procedure.
This only works for SQL 2005+
An alternative method is to use Context_Info to disable it for a single session, while allowing other sessions to continue to fire the trigger.
Context_Info is a variable which belongs to the session. Its value can be changed using SET Context_Info.
The trigger will mostly look like this:
USE AdventureWorks;
GO
-- creating the table in AdventureWorks database
IF OBJECT_ID('dbo.Table1') IS NOT NULL
DROP TABLE dbo.Table1
GO
CREATE TABLE dbo.Table1(ID INT)
GO
-- Creating a trigger
CREATE TRIGGER TR_Test ON dbo.Table1 FOR INSERT,UPDATE,DELETE
AS
DECLARE #Cinfo VARBINARY(128)
SELECT #Cinfo = Context_Info()
IF #Cinfo = 0x55555
RETURN
PRINT 'Trigger Executed'
-- Actual code goes here
-- For simplicity, I did not include any code
GO
If you want to prevent the trigger from being executed you can do the following:
SET Context_Info 0x55555
INSERT dbo.Table1 VALUES(100)
Before issuing the INSERT statement, the context info is set to a value. In the trigger, we are first checking if the value of context info is the same as the value declared. If yes, the trigger will simply return without executing its code, otherwise the trigger will fire.
source: http://www.mssqltips.com/tip.asp?tip=1591
if DISABLE TRIGGER/ENABLE TRIGGER is not an option for some reason, you can create a table with a single row which will serve as a flag for the trigger.

Resources