transform oracle query to postgresql - postgresql

I am trying to pass a query from oracle to POSTGRESQL and it has not worked for me, how can I do it? the code is the following:
Oracle:
create function updateinsert
create cursor xx
select id
from "CCRs"
where status = 'POR REGULARIZAR'
and id in (221424,188790);
begin
open cursor xx
loop
update ccrs
set status ='ANULADO',
isActive = false,
cancelationDate = now(),
updatedAt= now()
where id = xx.id;
INSERT INTO public.CCRHistories
(status, createdAt, updatedAt, ccrId, userId)
VALUES('ANULADO', now(), now(),xx.id , 2438);
end loop;
end;

No need for a LOOP or PL/pgSQL. This can be done with a single statement that uses a data modifying common table expression to copy the updated rows into the history table:
with updated as (
update "CCRs"
set status = 'ANULADO',
isActive = false,
cancelationDate = now(),
updatedAt= now()
where id = in (select id
from "CCRs"
where status = 'POR REGULARIZAR'
and id in (221424,188790))
returning id, status
)
INSERT INTO public.CCRHistories (status, createdAt, updatedAt, ccrId, userId)
select status, now(), now(), upd.id, 2438
from updated upd;
If "CCRs".id is unique, you can get rid of the subquery
with updated as (
update "CCRs"
set status = 'ANULADO',
isActive = false,
cancelationDate = now(),
updatedAt= now()
where id in (221424,188790)
and status = 'POR REGULARIZAR'
returning id, status
)
INSERT INTO public.CCRHistories (status, createdAt, updatedAt, ccrId, userId)
select status, now(), now(), upd.id, 2438
from updated upd;

I think that would be everything:
DO $$
declare
registro record;
cur_id cursor for select id
from
"CCRs"
where
status = 'POR REGULARIZAR' and id in (204);
begin
open cur_id;
fetch cur_id
into
registro;
while (found) loop
update
"CCRs"
set
status = 'ANULADO',
isActive = false,
cancelationDate = now(),
updatedAt = now()
where
id = registro.id;
insert
into
CCRHistories
(status,
createdAt,
updatedAt,
ccrId,
userId)
values('ANULADO',
now(),
now(),
registro.id,
2438);
fetch cur_id
into
registro;
end loop;
end $$ LANGUAGE 'plpgsql';

Related

Postgresql update multiple rows with same name and id, and update the consecutive rows vrersion

I have a table where insertion is in this form.
Table
I want the verion to get update by 1 whenever there is a new row with same name and id.
Required output
I tried using a function and trigger.
CREATE OR REPLACE FUNCTION update_ver()
RETURNS TRIGGER
LANGUAGE PLPGSQL
AS
$$
BEGIN
update version
set ver = ver + 1
where new.name = 'A' and new.id ='1';
RETURN new;
END;
$$
-- create table
CREATE TABLE mytable (
"name" varchar NULL,
id int4 NULL,
phone varchar NULL,
email varchar NULL,
ver int4 NULL
);
-- create trigger function
CREATE OR REPLACE FUNCTION before_insert()
RETURNS TRIGGER
LANGUAGE PLPGSQL
AS
$$
begin
new.ver = (select coalesce(max(ver), 0) + 1 from mytable where name = new.name and id = new.id);
return new;
end;
$$
-- set trigger function to table
create trigger trg_before_insert before
insert
on
mytable for each row execute function before_insert();
-- inserting sample data
INSERT INTO mytable ("name", id, phone, email) VALUES('A', 1, '123', '123#email.com');
INSERT INTO mytable ("name", id, phone, email) VALUES('B', 2, '345', '345#email.com');
INSERT INTO mytable ("name", id, phone, email) VALUES('A', 1, '456', '456#email.com');
-- select data and view
select * from mytable;
Result:
name|id|phone|email |ver|
----+--+-----+-------------+---+
A | 1|123 |123#email.com| 1|
B | 2|345 |345#email.com| 1|
A | 1|456 |456#email.com| 2|

trigger to set date automatic after update

Some background info: i have a table named defects which has column named status_id and another column named date_closed ,i want to set date_closed after status_id has been updated
i already try to do this using after update trigger with the following code:
after update on eba_bt_sw_defects
for each row
declare
l_status number(20) := null;
begin
select status_id into l_status from eba_bt_sw_defects D,eba_bt_status S where D.status_id = S.id;
if l_status in ( select id from eba_bt_status where is_open = 'N' and NVL(is_enhancement,'N')='N') then
:NEW.DATE_CLOSED := LOCALTIMESTAMP ;
end if;
end;
but an error occured ( subquery not allowed in this contextCompilation failed)
i want a help
A couple of things that need fixing in your code:
In a trigger do not select from the table the trigger you're on. This will probably raise a ORA-04091: table name is mutating, trigger/function may not see it error.
IF l_variable IN (SELECT ...) is not a valid oracle syntax. It raises PLS-00405: subquery not allowed in this context
I don't have your data so here is a similar example:
drop table todos;
drop table statuses;
-- create tables
create table statuses (
id number generated by default on null as identity
constraint statuses_id_pk primary key,
status varchar2(60 char),
is_open varchar2(1 char) constraint statuses_is_open_ck
check (is_open in ('Y','N'))
)
;
create table todos (
id number generated by default on null as identity
constraint todos_id_pk primary key,
name varchar2(255 char) not null,
close_date timestamp with local time zone,
status_id number
constraint todos_status_id_fk
references statuses on delete cascade
)
;
-- load data
insert into statuses (id, status, is_open ) values (1, 'OPEN', 'Y' );
insert into statuses (id, status, is_open ) values (2, 'COMPLETE', 'N' );
insert into statuses (id, status, is_open ) values (3, 'ON HOLD', 'Y' );
insert into statuses (id, status, is_open ) values (4, 'CANCELLED', 'N' );
commit;
insert into todos (name, close_date, status_id ) values ( 'Y2 Security Review', NULL, 1 );
-- triggers
CREATE OR REPLACE TRIGGER todos_biu BEFORE
INSERT OR UPDATE ON todos
FOR EACH ROW
DECLARE
l_dummy NUMBER;
BEGIN
SELECT
1
INTO l_dummy
FROM
statuses
WHERE
is_open = 'N' AND
id = :new.status_id;
:new.close_date := localtimestamp;
EXCEPTION
WHEN no_data_found THEN
-- I'm assuming you want close_date to NULL if todo is re-opened.
:new.close_date := NULL;
END todos_biu;
/
update todos set status_id = 2;
select * from todos;
id name close_date status_id
1 Y2 Security Review 11-MAY-22 05.27.04.987117000 PM 2

PostgreSQL Function and IF STATEMENT gives: "WARNING: there is no transaction in progress COMMIT"

I'm running PostgreSQL 13.0
This is what the SQL query gives when I try to execute it. It doesn't run.
the sql query:
CREATE OR REPLACE FUNCTION cut_and_paste_hobby_to_interest(integer, text) RETURNS BOOL
LANGUAGE plpgsql AS
BEGIN
IF (SELECT COUNT(*) FROM interests WHERE interest_name = (SELECT hname FROM hobbies WHERE id = $1)) = 0
THEN
INSERT INTO interests (interest_name, interest_type, inserted_at, updated_at) VALUES ((SELECT hname FROM hobbies WHERE id = $1), $2, '2021-07-02 12:23:22', '2021-07-02 12:23:22');
UPDATE user_things
SET
thingable_type = $2,
thingable_id = (SELECT currval('things_id_seq')),
inserted_at = '2021-07-02 12:23:22',
updated_at = '2021-07-02 12:23:22'
WHERE thingable_id IN ($1) AND thingable_type = 'Hobby';
DELETE FROM hobbies_tags
WHERE hobby_id IN ($1);
DELETE FROM hobbies
WHERE id IN ($1);
DELETE FROM user_things
WHERE thingable_id IN ($1) AND thingable_type = 'Hobby';
SELECT TRUE;
ELSE
SELECT FALSE;
END IF;
END;
this worked
CREATE OR REPLACE FUNCTION cut_and_paste_hobby_to_interest(integer, text) returns int as $$
BEGIN
IF (SELECT COUNT(*) FROM interests WHERE interest_name = (SELECT hname FROM hobbies WHERE id = $1)) = 0 THEN
INSERT INTO interests (interest_name, interest_type, inserted_at, updated_at) VALUES ((SELECT hname FROM hobbies WHERE id = $1), $2, '2021-07-02 12:23:22', '2021-07-02 12:23:22');
UPDATE user_things SET
thingable_type = $2,
thingable_id = (SELECT currval('interests_id_seq')),
inserted_at = '2021-07-02 12:23:22',
updated_at = '2021-07-02 12:23:22'
WHERE thingable_id IN ($1) AND thingable_type = 'Hobby';
DELETE FROM hobbies_tags
WHERE hobby_id IN ($1);
DELETE FROM hobbies
WHERE id IN ($1);
DELETE FROM user_things
WHERE thingable_id IN ($1) AND thingable_type = 'Hobby';
return 1;
ELSE
return 2;
END IF;
END; $$ language plpgsql;

How do I properly use sp_send_dbmail in a trigger?

I've created a trigger on a table called Project, the query successfully compiled however when I go to insert data into the table I get an error stating:
Msg 14636, Level 16, State 1, Procedure sp_send_dbmail, Line 112 [Batch Start Line 139]
No global profile is configured. Specify a profile name in the #profile_name parameter.
As well as:
Msg 3930, Level 16, State 1, Procedure sp_send_dbmail, Line 64 [Batch Start Line 139]
The current transaction cannot be committed and cannot support operations that write to the log file. Roll back the transaction.
I'm not completely familiar with the sp_send_dbmail procedure but I tried to have it so when a trigger is triggered it send an email out to xxxxx#gmail.com.
--Create a Table to hold the project audit
CREATE TABLE [dbo].[ProjectAudit](
projectId char(4) not null,
projectName varchar(50) null,
fundedbudget decimal(16,2) null,
firmFedID char(9) null,
statusID varchar(25) null,
projectTypeID char(4) null,
startDate date null,
projectedEndDate date null,
projectManager char(8) null,
dateTimeCreated smalldatetime null,
operation varchar(50),
userName varchar(50)
)
go
-- Project trigger
create TRIGGER trg_ProjectAudit
ON Project
After Insert,Delete,Update
AS
Begin
declare #name varchar(50)
declare #body varchar(100)
if exists(select projectId from inserted)
BEGIN
select #name = projectName
from inserted
set #name = #name + ' has been inputted into the project table.'
INSERT INTO ProjectAudit
(projectId, projectName, fundedbudget, firmFedID, statusID, projectTypeID,
startDate, projectedEndDate, projectManager, operation, dateTimeCreated, userName)
SELECT projectId, projectName, fundedbudget, firmFedID, statusID, projectTypeID,
startDate, projectedEndDate, projectManager, 'INSERT', getdate(), System_user
FROM Inserted
exec msdb.dbo.sp_send_dbmail #recipients = 'xxxxx#gmail.com',
#body = #name
END
if exists(select projectId from deleted)
begin
select #name = projectName
from deleted
set #name = #name + ' has been deleted from the project table.'
INSERT INTO ProjectAudit
(projectId, projectName, fundedbudget, firmFedID, statusID, projectTypeID,
startDate, projectedEndDate, projectManager, operation, dateTimeCreated, userName)
SELECT projectId, projectName, fundedbudget, firmFedID, statusID, projectTypeID,
startDate, projectedEndDate, projectManager, 'DELETE', getdate(), System_user
FROM deleted
exec msdb.dbo.sp_send_dbmail #recipients = 'xxxxx#gmail.com',
#body = #name
end
--check if a column update
if (update(projectid) or update(projectTypeID))
begin
print 'ProjectID or ProjectTypeID column was updated'
exec msdb.dbo.sp_send_dbmail #recipients = 'xxx#gmail.com',
#body = 'Data has been updated in the project table.'
end
End
GO

How to ignore an INSERT failed because of DUPLICATE KEY?

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.