Execute INSERT INTO in PLPGSQL using variables as values - sql

I'm trying to create function, which adds a record with given variables as values. My code:
CREATE OR REPLACE FUNCTION ADD_FILM(id INTEGER, t VARCHAR, y INTEGER, p REAL) RETURNS VARCHAR AS $$
DECLARE
query VARCHAR;
BEGIN
query = 'insert into films (id_film, title, year_production, price) values ('||id||','||t||','||y||','||p||')';
EXECUTE query;
RETURN 'OK';
EXCEPTION
WHEN UNIQUE_VIOLATION THEN
RAISE NOTICE 'Incorrect ID, next available ID set';
RETURN 0;
END;
$$ LANGUAGE PLPGSQL;
SELECT ADD_FILM(1,'aaa','2020','10');
Following function execution ends with error. What's wrong with syntax?
ERROR: column "aaa" does not exist

I'd rather do like this:
CREATE OR REPLACE FUNCTION ADD_FILM(id INTEGER, t VARCHAR, y INTEGER, p REAL) RETURNS
VARCHAR AS $$
BEGIN
insert into films (id_film, title, year_production, price) values (id,t,y,p);
RETURN 'OK';
....
....
See this link: https://www.postgresqltutorial.com/postgresql-create-procedure/
And call it:
CALL ADD_FILM(1,'aaa','2020','10');

Another possible solution with EXECUTE:
create table films
(
id_film int,
title varchar,
year_production int,
price real
);
CREATE TABLE
CREATE OR REPLACE FUNCTION ADD_FILM(id INTEGER, t VARCHAR, y INTEGER, p REAL) RETURNS VARCHAR AS $$
DECLARE
query VARCHAR;
BEGIN
query = 'insert into films (id_film, title, year_production, price) values ($1,$2,$3,$4)';
EXECUTE query USING id, t, y, p;
RETURN 'OK';
EXCEPTION
WHEN UNIQUE_VIOLATION THEN
RAISE NOTICE 'Incorrect ID, next available ID set';
RETURN 0;
END;
$$ LANGUAGE PLPGSQL;
CREATE FUNCTION
SELECT ADD_FILM(1,'aaa','2020','10');
add_film
----------
OK
(1 row)
select * from films;
id_film | title | year_production | price
---------+-------+-----------------+-------
1 | aaa | 2020 | 10
(1 row)

Related

How to make an insert with a combination of a select and passed parameters in a stored function

I have two tables, e.g.:
CREATE TABLE table_1
(
one_column INTEGER,
two_column INTEGER,
three_column INTEGER
);
CREATE TABLE table_2
(
id SERIAL,
column_1 INTEGER,
column_2 INTEGER,
column_3 INTEGER,
name TEXT,
step INTEGER
);
I have a stored function which receives a number of parameters. Within the function, I need to INSERT a row into a table using a (dynamic?) combination of the results from a SELECT and two of the function parameters. Currently I've got something similar to the following pseudo-code...
CREATE OR REPLACE FUNCTION function_p (
p_id INTEGER,
p_name TEXT DEFAULT '',
p_step INTEGER DEFAULT NULL
)
RETURNS VOID AS $$
BEGIN
INSERT INTO table_2 (
column_1,
column_2,
column_3,
p_name,
p_step
)
SELECT
one_column,
two_column,
three_column
FROM table_1
WHERE id = p_id;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;
Should the INSERT be more like this?
INSERT INTO table_2 (
column_1,
column_2,
column_3,
name,
step
)
(SELECT
one_column,
two_column,
three_column
FROM table_1
WHERE id = p_id),
p_name,
p_step;
Use format to create the insert statement. Also, you problably want a procedure, e.g.
CREATE OR REPLACE PROCEDURE function_p (
p_name TEXT DEFAULT '',
p_step INT DEFAULT NULL
)
LANGUAGE plpgsql AS $$
BEGIN
EXECUTE format('
INSERT INTO table_2 (column_1,column_2,column_3,name,step)
SELECT one_column, two_column,three_column,%L,%s
FROM table_1',p_name,p_step);
END;
$$
Keep in mind that the columns number (and types) used in the INSERT statement has to match with those coming from the SELECT.
Demo: db<>fiddle

How to return multiple inserted rows from a Stored function with insert select statement?

Sample Problem with queries Link
Not able to get any inserted row in the RETURNING statement with INSERT and SELECT command even after adding RETURN NEXT; RETURN ;
SCHEMA FOR USER TABLE
create table "user" (name text not null, updated_time TIMESTAMP NOT
NULL DEFAULT CURRENT_TIMESTAMP );
function which needs to be updated to return inserted rows
CREATE OR REPLACE FUNCTION testFn()
RETURNS table (name character varying , updated_time timestamp without time zone ) AS $$ DECLARE BEGIN
insert into "user" (name , updated_time)
select 'alex',now()
union
select 'alex2',now()
returning name, updated_time;
END;
$$ LANGUAGE plpgsql;
This function only inserts into DB but doesnt return the inserted rows with updated time on calling the function
it return no output on
select * from testFn()
Make it a language sql function:
CREATE OR REPLACE FUNCTION testFn()
RETURNS table (name character varying , updated_time timestamp without time zone )
AS
$$
insert into "user" (name , updated_time)
values
('alex', now()),
('alex2', now())
returning "user".name, "user".updated_time;
$$
LANGUAGE sql;
With a language plpgsql you would need a return query, rather then just putting the insert into it.
CREATE OR REPLACE FUNCTION testFn()
RETURNS table (name character varying , updated_time timestamp without time zone )
AS
$$
begin
return query
insert into "user" (name , updated_time)
values
('alex', now()),
('alex2', now())
returning "user".name, "user".updated_time;
end;
$$
LANGUAGE plpgsql;
To run the function use:
select *
from testfn();
Online example
It is much simpler to do with a language sql function. Try this (based on the previous version of your function):
CREATE OR REPLACE FUNCTION sp_post_items(i_data json)
RETURNS table (fulfiller_id text,item_id text, order_id text, status_id integer, item_updated_time timestamp)
AS $function$
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',
t->>'itemId',
i_data->>'orderId',
1000,
t->>'skuCode',
t->>'decorationTechnology',
10,
now()
from json_array_elements(i_data -> 'items') t
returning fulfiller_id, item_id, order_id, status_id, item_updated_time;
$function$
LANGUAGE sql;
or for the updated function:
CREATE OR REPLACE FUNCTION testFn()
RETURNS table (name text, updated_time timestamp without time zone ) AS
$$
insert into "user" (name , updated_time)
select 'alex',now()
union
select 'alex2',now()
returning name, updated_time;
$$ LANGUAGE sql;

How correctly create multiple entries by arrays in PostgreSQL?

In PostgreSQL database I have table which looks like this:
| question_id | question_text | widget | required | position |
|-------------|---------------|--------|----------|----------|
| int | text | int | boolean | int |
Second table which called factors_questions_relationship looks like this:
| factor_id | question_id |
|-------------|---------------|
| int | text |
I am trying to create function which would create multiple rows and return array of ids of new created entries. How correctly to make such function?
CREATE OR REPLACE FUNCTION factorio(
FACTOR_IDENTIFIER INT,
TEXT_ARR VARCHAR[],
WIDGET_ARR INT[],
REQUIRED_ARR BOOLEAN[],
POSITION_ARR INT[]
) RETURNS SETOF INT AS $$
BEGIN
RETURN QUERY
WITH RESULT_SET AS (
INSERT INTO QUESTIONS (TEXT, WIDGET, REQUIRED, POSITION)
SELECT
UNNEST(ARRAY[TEXT_ARR]) AS TEXT,
UNNEST(ARRAY[WIDGET_ARR]) AS WIDGET,
UNNEST(ARRAY[REQUIRED_ARR]) AS REQUIRED,
UNNEST(ARRAY[POSITION_ARR]) AS POSITION
RETURNING ID
)
--
INSERT INTO factors_questions_relationship (FACTOR_ID, QUESTION_ID)
SELECT FACTOR_IDENTIFIER FACTOR_ID, QUESTION_ID FROM UNNEST(ARRAY[array_agg(SELECT ID FROM RESULT_SET)]) QUESTION_ID
--
SELECT ID FROM RESULT_SET;
END;
$$ LANGUAGE plpgsql;
You can simply unnest them in columns
select
unnest(array['quick','brown','fox']) as question_text,
unnest(array[1,2,3]) as widget_id
Whereas putting them in FROM clause, would result to cartesian product:
select question_text, widget_id
from
unnest(array['quick','brown','fox']) as question_text,
unnest(array[1,2,3]) as widget_id
Output:
To obtain the Ids generated from newly-inserted records, use return query + returning id combo. Sample test:
create table z
(
id int generated by default as identity primary key,
question_text text
);
create or replace function insert_multiple_test()
returns table (someid int) as
$$
begin
return query
with resulting_rows as
(
insert into z(question_text) values
('hello'),
('你好'),
('hola')
returning id
)
select id from resulting_rows;
end;
$$ language 'plpgsql';
select * from insert_multiple_test();
Output:
Here's for SETOF:
create table z(id int generated by default as identity primary key, question_text text);
create or replace function insert_multiple_test()
returns setof int
as
$$
begin
return query
with resulting_rows as
(
insert into z(question_text) values
('hello'),
('你好'),
('hola')
returning id
)
select id from resulting_rows;
end;
$$ language 'plpgsql';
select x.the_id from insert_multiple_test() as x(the_id);
Output:
If you don't need to run multiple queries, you can just use LANGUAGE 'sql', it's simpler:
create table z
(
id int generated by default as identity primary key,
question_text text
);
create or replace function insert_multiple_test()
returns setof int as
$$
insert into z(question_text) values
('hello'),
('你好'),
('hola')
returning id;
$$ language 'sql';
select x.id_here from insert_multiple_test() as x(id_here);
Output:

How to return PK from insert query?

I have a postgresql table with sequence:
CREATE TABLE A (
id integer NOT NULL DEFAULT nextval('a_seq'::regclass),
X integer,
Y integer,
Z boolean default false,
CONSTRAINT A_pkey PRIMARY KEY (id)
)
I have an insert statment in function as follows:
insert into A(x,y) select $1,getdig();
i want this insert to return the id the row was given to a function varabile called A_id
It should be something like:
CREATE OR REPLACE FUNCTION bbb(m integer)
RETURNS integer AS
$BODY$
declare
A_id int;
begin
insert into A(x,y) select $1,getdig() RETURNING id into A_id;
actions using A_id like:
update A set z=True where id=A_id;
end;
$BODY$
LANGUAGE plpgsql VOLATILE
How do I do that?
There is no need for the select:
CREATE OR REPLACE FUNCTION bbb(m integer)
RETURNS integer AS
$BODY$
declare
A_id int;
begin
insert into A(x,y)
values ($1,getdig())
RETURNING id into A_id;
-- actions using A_id like:
update A set z=True where id=A_id;
return a_id; -- don't forget to return something!
end;
$BODY$
LANGUAGE plpgsql VOLATILE
You use the returning clause:
with i as (
insert into A(x,y)
select $1, getdig()
returning id
)
select *
from i;
Technically, the CTE is not necessary. But I prefer that queries that return values start with SELECT.

ERROR: query has no destination for result data

i have created a function in PostgreSQL to insert to the following
CREATE TABLE gtab83
(
orderid integer NOT NULL DEFAULT nextval('seq_gtab83_id'::regclass),
acid integer,
slno integer,
orderdte date
)
and created Function is
CREATE OR REPLACE FUNCTION funcInsert(iacid int,islno int,idate date) RETURNS int AS
$$
declare id_val int;
BEGIN
INSERT INTO GTAB83 (acid,slno,orderdte) VALUES (iacid,islno,idate) RETURNING orderid
into id_val;
return id_val;
END;
$$
LANGUAGE plpgsql;
when a execute the above function
select funcInsert(666,13,'2014-06-06'
getting this error
ERROR: query has no destination for result data
CONTEXT: PL/pgSQL function procgtab83(integer,integer,date) line 3 at SQL statement
create or replace function funcinsert(iacid int, islno int, idate date)
returns int as $$
declare id_val int;
begin
with i as (
insert into gtab83 (acid,slno,orderdte)
values (iacid,islno,idate)
returning orderid
)
select orderid into id_val
from i;
return id_val;
end;
$$ language plpgsql;
It can be much simpler as plain sql
create or replace function funcinsert(iacid int, islno int, idate date)
returns int as $$
insert into gtab83 (acid,slno,orderdte)
values (iacid,islno,idate)
returning orderid
;
$$ language sql;
This code is working:
postgres=# create table xx(a int);
CREATE TABLE
postgres=# create or replace function bu(int) returns int as
$$declare x int;
begin
insert into xx values($1) returning a into x;
return x;
end $$ language plpgsql;
CREATE FUNCTION
postgres=# select bu(10);
bu
────
10
(1 row)
And because it is same code as your, I expect, so you use some very old Postgres. I remember similar bug in pg, but it is more than five years fixed.