PostgreSQL: subquery must return one column - postgresql

Looking obvious error, still see no chance to find it. I've made to localize error in this function:
CREATE OR REPLACE FUNCTION findRecipientsByQuestion(questionId BIGINT)
RETURNS SETOF BIGINT AS $$
DECLARE
question questionObject;
BEGIN
question := (
SELECT "a"."id", "a"."body", "a"."author_id", "a"."category_id", "a"."urgent", "a"."created_at", "a"."locale_id", "a"."lat", "a"."lng", "a"."radius"
FROM "question" "a"
WHERE "a"."id"=questionId
LIMIT 1
);
RETURN QUERY SELECT "a"."id"
FROM "user" "a" INNER JOIN "notifications" "b" ON ("a"."id"="b"."user_id")
WHERE ("b"."category_id"=question.category_id OR "b"."urgent") AND
isGeoMatch("a"."lat", "a"."lng", "a"."radius", question.lat, question.lng, question.radius);
END
$$LANGUAGE plpgsql;
Which uses this type:
CREATE TYPE questionObject AS (
id BIGINT,
body VARCHAR,
author_id BIGINT,
category_id BIGINT,
urgent BOOLEAN,
created_at TIMESTAMP,
locale_id BIGINT,
lat DOUBLE PRECISION,
lng DOUBLE PRECISION,
radius INTEGER
);
And I'm getting this error in runtime:
Error: subquery must return only one column

I would just get rid off all the complexity and make it plain sql:
create or replace function findrecipientsbyquestion (
_questionid bigint
) returns setof bigint as $$
select a.id
from
user a
inner join
notifications b on a.id = b.user_id
inner join (
select categoty_id, lat, lng, radius
from question
where id = _questionid
limit 1
) q on q.category_id = b.category_id or b.urgent
where isgeomatch(a.lat, a.lng, a.radius, q.lat, q.lng, q.radius);
$$ language sql;

with my type
CREATE TYPE map.get_near_link AS
(link_id integer,
distance integer,
direction integer,
geom public.geometry(4));
I do
sRow map.get_near_link;
SELECT i.Link_ID, i.Distance, i.Direction, i.geom into sRow
FROM
index_query i;

Related

postgis function correctly returns table with geom but not picked up by pg_featureserv

Using pg_featureserv
wrote a function that returns a table + geometry
CREATE OR REPLACE FUNCTION public.nearest_retail(lon float, lat float)
RETURNS table(id int,
dba_name varchar,
entity_name varchar,
category varchar,
address varchar,
notes varchar,
distance float,
geom geometry
)
AS $$
select t.uid,dba_name,entity_name,md2.category,address,notes,distance,st_union(shape,geom) geom
from
( select distinct on(category) category,uid,
st_distance(st_transform(geom,2263),st_transform(st_setsrid(st_makepoint(lon, lat),4326),2263)) distance,
st_shortestline(geom,st_setsrid(st_makepoint(lon, lat),4326)) shape
from merged_datasets md
where st_dwithin(st_transform(st_setsrid(st_makepoint(lon, lat),4326),2263),st_transform(geom,2263),5280)
and category is not null
)t join merged_datasets md2 using(uid);
$$ LANGUAGE sql;
calling the function in the DB itself it works as its supposed to, however I cannot get it to show up in the pg_featureserv
The doc says
Because there are usually many functions in a Postgres database, the service only publishes functions defined in the postgisftw schema.

CREATE TABLE is not allowed in a non-volatile function

I have the following three tables :
create table drugs(
id integer,
name varchar(20),
primary key(id)
);
create table prescription(
id integer,
drug_id integer,
primary key(id),
foreign key(drug_id) references drugs(id)
);
create table visits(
patient_id varchar(10),
prescription_id integer,
primary key( patient_id , prescription_id),
foreign key(prescription_id) references prescription(id)
);
I wrote the following function on these tables to show me a patient's drugs list(the patient id is parameter):
CREATE OR REPLACE FUNCTION public.patients_drugs(
patientid character varying)
RETURNS TABLE(drug_id integer, drug_name character varying)
LANGUAGE 'plpgsql'
COST 100
STABLE STRICT
ROWS 1000
AS $BODY$
begin
create temporary table result_table(
drug_id integer,
drug_name varchar(20)
);
return query select distinct drug.id , drug.name
from visits join prescription
on visits.patient_id = patientID;
end;
$BODY$;
However, it gives me this error:
CREATE TABLE is not allowed in a non-volatile function
You don't need to create a table in order to be able to "return a table". Just get rid of the CREATE TABLE statement.
But your query isn't correct either, as you are selecting columns from the drug table, but you never include that in the FROM clause. You can also get rid of the distinct clause if you don't use a join, but an EXISTS condition:
CREATE OR REPLACE FUNCTION public.patients_drugs(p_patientid character varying)
RETURNS TABLE(drug_id integer, drug_name character varying)
LANGUAGE plpgsql
AS $BODY$
begin
return query
select d.*
from drugs d
where exists (select *
from prescription p
join visits v on v.prescription_id = p.id
where d.id = p.drug_id
and v.patientid = p_patientid);
end;
$BODY$;
Or better, use a simple SQL function:
CREATE OR REPLACE FUNCTION public.patients_drugs(p_patientid character varying)
RETURNS TABLE(drug_id integer, drug_name character varying)
LANGUAGE sql
AS
$BODY$
select d.*
from drugs d
where exists (select *
from prescription p
join visits v on v.prescription_id = p.id
where d.id = p.drug_id
and v.patientid = p_patientid);
$BODY$;

Returned type bigint does not match expected type integer in column 3

Below is my table structure for sold_quantity (Migration File)
alter table public.invoice_item add column sold_quantity int4 default 1;
Below is the function for execution
CREATE OR REPLACE FUNCTION sold_quantity()
RETURNS TABLE(
invoiceid BIGINT,
itemid BIGINT,
sum_sold_quantity INT)
AS $$
BEGIN
RETURN QUERY SELECT
invoice_id as invoiceid, item_id as itemid, sum(sold_quantity) as
sum_sold_quantity
FROM
invoice_item
WHERE
status='sold'
GROUP BY
invoice_id, item_id;
END; $$
What is the wrong in my code, Please help me solve this Error
Returned type bigint does not match expected type integer in column 3
sum() returns a bigint, not necessarily the type of the column that is being summed.
If you are 100% sure your sum never exceeds the range for an integer, you can fix this using a cast in your query: sum(sold_quantity)::int as sum_sold_quantity
But it would be better to adjust the signature of the function:
CREATE OR REPLACE FUNCTION sold_quantity()
RETURNS TABLE(
invoiceid BIGINT,
itemid BIGINT,
sum_sold_quantity BIGINT)

Select [COLUMN_NAME] AS, self referencing table

The following is my function get_reportees performed on the self referencing table emp_tabref1
CREATE OR REPLACE FUNCTION get_reportees4(IN id integer)
RETURNS TABLE(e_id integer, e_name character varying, e_manager integer, e_man_name character varying) AS
$$
BEGIN
RETURN QUERY
WITH RECURSIVE manger_hierarchy(e_id, e_name, m_id, m_name) AS
(
SELECT e.emp_id, e.emp_name, e.mgr_id, e.emp_name AS man_name
FROM emp_tabref1 e WHERE e.emp_id = id
UNION
SELECT rp.emp_id, rp.emp_name, rp.mgr_id, rp.emp_name AS man_name
FROM manger_hierarchy mh INNER JOIN emp_tabref1 rp ON mh.e_id = rp.mgr_id
)
SELECT * from manger_hierarchy;
END;
$$ LANGUAGE plpgsql VOLATILE
Table structure of emp_tabref1:
CREATE TABLE **emp_tabref1**
(
emp_id integer NOT NULL,
emp_name character varying(50) NOT NULL,
mgr_id integer,
CONSTRAINT emp_tabref_pkey PRIMARY KEY (emp_id),
CONSTRAINT emp_tabref_mgr_id_fkey FOREIGN KEY (mgr_id)
REFERENCES emp_tabref (emp_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
What I want returned is the hierarchy (both above and below) of the id that we are passing which will have the emp_name, emp_id, mgr_id and mgr_name.
But my function is returning like this:
select * from get_reportees4(9)
e_id e_name e_manager e_man_name
1 9 "Emp9" 10 "Emp9"
2 5 "Emp5" 9 "Emp5"
3 6 "Emp6" 9 "Emp6"
where my expected output is
e_id e_name e_manager e_man_name
1 9 "Emp9" 10 "Emp10"
2 5 "Emp5" 9 "Emp9"
3 6 "Emp6" 9 "Emp9"
The function should return the manager name and not the employee name. Please help!
Found a solution! By creating a new join between the temporary manger_hierarchy table and the emp_tabref1 table using mgr_id and emp_id
CREATE OR REPLACE FUNCTION get_reportees4(IN id integer)
RETURNS TABLE(e_id integer, e_name character varying, e_manager integer, e_man_name character varying) AS
$$
BEGIN
RETURN QUERY
WITH RECURSIVE manger_hierarchy(e_id, e_name, m_id, m_name) AS
(
SELECT e.emp_id, e.emp_name, e.mgr_id, e.emp_name AS man_name
FROM emp_tabref1 e WHERE e.emp_id = id
UNION
SELECT rp.emp_id, rp.emp_name, rp.mgr_id, rp.emp_name AS man_name
FROM manger_hierarchy mh INNER JOIN emp_tabref1 rp ON mh.e_id = rp.mgr_id
)
SELECT manger_hierarchy.e_id, manger_hierarchy.e_name, manger_hierarchy.m_id, emp_tabref1.emp_name
FROM manger_hierarchy LEFT JOIN emp_tabref1 ON manger_hierarchy.m_id = emp_tabref1.emp_id;
END;
$$ LANGUAGE plpgsql VOLATILE
SELECT manger_hierarchy.e_id, manger_hierarchy.e_name, manger_hierarchy.m_id, emp_tabref1.emp_name
FROM manger_hierarchy LEFT JOIN emp_tabref1 ON manger_hierarchy.m_id = emp_tabref1.emp_id;

Postgres Stored Procedure issue - how do I solve this?

I am creating a stored procedure that looks like this:
CREATE FUNCTION get_components(_given_user_id integer) RETURNS TABLE (id integer, name varchar, active boolean, parent integer, type smallint, description varchar, email varchar, user_id integer, component_id integer, flag smallint) AS $body$
BEGIN
RETURN QUERY SELECT s.* FROM st_components s LEFT JOIN (SELECT a.* FROM st_users_components_perms a WHERE a.user_id=_given_user_id) ON a.component_id=s.id ORDER BY name;
END;
$body$ LANGUAGE plpgsql;
The problem is, ON a.component_id=s.id ORDER BY name doesn't work because a.component_id is out of scope at this point. Is there a way to declare "a" as st_users_components_perms outside of the query? How is this solved? Thank you very much for any insight!
SELECT
s.*
FROM
st_components s
LEFT JOIN
(SELECT
a.*
FROM
st_users_components_perms a
WHERE
a.user_id = <CONSTANT>
) AS x
ON
x.component_id = s.id
ORDER BY
name;
Though the query seems pointless but let's assume it's a simplified version of your real query.