I'm new to postgres and to programming and I already searched for solution for this but I couldn't quite get it. I'm trying to make a function that will return information about all the customers on that particular country whenever I call the country. This is the error that pops up. I'm really sorry for asking this but I've been stuck here since yesterday.
ERROR: query has no destination for result data
HINT: If you want to discard the results of a SELECT, use PERFORM instead.
CONTEXT: PL/pgSQL function country(text) line 5 at SQL statement
Here is the function:
create or replace function country(text) returns text as $$
begin
select customer_id, customer.first_name, customer.last_name
from customer
inner join address on customer.address_id = address.address_id
inner join city on address.city_id = city.city_id
inner join country on city.country_id = country.country_id
where country = '$1';
end;
$$
language plpgsql;
If you are executing a select statement in a PL/pgSQL function, then you should place the result of the query in some variable(s) (= the destination). Then you work with the variable(s) in the function. You should also have a RETURN statement.
create or replace function country(text) returns text as $$
declare -- declare some variables
id integer;
fname text;
lname text;
begin
select customer_id, customer.first_name, customer.last_name
into id, fname, lname -- store query results in variables
from customer
inner join address on customer.address_id = address.address_id
inner join city on address.city_id = city.city_id
inner join country on city.country_id = country.country_id
where country = $1; -- don't quote parameter references
-- do something with the variables and return a value from the function
return format('%s: %s %s', id, upper(lname), fname);
end;
$$ language plpgsql;
Do note that the above only works if the query returns a single row. If the query returns multiple rows you can use a loop in the function. Even simpler, you can just return the results from the query like so:
create or replace function country(text)
returns table (id integer, first_name varchar, last_name varchar) as $$
begin
return query
select customer_id, customer.first_name, customer.last_name
from customer
inner join address on customer.address_id = address.address_id
inner join city on address.city_id = city.city_id
inner join country on city.country_id = country.country_id
where country = $1;
end;
$$ language plpgsql;
But like Evan Carroll said, unless you need a PL/pgSQL function to modify the data before returning it, you are better off with a simple view.
CREATE OR REPLACE FUNCTIOn func_process_client_contact(p_client_version_id bigint, p_publish_id bigint)
RETURNS TABLE(status_message character varying)
LANGUAGE plpgsql
AS $function$
declare
p_rowcount int;
BEGIN
-- validating input parameters : start
IF ( p_client_version_id IS NULL OR p_publish_id IS NULL )
THEN
RETURN;
END IF;
-- validating input parameters : end
WITH cte_target AS
(
SELECT
g.name AS clientname,
gcr.group_id AS clientid,
cr.id AS clientrelationid,
crc.id AS clientrelationcontactid,
crd.id AS clientrelationdesignation_id
FROM GROUPS g
JOIN group_client_relation gcr ON gcr.group_id =g.id
JOIN client_relation cr ON cr.id = gcr.client_relation_id
JOIN client_relation_contact crc ON crc.client_relation_id =cr.id
JOIN client_relation_designation crd ON cr.client_relation_designation_id =crd.id
)
SELECT * FROM cte_target ct WHERE ct.clientname='ABC';
GET DIAGNOSTICS p_rowcount = ROW_COUNT;
RAISE NOTICE 'returned % rows', p_rowcount;
IF ( p_rowcount=0 )
THEN
RETURN query SELECT 'hello' AS status_message;
ELSE
RETURN query SELECT 'Success' AS status_message;
END IF;
END
$function$
Please use below to get result of given function..
create or replace function country(in_parameter text,out out_res refcursor) as $$
begin
open out_res for
select customer_id, customer.first_name, customer.last_name
from customer
inner join address on customer.address_id = address.address_id
inner join city on address.city_id = city.city_id
inner join country on city.country_id = country.country_id
where country = '$1';
end;
$$
language plpgsql;
This is not normal for SQL. Normally, this would be a VIEW.
CREATE VIEW myView AS
SELECT customer_id, customer.first_name, customer.last_name
FROM customer
INNER JOIN address USING (address_id)
INNER JOIN city USING (city_id)
INNER JOIN country USING (country_id);
Then you do
SELECT * FROM myView WHERE country = ?
All of that said, if you insist on making this a function, and you shouldn't, you should make it a LANAGUAGE SQL and not LANGUAGE plppsql.
It worked for my colleague when she used OPEN MYCURS before the select query and RETURN MYCURS after the select query.
There might be some situations where we want to call another psql function from a function. If we just want to invoke the function and don't assign the return value to anything, then using select inner_function_call(); inside the main function will throw this error. As the hint suggests, use perform inner_function_call() instead.
Related
I have this function, which for some reason is causing an issue with thw input parameter i pass to its execute statement.
DROP FUNCTION IF EXISTS ingoing_outgoing_reference_intigrity_breach(altered_table_name text, entity_ids bigint[]);
DROP TYPE IF EXISTS detection_intgrity_result;
CREATE or REPLACE AGGREGATE range_merge(anyrange)
(
sfunc = range_merge,
stype = anyrange
);
create type detection_intgrity_result
as (source_entity_id bigint,
source_valid tsrange,
causes_intigrity_breach boolean,
depending_entity_internal_name text,
depending_entity_id bigint,
depending_valid tsrange
);
CREATE OR REPLACE FUNCTION ingoing_outgoing_reference_intigrity_breach(altered_table_name text, entity_ids bigint[])
RETURNS setof detection_intgrity_result
AS $$
DECLARE
i record;
result_row detection_intgrity_result;
BEGIN
FOR i IN (
SELECT
tc.table_name, kcu.column_name,
ccu.table_name AS foreign_table_name
FROM
information_schema.table_constraints AS tc
JOIN
information_schema.key_column_usage AS kcu
ON
tc.constraint_name = kcu.constraint_name
JOIN
information_schema.constraint_column_usage AS ccu
ON
ccu.constraint_name = tc.constraint_name
WHERE
constraint_type = 'FOREIGN KEY' and
ccu.column_name != 'id' and
kcu.column_name != 'entity_id' and
ccu.table_name = altered_table_name OR tc.table_name = altered_table_name
and ccu.table_name != tc.table_name) LOOP
EXECUTE format('SELECT '||i.table_name||'.entity_id, '||i.table_name||'.'||i.column_name||',
range_merge('||i.foreign_table_name||'_registration.valid) #> range_merge('||i.table_name||'.valid) as lifespan,
range_merge('||i.foreign_table_name||'_registration.valid) <# range_merge('||i.table_name||'.valid) as reverse_lifespan
from '||i.foreign_table_name||'_registration, '||i.table_name||'
where '||i.table_name||'.'||i.column_name||' in $2
and '||i.table_name||'_registration.registration #> now()::timestamp
GROUP BY '||i.table_name||'.'||i.column_name||', '||i.table_name||'_registration.entity_id') using entity_ids;
END LOOP;
END
$$ LANGUAGE plpgsql;
select * from ingoing_outgoing_reference_intigrity_breach('country', '{1,12,1}')
Am i not able to pass input parameters to a execute statement?
The function look up tables which have ingoing and outgoing refereneces and ensure that data between those two is still valid.
There are three problems with that EXECUTE statement:
You use parameter $2, which corresponds to the second argument, but there is only a single argument.
You are trying to pass an array as IN list, which won't work. Use the equivalent, but correct
WHERE colname = ANY ($1)
You construct an SQL string with ||, which makes the code vulnerable to SQL injection. Use the format function with the %I pattern to make the code safe and more readable.
Following functions are created for doing housekeeping within the database (PostgreSQL 11.4).
entity_with_multiple_taskexec: returns a list of entities for which the housekeeping should be done.
row_id_to_delete: returns tuples of id's to delete
Just for completeness, the function which works fine:
CREATE OR REPLACE FUNCTION entity_with_multiple_taskexec()
RETURNS TABLE(entitykey varchar) AS
$func$
BEGIN
RETURN QUERY select distinct task.entitykey from
(select task.entitykey from task where dtype = 'PropagationTask' group by task.entitykey having count(*) > (select count(*) from conninstance)) more_than_one_entry
inner join task on task.entitykey = more_than_one_entry.entitykey
inner join taskexec on taskexec.task_id = task.id order by task.entitykey asc;
END
$func$ LANGUAGE plpgsql;
But which the second function, I'm not able to return a table, created from looping through the results of the entity_with_multiple_taskexec function;
CREATE OR REPLACE FUNCTION row_id_to_delete()
RETURNS TABLE(task_id varchar, taskexec_id varchar) AS
$func$
DECLARE
entityrow RECORD;
resultset RECORD;
BEGIN
FOR entityrow IN SELECT entitykey FROM entity_with_multiple_taskexec() LOOP
insert into resultset select task.id as task_id, taskexec.id as taskexec_id from task
inner join taskexec on taskexec.task_id = task.id where taskexec.entitykey = entityrow.entitykey order by taskexec.enddate desc offset 1
END LOOP;
RETURN resultset;
END
$func$ LANGUAGE plpgsql;
This breaks with the following error
ERROR: syntax error at or near "END"
LINE 12: END LOOP;
I've tried different approaches. What would be a good solution to return the table?
You don't need a loop, just join to the function as if it is a table.
There is also no need to use PL/pgSQL for this, a simple language sql function will be more efficient.
CREATE OR REPLACE FUNCTION row_id_to_delete()
RETURNS TABLE(task_id varchar, taskexec_id varchar) AS
$func$
select task.id as task_id, taskexec.id as taskexec_id
from task
join taskexec on taskexec.task_id = task.id
join entity_with_multiple_taskexec() as mt on mt.entitykey = taskexec.entitykey
order by taskexec.enddate desc
offset 1
$func$
LANGUAGE sql;
I have a recursive query which I want to use in PostgreSQL function, and it should return a Boolean value.
CREATE OR REPLACE FUNCTION store.is_item(object1 VARCHAR(40), object2 VARCHAR(40))
RETURNS BOOLEAN AS $$
BEGIN
WITH RECURSIVE externals AS (
SELECT object_id, used_id
FROM store.obj_depend
WHERE external = true
), history AS (
SELECT content_id AS id
FROM store.minfos
WHERE id= $2
UNION
SELECT externals.used_id
FROM externals
INNER JOIN history ON history.id = externals.object_id
),
PERFORM (SELECT c.id FROM store.cinfo AS c WHERE c.id = $1 INNER JOIN history
ON c.id = history.id);
RETURN FOUND;
END;
$$ LANGUAGE plpgsql;
When I try this it gives asyntax error at or near SELECT error.
The PERFORM is plpgsql statement and cannot be used inside any SQL command.
You can use PERFORM like proposed #klin, but then CTE is used inside a subquery, and subquery returns one row every time. Then a FOUND variable should be every time true.
In this case is better to use auxiliary variable as targer:
CREATE OR REPLACE FUNCTION store.is_item(object1 VARCHAR(40), object2 VARCHAR(40))
RETURNS BOOLEAN AS $$
DECLARE r record;
BEGIN
WITH RECURSIVE
externals AS (SELECT object_id, used_id
FROM store.obj_depend
WHERE external = true)
history AS (SELECT content_id AS id
FROM store.minfos
WHERE id = object2
UNION
SELECT externals.used_id
FROM externals
INNER JOIN history ON history.id = externals.object_id)
SELECT c.id
FROM store.cinfo AS c
INNER JOIN history ON c.id = history.id
WHERE c.id = $1
INTO r;
RETURN FOUND;
END;
$$ LANGUAGE plpgsql;
I am developing an application its database is Postgres 9.5
I am having the following plpgsql function
CREATE OR REPLACE FUNCTION public.getall_available_products(
IN start_day_id integer,
IN end_day_id integer)
RETURNS TABLE(id integer) AS
$BODY$
SELECT product_id As id
FROM product_days
WHERE available > 0
AND days_id BETWEEN start_day_id AND end_day_id
$BODY$
LANGUAGE sql VOLATILE
I need to use the result of the above function in a join query in another plpgsql
function
CREATE OR REPLACE FUNCTION public.get_available_product_details(
IN start_day_id integer,
IN end_day_id integer)
RETURNS SETOF record AS
$BODY$declare
begin
SELECT pd.days_id As pd_days_id, pd.id AS p_id, pd.name AS p_name
FROM product p JOIN product_days pd
Using(id)
WHERE pd.id in
Select * from
//here I need to use the result of the getall_available_products
//function
end;
$BODY$
LANGUAGE plpgsql VOLATILE
How should I use the result of the first function in the second function? where I specify with comments.
You can select from set / table returning functions like tables or views. In your case:
SELECT pd.days_id As pd_days_id, pd.id AS p_id, pd.name AS p_name
FROM product p JOIN product_days pd USING(id)
WHERE pd.id IN
(SELECT a.id FROM public.getall_available_products(start_day_id, end_day_id) a);
You may even join with functions:
SELECT pd.days_id As pd_days_id, pd.id AS p_id, pd.name AS p_name
FROM product p JOIN product_days pd USING(id)
JOIN public.getall_available_products(start_day_id, end_day_id) a ON pd.id = a.id;
This should give the same result.
Note: If you want pass column values as function arguments you should take a look at the relatively new keyword LATERAL.
I've aliased everything, but can't seem to get this query working without the
error column reference \"id\" is ambiguous"
It seems to work if i remove one of the joins, but i'm just confused as to why it won't work with two?
create function influence.person_info(user_id integer)
returns setof influence.person_object as
$$
declare
obj influence.person_object;
begin
select t1.email as a_email, t2.organisation_url as a_org, t3.first_name as a_first, t3.last_name as a_last into obj
from influence_private.person_account as t1
inner join influence_private.organisation_account as t2 on (t1.organisation_id = t2.id)
inner join influence.person as t3 on (t1.person_id = t3.id)
where id = $1;
return next obj;
end;
$$ LANGUAGE plpgsql stable;
Any pointers?
In your where clause you will need to explicitly state which id you are referring to.
If you check your query:
select t1.email as a_email, ...
from influence_private.person_account as t1
join influence_private.organisation_account as t2
on (t1.organisation_id = t2.id) -- here
join influence.person as t3
on (t1.person_id = t3.id) -- here
Both your tables (influence_private and influence) have column named id. Postgresql doesn't know which one you want to use so you have to use full name like t2.id.
The problem of your query is t2 and t3 both tables have a column called id. In the Where clause id is ambigous because it doesn't know what id you are refering (t2 or t3) specify it and it will work properly.
Example fix:
create function influence.person_info(user_id integer)
returns setof influence.person_object as
$$
declare
obj influence.person_object;
begin
select t1.email as a_email, t2.organisation_url as a_org, t3.first_name as a_first, t3.last_name as a_last into obj
from influence_private.person_account as t1
inner join influence_private.organisation_account as t2 on (t1.organisation_id = t2.id)
inner join influence.person as t3 on (t1.person_id = t3.id)
where t1.id = $1;
return next obj;
end;
$$ LANGUAGE plpgsql stable;