create table public.orders ( orderID serial PRIMARY KEY, orderdate timestamp NOT NULL ); create table public.orderdetails ( orderdetailID serial PRIMARY KEY, orderID integer REFERENCES public.orders(orderID), item varchar(20) NOT NULL, quantity INTEGER NOT NULL );
I have (very simplified sample) tables as above, into which I want to insert details of an order and order details in one action.
I am familiar with transactions, and could insert data with an SQL command like the below:
DO $$ DECLARE inserted_id integer; BEGIN INSERT INTO public.orders(orderdate) VALUES (NOW()) RETURNING orderID INTO inserted_id; INSERT INTO public.orderdetails(orderID, item, quantity) VALUES (inserted_id, 'Red Widget', 10), (inserted_id, 'Blue Widget', 5); END $$ LANGUAGE plpgsql;
However, ideally I’d like to have a query like the above a function if possible, rather than being stored within my application.
Could anyone point me in the right direction for supplying multiple records to a postgres function? Alternatively, if what I am looking to do is considered bad practice, please let me know what other route I should follow.
Thanks in advance.
Advertisement
Answer
You can use an array of tuples to pass multiple rows to the function. You need a custom type:
create type order_input as ( item text, quantity integer);
Use array of this type for an argument of the function:
create or replace function insert_into_orders(order_input[]) returns void language plpgsql as $$ declare inserted_id integer; begin insert into public.orders(orderdate) values (now()) returning orderid into inserted_id; insert into public.orderdetails(orderid, item, quantity) select inserted_id, item, quantity from unnest($1); end $$;
Usage:
select insert_into_orders( array[ ('Red Widget', 10), ('Blue Widget', 5) ]::order_input[] ); select * from orderdetails; orderdetailid | orderid | item | quantity ---------------+---------+-------------+---------- 1 | 1 | Red Widget | 10 2 | 1 | Blue Widget | 5 (2 rows)