Postgresql Function for Inserting Data With Join - sql

i tried to do create postgres sql function to insert data from parameter that given in the function.
For example There are 2 tables:
Table Customer
Customer
customer_id
customer_name
customer_phone
Table Transaction
seller_id
customer_name
customer_phone
price
item_name
My current function looks like:
CREATE OR REPLATE FUNCTON add_transaction_detail(seller_id int, customer_id int, price int, item_name varchar)
RETURNS character varying
LANGUAGE plpsql
SECURITY DEFINER
AS $function$
DECLARE
seller_id ALIAS FOR $1;
customer_id ALIAS FOR $2;
BEGIN
INSERT INTO transaction(seller_id, customer_name, customer_phone, item_name, price)
SELECT seller_id, c.customer_name, c.customer_phone, item_name, price
FROM table1 t JOIN customer c ON t.customer_id=c.customer_id
END;
$function$
i found error in my insert code. is there any way to set the parameter into 1 table then join them ? thanks

Related

How to automaticaly fill a foreign key when a row is inserted in Postgresql

I am currently working under postgres 14.
I got two tables customers and contacts, and i want to automatically find the customer_id from the customer name when i add a new contact in the table contacts.
CREATE TABLE customers(
customer_id INT GENERATED ALWAYS AS IDENTITY,
customer_name VARCHAR(255) NOT NULL UNIQUE,
PRIMARY KEY(customer_id)
);
CREATE TABLE contacts(
contact_id INT GENERATED ALWAYS AS IDENTITY,
customer_id INT,
contact_name VARCHAR(255) NOT NULL UNIQUE,
PRIMARY KEY(contact_id),
CONSTRAINT fk_customer
FOREIGN KEY(customer_id)
REFERENCES customers(customer_id)
);
INSERT INTO customers(customer_name) VALUES('my_first_customer')
So from here i want to do something like that :
INSERT INTO contacts(customer_id, contact_name ) VALUES('my_first_customer',"thomas")
and i want to get this result in contacts table :
contact_id
customer_id
contact_name
1
1
"thomas"
i tried to make a function to change the value from name to id but get an error of type.
Because the the type error is catch before the trigger. Here is my function
CREATE FUNCTION get_id_fct()
RETURNS TRIGGER AS $get_id_fct$
DECLARE val_id INTEGER;
BEGIN
SELECT customer_id INTO val_id FROM customers WHERE customers.customer_id = NEW.customer_id;
NEW.customer_id := val_id;
RETURN NEW
END
$get_id_fct$ LANGUAGE plpgsql;
CREATE TRIGGER get_id
BEFORE INSERT ON contacts
FOR EACH ROW EXECUTE PROCEDURE get_id_fct();'
Is their a way around that ? or specific method to do this task that i don't know about ?
I am quite a beginner at SQL.
You can make a subquery in insert statement
INSERT INTO contacts(customer_id, contact_name ) VALUES (
(select customer_id from customers where customer_name = 'my_first_customer') ,'thomas');
This query will fail if there is more than one customer with given name, and insert a null value for customer_id if there is no customers with given name
Different approach (proposed by #wildplasser)
INSERT INTO contacts(customer_id, contact_name )
select customer_id,'thomas' from customers where customer_name = 'my_first_customer';
In this case, when there is no customer with given name, no row will be created. When there is more than one customer with given name, for each of them record will be created.
Or you can create view with INSTEAD OF trigger.
create view v_contacts as
select customer_name, contact_name from customers
join contacts on customers.customer_id = contacts.customer_id;
CREATE FUNCTION emp () RETURNS trigger AS $$
BEGIN
INSERT INTO contacts(customer_id, contact_name )
VALUES((select customer_id from customers where customer_name =
NEW.customer_NAME),NEW.contact_NAME);
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER v_contactst
INSTEAD OF INSERT ON v_contacts
FOR EACH ROW
EXECUTE FUNCTION emp();
Insert statement
INSERT INTO v_contacts(customer_name, contact_name ) VALUES('my_first_customer','thomas')
And just a note, that columns customer_name and contact_name are not unique so this code is not safe and throw error if customer_name didn't exists or there is more than one record with this customer_name

Postgresql : Best way to handle upsert with Jsonb data in Postgresql

Trying to handle Put/Patch request in the below SP along with insert if new primary values are recieved if I get unique Key constraint which is item_id in our case
Primary Key : ItemId
If no unique key constraint insert into view
If unique key constraint occurs i.e. json which has same itemId upsert its properties viz. quantity,status,etc.
Stored procedure
CREATE OR REPLACE FUNCTION sp_post_items1(i_data jsonb)
RETURNS TABLE(
fulfiller_id varchar,
item_id varchar,
order_id varchar,
status_id integer,
item_updated_time timestamp without time zone)
AS $function$
DECLARE
itemId1 varchar := null;
statusId1 integer := 1000;
quantity1 numeric;
begin
--SELECT t->>'itemId' itemId ,t->>'statusId' statusId,(t->>'quantity')::numeric quantity INTO itemId1,statusId1,quantity1 FROM jsonb_array_elements(i_data -> 'items') t ;
INSERT INTO vw_item_status_detail(
fulfiller_id,
item_id,
order_id,
status_id,
sku_code,
decoration_technology,
quantity,
item_updated_time)
SELECT
i_data->>'fulfillerId' fulfillerId,
t->>'itemId' itemId,
i_data->>'orderId' orderId,
1000,
t->>'skuCode' skuCode,
t->>'decorationTechnology' decorationTechnology,
(t->>'quantity')::numeric quantity ,
NOW()
FROM jsonb_array_elements(i_data -> 'items') t ;
exception when unique_violation then
update vw_item_status_detail v1 set quantity = coalesce (quantity1 , V1.quantity ), status_id = coalesce (statusId1, V1.status_id ), item_updated_time = now() where v1.item_id = itemId1 ;
RETURN QUERY SELECT
v.fulfiller_id fulfiller_id,
v.item_id item_id,
v.order_id order_id,
v.status_id status_id,
v.item_updated_time item_updated_time
FROM vw_item_status_detail v
WHERE (v.order_id = (SELECT i_data->>'orderId') )
AND (v.fulfiller_id = (SELECT i_data->>'fulfillerId'));
END;
$function$
LANGUAGE plpgsql;
Sample sp call along with JSON
select * from sp_post_items1('{"orderId": "newtestput1",
"fulfillerId":"kv0fdt6cx7",
"orderDetailsUrl":"het",
"items":[
{
"attributes":
[{"name":"OracleSku","value":"DWj"},{"name":"taskId","value":"33a1595-e36769876c52"},{"name":"height","value":"5.5"},
{"name":"width","value":"32.004"},{"name":"productFamily","value":"DWT"},{"name":"template","value":"GI-AST70W"},
{"name":"labelInfo","value":"DWU-ROHS,LMQ-DBLU-BLU"},{"name":"decorationTechnology","value":"laserEngraving"},
{"name":"material","value":"Rubber LMQ + LHY"},{"name":"XYZ_barcode","value":"21234348.1"},{"name":"orderType","value":"text"},
{"name":"scheduledShipDate","value":"2020-09-12T23:59:00"},{"name":"orderReference","value":"21235677.9"},{"name":"docRefUrl","value":""},{"name":"additionalInfo","value":""}],
"decorationTechnology":"laserEngraving","itemDescription":"Test Sku for Oracle testing",
"itemId":"item1",
"manufacturingUrl":"htighess",
"skuCode":"CIM-QYXB3789","productName":"Test Sku for Oracle testing","quantity":"2000","taskId":"33a1ea44-1f45-4f2d-9595-e36769876c52"
},
{
"attributes":
[{"name":"OracleSku","value":"DWT-XXX-B3LUX-BB-C"},{"name":"taskId","value":"33a1ea44-1f45-4f6c52"},{"name":"height","value":"5.5"},
{"name":"width","value":"32.004"},{"name":"productFamily","value":"DWT"},{"name":"template","value":"GIFTSET-DWT-INDX-AST70W"},
{"name":"labelInfo","value":"DWK-DBLU-BLU"},{"name":"decorationTechnology","value":"laserEngraving"},
{"name":"material","value":"Rubber LMQ + LHY"},{"name":"XYZ_barcode","value":"21234348.1"},{"name":"orderType","value":"text"},
{"name":"scheduledShipDate","value":"2020-09-12T23:59:00"},{"name":"orderReference","value":"21235677.9"},{"name":"docRefUrl","value":""},{"name":"additionalInfo","value":""}],
"decorationTechnology":"laserEngraving","itemDescription":"Test Sku for Oracle testing",
"itemId":"item2",
"manufacturingUrl":"httpdfg",
"skuCode":"CIM-QYXB3789","productName":"Test Sku for Oracle testing","quantity":"1000","taskId":"33a1edfge36769876c52"
}
]
}'::jsonb)
tried of using a for loop but its not the most optimized way to handle such scenario
Tried approach: 1.To create a temp table , but was not successful
We can user from / not in clause combination clause to insert as well as update in this case

Postgresql :Syntax issue in MERGE with JSON param in stored function

Facing syntax issue when I have json input, which needs to be insert/update values from JSON into the table which is a view
Expected
When Primary id matched with Item_id (PK) - update its corresponding values
When Primary id not matched with Item_id (PK) - insert into the view
Sample SP
drop function if exists sp_post_itemsx;
CREATE FUNCTION sp_post_itemsx(i_data jsonb)
RETURNS VOID
AS $function$
BEGIN
MERGE INTO vw_item_status_detail
USING
(SELECT
i_data->>'fulfillerId' fulfillerId,
t->>'itemId' itemId,
i_data->>'orderId' orderId,
1000,
t->>'skuCode' skuCode,
t->>'decorationTechnology' decorationTechnology,
(t->>'quantity')::numeric quantity ,
NOW()
FROM jsonb_array_elements(i_data -> 'items')) as t ON item_id = t.itemId
WHEN MATCHED THEN UPDATE SET
vw.quantity = t.quantity
WHEN NOT MATCHED THEN
INSERT INTO vw_item_status_detail(
fulfiller_id,
item_id,
order_id,
status_id,
sku_code,
decoration_technology,
quantity,
item_updated_time)
SELECT
i_data->>'fulfillerId' fulfillerId,
t->>'itemId' itemId,
i_data->>'orderId' orderId,
1000,
t->>'skuCode' skuCode,
t->>'decorationTechnology' decorationTechnology,
(t->>'quantity')::numeric quantity ,
NOW()
FROM jsonb_array_elements(i_data -> 'items') t ;
END;
$function$
LANGUAGE plpgsql;
ERROR: "vw_item_status_detail" is not a known variable
Not sure what is wrong with the syntax or MERGE doesn't work with insert for views table

SQL: Stored procedure

I am implementing a library management system in SQL. I have the following table structure and some values inserted in them:
create table books
(
IdBook number(5),
NameBook varchar2(35),
primary key(IdBook)
);
create table users
(
IdUsers number(5),
NameUser varchar2(20),
primary key(IdUsers)
);
create table borrowed
(
IdBorrowed number(5),
IdUsers number(5),
IdBook number(5),
DueDate date,
DateReturned date,
constraint fk_borrowed foreign key(IdUsers) references users(IdUsers),
constraint fk_borrowed2 foreign key(IdBook) references books(IdBook)
);
insert into books values(0,'FairyTale');
insert into books values(1,'Crime and Punishment');
insert into books values(2,'Anna Karenina');
insert into books values(3,'Norwegian Wood');
insert into users values(01,'Robb Dora');
insert into users values(02,'Pop Alina');
insert into users values(03,'Grozavescu Teodor');
insert into users values(04,'Popa Alin');
insert into borrowed values(10,02,3,'22-Jan-2017',null);
insert into borrowed values(11,01,1,'25-Jan-2017','19-Dec-2016');
insert into borrowed values(12,01,3,'22-Jan-2017',null);
insert into borrowed values(13,04,2,'22-Jan-2017','13-Dec-2016');
What I want now is that my db to allow "borrowing" books for the users(i.e insert into the borrowed table) that have no unreturned books(i.e date returned is not null) and if they have unreturned books I want to abandon the whole process. I thought to implement this in the following way:
create or replace procedure borrowBook(IdBorrowed in number,IdUsers number,IdBook number,DueDate date,DateReturned date) as begin
if exists (SELECT u.IdUsers, u.NameUser, b.DateReturned
FROM users u, borrowed b
WHERE u.IDUSERS = b.IdUsers and DateReturned is not null),
insert into borrowed values(IdBorrowed,IdUsers,IdBook,DueDate,DateReturned);
end borrowBook;
The above procedure does not check if the parameter I pass to this function is the same as the one in my select and I do not know how to do this and correctly insert a value in my table.
Any help would be much appreciated. Thank in advance!
You should not name your parameters the same as columns also used inside the procedure.
You can also simplify your procedure to a single INSERT statement, no IF required:
create or replace procedure borrowBook(p_idborrowed in number, p_idusers number, p_idbook number, p_duedate date, p_datereturned date)
as
begin
insert into borrowed (idborrowed, idusers, idbook, duedate, datereturned)
select p_idborrowed, p_idusers, p_idbook, p_duedate, p_datereturned
from dual
where not exists (select *
from users u
join borrowed b on u.idusers = b.idusers
and b.datereturned is not null);
end borrowBook;
It's also good coding style to explicitly list the columns for an INSERT statement. And you should get used to the explicit JOIN operator instead of using implicit joins in the where clause.
What about this one:
create or replace procedure borrowBook( p_IdBorrowed in number ,
p_IdUsers number ,
p_IdBook number ,
p_DueDate date ,
p_DateReturned date )
as
begin
if (SELECT COUNT(*)
FROM borrowed
WHERE IDUSERS = p_IdUsers
AND DateReturned IS NULL) = 0 THEN
insert into borrowed values (p_IdBorrowed ,
p_IdUsers ,
p_IdBook ,
p_DueDate ,
p_DateReturned );
end if ;
end borrowBook;
You would seem to want something like this:
create or replace procedure borrowBook (
in_IdBorrowed in number,
in_IdUsers number,
in_IdBook number,
in_DueDate date,
in_DateReturned date
) as
v_flag number;
begin
select (case when exists (select 1
from borrowed b
where b.IdUsers = in_IdUsers and b.DateReturned is not null
)
then 1 else 0
end)
into v_flag
from dual;
if (flag = 0) then
insert into borrowed
values(in_IdBorrowed, in_IdUsers, in_IdBook, in_DueDate, v_DateReturned);
end if
end -- borrowBook;

i am trying to execute the before insert trigger , but i m getting the sql errors

what i want to achieve is i have a table called orders.
i want to perform the before insert trigger on my orders table.i want to capture the
username of person performing INSERT into table.
one table called info which contain the user.
this is my code
create table orders
(
order_id int,
quantity int,
cost int,
total_cost int,
created_date datetime,
created_by varchar(20)
)
create trigger beforeInsertdata
before insert
on orders
for each row
declare
v_username varchar2(10);
begin
-- Find username of person performing INSERT into table
SELECT user INTO v_username
FROM info;
-- Update create_date field to current system date
:new.create_date := sysdate;
-- Update created_by field to the username of the person performing the INSERT
:new.created_by := v_username;
END;
--user information--
create table info
(
userid int ,
user_name varchar(10)
)
insert into info values(1,'vivek')
select * from info
Basically, triggers are classified into two main types:-
1)After Triggers (For Triggers)
2)Instead Of Triggers
and the syntax for trigger is
CREATE TRIGGER trigger_name ON table_name
[FOR|AFTER|INSTEAD OF] [INSERT|UPDATE|DELETE]
AS
//your code goes here
GO
NOTE : FOR keyword used for INSERT |UPDATE Command where as AFTER USED FOR DELETE Command.
It's hard to tell what you're really trying to do. I've modified your code sample so that it will work on SQL2K5 and made some assumptions about how you're wanting to use the connected user account.
CREATE TABLE orders (
order_id int,
quantity int,
cost int,
total_cost int,
created_date datetime,
created_by varchar(20)
);
CREATE TABLE info (
userid int,
user_name varchar(10)
);
INSERT INTO info
VALUES (1, 'vivek');
SELECT *
FROM info;
CREATE TRIGGER orders_InsteadOfInsert ON orders
INSTEAD OF INSERT AS BEGIN
SET NOCOUNT ON;
-- varchar(10) is to match your table, but probably should be larger
DECLARE #CurrentUser VarChar(10);
SELECT #CurrentUser = SYSTEM_USER;
IF (#CurrentUser NOT IN (SELECT user_name FROM info)) BEGIN
-- consider using an identity column for the key instead of this
INSERT INTO info (userid, user_name)
SELECT
ISNULL((SELECT MAX(userid) FROM info), 0) + 1,
#CurrentUser;
END;
INSERT INTO orders (order_id, quantity, cost, total_cost, created_date, created_by)
SELECT
INS.order_id,
INS.quantity,
INS.cost,
INS.total_cost,
GETDATE(),
#CurrentUser
FROM INSERTED INS;
END;