I have an Oracle procedure to add a row to the JOB_HISTORY table and a trigger to call the procedure when data is updated on two columns( job_id, department_id) in the table EMPLOYEES: I´m trying to rewrite them for Sql server 2008, can anyone help me to rewrite both of them please? I might have done it with the procedure but cannot do it with the trigger. any suggestion is welcome?
Procedure:
CREATE OR REPLACE PROCEDURE add_job_history
( p_emp_id job_history.employee_id%type
, p_start_date job_history.start_date%type
, p_end_date job_history.end_date%type
, p_job_id job_history.job_id%type
, p_department_id job_history.department_id%type )
IS BEGIN
INSERT INTO job_history (employee_id, start_date, end_date,job_id,department_id)
VALUES(p_emp_id,p_start_date,p_end_date,p_job_id,p_department_id);
END add_job_history;
Trigger:
CREATE OR REPLACE TRIGGER update_job_history
AFTER UPDATE OF job_id,department_id ON employees
FOR EACH ROW
BEGIN
add_job_history(:old.employee_id, :old.hire_date, sysdate,
:old.job_id,:old.department_id);
END;
This is how I wrote the procedure I´m not sure is doing the same thing as the one above though.
CREATE PROCEDURE add_job_history
(#p_emp_id INTEGER,
#p_start_date DATE,
#p_end_date DATE,
#p_job_id VARCHAR(10),
#p_department_id INTEGER ) AS
BEGIN
INSERT INTO job_history (employee_id, start_date, end_date,
job_id, department_id)
VALUES(#p_emp_id, #p_start_date, #p_end_date, #p_job_id,#p_department_id)
END ;
CREATE TRIGGER update_job_history ON (table_EMPLOYEES)
AFTER UPDATE
AS
BEGIN
if exists(select 1 From inserted as i inner join deleted as d on d.employee_id = i.employee_id where d.job_id != i.job_id or d.department_id != i.department_id)
begin
insert into job_history (employee_id, start_date, end_date,job_id,department_id)
select d.employee_id, d.start_date,d.end_date,d.job_id,d.department_id
from deleted as d
end
END;
thanks for your help #Long
I found The answer even if I still have to test the logic of it and i might change some inserted i. with d.
create trigger update_job_history on employees
after update
as
begin
set nocount on;
insert into job_history (employee_id, start_date, end_date ,job_id,department_id)
select i.employee_id, i.hire_date as start_date , d.hire_date as end_date, d.job_id ,d.department_id
From inserted as i inner join deleted as d on d.employee_id = i.employee_id where d.job_id != i.job_id or d.department_id != i.department_id
end
Related
I am trying to create a FUNCTION, TRIGGER, and PROCEDURE. Each time I try to run it, I get a Syntax Error on/around the CREATE line. Below is what I have:
------------------------------------------
CREATE FUNCTION summary_refresh_fun()
RETURNS TRIGGER
LANGUAGE plpgsql
AS $$
BEGIN
DELETE FROM summary;
INSERT INTO summary (
SELECT
concat_ws (', ', last_name, first_name) AS customer_name,
email,
COUNT(customer_id)
FROM detailed
GROUP BY customer_id, customer_name, email
-- HAVING count(customer_id) > 30
ORDER BY count(customer_id)DESC
LIMIT 100
);
RETURN NEW;
END; $$
----------------------------------------------
CREATE TRIGGER summary_refresh
AFTER INSERT ON detailed
FOR EACH STATEMENT
EXECUTE PROCEDURE summary_refresh_fun();
---------------------------------------------
CREATE PROCEDURE refresh_tables()
LANGUAGE plpgsql
AS $$
BEGIN
DELETE FROM detailed;
INSERT INTO detailed(
customer_id, -- customer
first_name, -- customer
last_name, -- customer
email, -- customer
rental_id, -- rental
rental_date, -- rental
return_date, -- rental
staff_id --rental
)
SELECT
c.customer_id, c.first_name, c.last_name, c.email,
r.rental_id, r.rental_date, r.return_date, r.staff_id
FROM rental AS r
INNER JOIN customer AS c ON c.customer_id = r.customer_id;
END;$$
I am getting errors at CREATE TRIGGER and CREATE PROCEDURE:
[Syntax Error on CREATE TRIGGER][1]
[Syntax Error on CREATE PROCEDURE][2]
Any idea on why this Syntax Error is occurring?
Using PostgreSQL 13
[1]: https://i.stack.imgur.com/TIyPP.png
[2]: https://i.stack.imgur.com/gL5ya.png
You need ; after END; $$ to separate create function from create trigger
I was attempting an INSERT INTO.... ( SELECT... ) (inserting a batch of rows from SELECT... subquery), onto the same table in my database. For the most part it was working, however, I did see a "Deadlock" exception logged every now and then. Does it make sense to do this or is there a way to avoid a deadlock scenario? On a high-level, my queries both resemble this structure:
CREATE OR REPLACE PROCEDURE myConcurrentProc() LANGUAGE plpgsql
AS $procedure$
DECLARE
BEGIN
LOOP
EXIT WHEN row_count = 0
WITH cte AS (SELECT *
FROM TableA tbla
WHERE EXISTS (SELECT 1 FROM TableB tblb WHERE tblb.id = tbla.id)
INSERT INTO concurrent_table (SELECT id FROM cte);
COMMIT;
UPDATE log_tbl
SET status = 'FINISHED',
WHERE job_name = 'tblA_and_B_job';
END LOOP;
END
$procedure$;
And the other script that runs in parallel and INSERTS... also to the same table is also basically:
CREATE OR REPLACE PROCEDURE myConcurrentProc() LANGUAGE plpgsql
AS $procedure$
DECLARE
BEGIN
LOOP
EXIT WHEN row_count = 0
WITH cte AS (SELECT *
FROM TableC c
WHERE EXISTS (SELECT 1 FROM TableD d WHERE d.id = tblc.id)
INSERT INTO concurrent_table (SELECT id FROM cte);
COMMIT;
UPDATE log_tbl
SET status = 'FINISHED',
WHERE job_name = 'tbl_C_and_D_job';
END LOOP;
END
$procedure$;
So you can see I'm querying two different tables in each script, however inserting into the same some_table. I also have the UPDATE... statement that writes to a log table so I suppose that could also cause issues. Is there any way to use BEGIN... END here and COMMIT to avoid any deadlock/concurrency issues or should I just create a 2nd table to hold the "tbl_C_and_D_job" data?
I have a straight forward trigger function that is set to run on UPDATE or INSERT in a table.
When this trigger runs, I want to insert the record into another table, only if it doesn't already exist there.
I could simply ignore checking and let the insert fail, but I feel like that's not the best approach.
-- Trigger
CREATE TRIGGER archivelogic_trigger AFTER INSERT OR UPDATE ON entsf.et4ae5__individualemailresult__c
FOR EACH ROW EXECUTE PROCEDURE entsf.archivelogicfunc();
-- Function
CREATE OR REPLACE FUNCTION entsf.archivelogicfunc() RETURNS TRIGGER AS $result_table$
BEGIN
BEGIN
IF (DATE(NEW.et4ae5__datesent__c) < NOW() - INTERVAL '180 days'
AND DATE(NEW.et4ae5__datesent__c) > NOW() - INTERVAL '540 days'
AND NEW.id NOT IN (SELECT id FROM archive.individualemailresult__c)) -- this seems expensive
THEN
INSERT INTO archive.individualemailresult__c
(dateopened__c,
numberoftotalclicks__c,
datebounced__c,
fromname__c,
hardbounce__c,
fromaddress__c,
softbounce__c,
name,
lastmodifieddate,
opened__c,
ownerid,
subjectline__c,
isdeleted,
contact__c,
systemmodstamp,
lastmodifiedbyid,
datesent__c,
dateunsubscribed__c,
createddate,
createdbyid,
lead__c,
tracking_as_of__c,
numberofuniqueclicks__c,
senddefinition__c,
mergeid__c,
triggeredsenddefinition__c,
sfid,
id,
_hc_lastop,
_hc_err)
VALUES
(NEW.et4ae5__dateopened__c,
NEW.et4ae5__numberoftotalclicks__c,
NEW.et4ae5__datebounced__c,
NEW.et4ae5__fromname__c,
NEW.et4ae5__hardbounce__c,
NEW.et4ae5__fromaddress__c,
NEW.et4ae5__softbounce__c,
NEW.name,
NEW.lastmodifieddate,
NEW.et4ae5__opened__c,
NEW.ownerid,
NEW.et4ae5__subjectline__c,
NEW.isdeleted,
NEW.et4ae5__contact__c,
NEW.systemmodstamp,
NEW.lastmodifiedbyid,
NEW.et4ae5__datesent__c,
NEW.et4ae5__dateunsubscribed__c,
NEW.createddate,
NEW.createdbyid,
NEW.et4ae5__lead__c,
NEW.et4ae5__tracking_as_of__c,
NEW.et4ae5__numberofuniqueclicks__c,
NEW.et4ae5__senddefinition__c,
NEW.et4ae5__mergeid__c,
NEW.et4ae5__triggeredsenddefinition__c,
NEW.sfid,
NEW.id,
NEW._hc_lastop,
NEW._hc_err);
END IF;
RETURN NULL;
END;
I added the line in my logic that checks to see if that ID exists in the other table, but I'm not sure if this is the best way to handle it?
AND NEW.id NOT IN (SELECT id FROM archive.individualemailresult__c)) -- this seems expensive
I'm trying to implement table partitioning with dynamic table creation using BEFORE INSERT trigger to create new tables and indexes when necesarry using following solution:
create table mylog (
mylog_id serial not null primary key,
ts timestamp(0) not null default now(),
data text not null
);
CREATE OR REPLACE FUNCTION mylog_insert() RETURNS trigger AS
$BODY$
DECLARE
_name text;
_from timestamp(0);
_to timestamp(0);
BEGIN
SELECT into _name 'mylog_'||replace(substring(date_trunc('day', new.ts)::text from 0 for 11), '-', '');
IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name=_name) then
SELECT into _from date_trunc('day', new.ts)::timestamp(0);
SELECT into _to _from + INTERVAL '1 day';
EXECUTE 'CREATE TABLE '||_name||' () INHERITS (mylog)';
EXECUTE 'ALTER TABLE '||_name||' ADD CONSTRAINT ts_check CHECK (ts >= '||quote_literal(_from)||' AND ts < '||quote_literal(_to)||')';
EXECUTE 'CREATE INDEX '||_name||'_ts_idx on '||_name||'(ts)';
END IF;
EXECUTE 'INSERT INTO '||_name||' (ts, data) VALUES ($1, $2)' USING
new.ts, new.data;
RETURN null;
END;
$BODY$
LANGUAGE plpgsql;
CREATE TRIGGER mylog_insert
BEFORE INSERT
ON mylog
FOR EACH ROW
EXECUTE PROCEDURE mylog_insert();
Everything works as expected but each day when concurrent INSERT statements are being fired for the first time that day, one of them fails trying to "create table that already exists". I suspect that this is caused by the triggers being fired concurrently and both trying to create new table and only one can succeed.
I could be using CREATE TABLE IF NOT EXIST but I cannot detect the outcome so I cannot reliably create constraints and indexes.
What can I do to avoid such problem? Is there any way to signal the fact that the table has been already created to other concurrent triggers? Or maybe there is a way of knowing if CREATE TABLE IF NOT EXISTS created new table or not?
What I do is create a pgAgent job to run every day and create 3 months of tables ahead of time.
CREATE OR REPLACE FUNCTION avl_db.create_alltables()
RETURNS numeric AS
$BODY$
DECLARE
rec record;
BEGIN
FOR rec IN
SELECT date_trunc('day', i::timestamp without time zone) as table_day
FROM generate_series(now()::date,
now()::date + '3 MONTH'::interval,
'1 DAY'::interval) as i
LOOP
PERFORM avl_db.create_table (rec.table_day);
END LOOP;
PERFORM avl_db.avl_partition(now()::date,
now()::date + '3 MONTH'::interval);
RETURN 0;
END;
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
ALTER FUNCTION avl_db.create_alltables()
OWNER TO postgres;
create_table is very similar to your CREATE TABLE code
avl_partition update the BEFORE INSERT TRIGGER but I saw you do that part with dynamic query. Will have to check again that.
Also I see you are doing inherit, but you are missing a very important CONSTRAINT
CONSTRAINT route_sources_20170601_event_time_check CHECK (
event_time >= '2017-06-01 00:00:00'::timestamp without time zone
AND event_time < '2017-06-02 00:00:00'::timestamp without time zone
)
This improve the query a lot when doing a search for event_time because doesn't have to check every table.
See how doesn't check all tables for the month:
Eventually I wrapped CREATE TABLE in BEGIN...EXCEPTION block that catches duplicate_table exception - this did the trick, but creating the tables upfront in a cronjob is much better approach performance-wise.
CREATE OR REPLACE FUNCTION mylog_insert() RETURNS trigger AS
$BODY$
DECLARE
_name text;
_from timestamp(0);
_to timestamp(0);
BEGIN
SELECT into _name 'mylog_'||replace(substring(date_trunc('day', new.ts)::text from 0 for 11), '-', '');
IF NOT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_name=_name) then
SELECT into _from date_trunc('day', new.ts)::timestamp(0);
SELECT into _to _from + INTERVAL '1 day';
BEGIN
EXECUTE 'CREATE TABLE '||_name||' () INHERITS (mylog)';
EXECUTE 'ALTER TABLE '||_name||' ADD CONSTRAINT ts_check CHECK (ts >= '||quote_literal(_from)||' AND ts < '||quote_literal(_to)||')';
EXECUTE 'CREATE INDEX '||_name||'_ts_idx on '||_name||'(ts)';
EXCEPTION WHEN duplicate_table THEN
RAISE NOTICE 'table exists -- ignoring';
END;
END IF;
EXECUTE 'INSERT INTO '||_name||' (ts, data) VALUES ($1, $2)' USING
new.ts, new.data;
RETURN null;
END;
$BODY$
LANGUAGE plpgsql;
I am not good at T-SQL. How can does following trigger into T-SQL? For each doesn't work in T-SQL.
CREATE OR REPLACE TRIGGER DSS.TRG_DEPO_STOK_IZLEME
BEFORE INSERT OR UPDATE
ON DSS.CR_DEPO_STOK FOR EACH ROW
BEGIN
INSERT INTO CR_DEPO_STOK_IZLEME
(ID_DEPO_STOK_IZLEME
, ID_DEPO_STOK
, MT_MIKTAR_ESKI
, MT_MIKTAR_YENI
, EKLEME_TARIHI
)
VALUES (SEQ_ID_DEPO_STOK_IZLEME.NEXTVAL
, :NEW.ID_DEPO_STOK
, :OLD.MT_MIKTAR
, :NEW.MT_MIKTAR
, SYSDATE
);
EXCEPTION
WHEN OTHERS
THEN
NULL;
END;
Probably something like
CREATE TRIGGER DSS.TRG_DEPO_STOK_IZLEME
ON DSS.CR_DEPO_STOK
AFTER INSERT, UPDATE
AS
BEGIN
INSERT INTO CR_DEPO_STOK_IZLEME
(ID_DEPO_STOK
, MT_MIKTAR_ESKI
, MT_MIKTAR_YENI
, EKLEME_TARIHI
)
SELECT i.ID_DEPO_STOK
, d.MT_MIKTAR
, i.MT_MIKTAR
, GETDATE()
FROM INSERTED i FULL OUTER JOIN DELETED d ON i.pk = d.pk
END;
There are no row triggers in TSQL also sequences will not appear until the next version so I have assumed that ID_DEPO_STOK_IZLEME will be an identity column.