PostreSQL function extract value from jsonb - postgresql

I have created a PostgreSQL function and it is receiving jsonb as an argument and want to extract value from jsonb and save it to db.
function:
CREATE OR REPLACE FUNCTION public.insertorupdatevendorcontactnos(
contactnos jsonb)
RETURNS TABLE(vendorhistory_id bigint, vendor_id bigint)
LANGUAGE 'plpgsql'
COST 100
VOLATILE PARALLEL UNSAFE
AS $BODY$
BEGIN
insert into vendorcontactnos (vendorid, key, value, createdby, createdon)
select (contactnos->>'vendorid') :: bigint,
(x->>'key')::integer,
x->>'value',
(contactnos->>'createdby') :: integer,
NOW()
from jsonb_array_elements(contactnos ->'items') as x;
INSERT INTO public.vendorcontactnoshistory( vendorid, vendorhistoryid, key, value, createdby, createdon)
select (contactnos->>'vendorid') :: bigint,
(contactnos->>'vendorhistoryid') :: bigint,
(x->>'key')::integer,
x->>'value',
(contactnos->>'createdby') :: integer,
NOW()
from jsonb_array_elements(contactnos ->'items') as x;
RETURN QUERY (select (contactnos->>'vendorid') :: bigint,
(contactnos->>'vendorhistoryid') :: bigint);
END;
$BODY$;
calling as
select * from insertorupdatevendorcontactnos('[{"vendorid":100,
"vendorhistoryid":1,
"createdby":5,
"items":[
{"key":1, "value":"+19876543210"},
{"key":2, "value":"+16543219870"},
{"key":3, "value":"+13210654987"}
]}]');
I need vendorcontactnos table output as below:
id vendorid key value createdby createdon
1 100 1 +19876543210 5 current date time
2 100 2 +16543219870 5 current date time
3 100 3 +13210654987 5 current date time
It is not extracting vendorid and createdby json values and saving it to DB table.

So, I was passing an json array in the function calling and I was expecting json in the function. removed square brackets of an json array, passing only json worked for me.
select * from insertorupdatevendorcontactnos('{"vendorid":1,
"vendorhistoryid":1,
"createdby":1,
"items":[
{"key":1, "value":"+19876543210"},
{"key":2, "value":"+16543219870"},
{"key":3, "value":"+13210654987"}
]}');

Related

ERROR: set-returning functions are not allowed in WHERE array from

This is the function to get sub_arr from parameter and casting the sub_arr to UUID and find its id. And added timestamp where the student_subjects table were the id does not exist
CREATE OR REPLACE FUNCTION public.add_subjects(
std_ids uuid,
subjects_coll json)
RETURNS TABLE(ids integer, student_subjects_guid uuid, student_ids integer, subject_ids integer, edited timestamp without time zone, deletes timestamp without time zone)
LANGUAGE 'plpgsql'
COST 100
VOLATILE PARALLEL UNSAFE
ROWS 1000
AS $BODY$
declare
std_id integer;
begin
select id
into std_id
from students
where guid = std_ids;
if (std_id is not null)then
RETURN QUERY
update student_subjects
set deleted =now()
where student_id = std_id AND
subject_id not in (select id from subjects
where guid in (
cast(json_array_elements(subjects_coll ->
'sub_arr') as uuid)))
RETURNING id as ids, guid as student_subjects_guids,student_id as student_ids,subject_id as subject_ids,modified as edited, deleted as deletes;
end if;
end;
$BODY$;
ALTER FUNCTION public.add_subjects(uuid, json)
OWNER TO postgres;
Function argument passing subject id and subject guid
SELECT * FROM add_subjects('e1ea3e2a-9521-410e-9d76-0627e1ee9e2d','{"sub_arr":["f0952a26-49ad-467f-96ad-6566a19a8b46",
"eb9a7050-3ea7-428d-b5af-0b7622fae316"
]}');
Error
ERROR: set-returning functions are not allowed in WHERE
LINE 2: ...t in (select id from subjects where guid in (cast(json_array...
^
Change the type of the subjects_coll parameter to jsonb, then you can use the JSON containment operator #>. Also, it can help to turn the NOT IN into a NOT EXISTS:
UPDATE student_subjects
SET deleted = now()
WHERE student_id = std_id
AND NOT EXISTS (SELECT i FROM subjects
WHERE student_subjacts.subject_id = id
AND subjects_coll
#> jsonb_build_object(
'sub_arr',
jsonb_build_array(guid)
)
)
RETURNING id as ids,
guid as student_subjects_guids,
student_id as student_ids,
subject_id as subject_ids,
modified as edited,
deleted as deletes;

Function to insert data into different tables

I have three tables in PostgreSQL:
CREATE TABLE organization (id int, name text, parent_id int);
CREATE TABLE staff (id int, name text, family text, organization_id int);
CREATE TABLE clock(id int, staff_id int, Date date, Time time);
I need a function that gets all the fields of these tables as inputs (8 on total) and then inserts these inputs into appropriate fields of the tables
Here is my code:
CREATE FUNCTION insert_into_tables(org_name character varying(50), org_PID int, person_name character varying(50),_family character varying(50), org_id int, staff_id int,_date date, _time time without time zone)
RETURNS void AS $$
BEGIN
INSERT INTO "Org".organisation("Name", "PID")
VALUES ($1, $2);
INSERT INTO "Org".staff("Name", "Family", "Organization_id")
VALUES ($3, $4, $5);
INSERT INTO "Org"."Clock"("Staff_Id", "Date", "Time")
VALUES ($6, $7, $8);
END;
$$ LANGUAGE plpgsql;
select * from insert_into_tables('SASAD',9,'mamad','Imani',2,2,1397-10-22,'08:26:47')
But no data is inserted. I get the error:
ERROR: function insert_into_tables(unknown, integer, unknown, unknown, integer, integer, integer, unknown) does not exist
LINE 17: select * from insert_into_tables('SASAD',9,'mamad','Imani',2... ^
HINT: No function matches the given name and argument types. You might need to add explicit type casts.
Where did i go wrong?
That's because the 2nd last parameter is declared as date, not int. You forgot the single quotes:
select * from insert_into_tables('SASAD',9,'mamad','Imani',2,2,'1397-10-22','08:26:47');
Without single quotes, this is interpreted as subtraction between 3 integer constants, resulting in an integer: 1397-10-22 = 1365.
Also fix your identifiers: double-quoting preserves upper-case letters, so "Name" is distinct from name etc. See:
Are PostgreSQL column names case-sensitive?

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)

PostgreSQL: subquery must return one column

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;

How to create a pageable function in PostgreSQL

I have two tables: event and location
CREATE TABLE location
(
location_id bigint NOT NULL,
version bigint NOT NULL,
active boolean NOT NULL,
created timestamp without time zone NOT NULL,
latitude double precision NOT NULL,
longitude double precision NOT NULL,
updated timestamp without time zone,
CONSTRAINT location_pkey PRIMARY KEY (location_id)
)
CREATE TABLE event
(
event_id bigint NOT NULL,
version bigint NOT NULL,
active boolean NOT NULL,
created timestamp without time zone NOT NULL,
end_date date,
entry_fee numeric(19,2),
location_id bigint NOT NULL,
organizer_id bigint NOT NULL,
start_date date NOT NULL,
timetable_id bigint,
updated timestamp without time zone,
CONSTRAINT event_pkey PRIMARY KEY (event_id),
CONSTRAINT fk_organizer FOREIGN KEY (organizer_id)
REFERENCES "user" (user_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT fk_timetable FOREIGN KEY (timetable_id)
REFERENCES timetable (timetable_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION,
CONSTRAINT fk_location FOREIGN KEY (location_id)
REFERENCES location (location_id) MATCH SIMPLE
ON UPDATE NO ACTION ON DELETE NO ACTION
)
Other tables are of lesser to no importance so they will not be shown (unless explicitly asked).
And for those tables, using cube and earthdistance pgsql extensions I've created the following function for finding all event_ids within a certain radius of a certain point.
CREATE OR REPLACE FUNCTION eventidswithinradius(
lat double precision,
lng double precision,
radius double precision)
RETURNS SETOF bigint AS
$BODY$
BEGIN
RETURN QUERY SELECT event.event_id
FROM event
INNER JOIN location ON location.location_id = event.location_id
WHERE earth_box( ll_to_earth(lat, lng), radius) #> ll_to_earth(location.latitude, location.longitude);
END;
$BODY$
And this works as expected. Now I wish to make it pageable, and am stuck on how to get all the necessary values (the table with paged contents and total count).
So far I've created this:
CREATE OR REPLACE FUNCTION pagedeventidswithinradius(
IN lat double precision,
IN lng double precision,
IN radius double precision,
IN page_size integer,
IN page_offset integer)
RETURNS TABLE( total_size integer , event_id bigint ) AS
$BODY$
DECLARE total integer;
BEGIN
SELECT COUNT(location.*) INTO total FROM location WHERE earth_box( ll_to_earth(lat, lng), radius) #> ll_to_earth(location.latitude, location.longitude);
RETURN QUERY SELECT total, event.event_id as event_id
FROM event
INNER JOIN location ON location.location_id = event.location_id
WHERE earth_box( ll_to_earth(lat, lng), radius) #> ll_to_earth(location.latitude, location.longitude)
ORDER BY event_id
LIMIT page_size OFFSET page_offset;
END;
$BODY$
Here count is called only once and stored in a variable since I assumed that if I placed COUNT into the return query itself it would be called for each row.
This kind of works, but it is difficult to parse on the back-end since the result is in the form of (count, event_id), also count is needlessly repeated over all result rows. I was hoping I could simply add total as an OUT param and have the function return the table and fill the OUT variable with total count, however it seems this is not allowed. I can always have the count be a separate function but I was wondering if there is a better way to approach this issue?
No, there isn't really a better option. You want two different types of quantities so you need two queries. You can improve upon your function, however:
CREATE FUNCTION eventidswithinradius(lat float8, long float8, radius float8) RETURNS SETOF bigint AS $BODY$
SELECT event.event_id
FROM event
JOIN location l USING (location_id)
WHERE earth_box(ll_to_earth(lat, lng), radius) #> ll_to_earth(l.latitude, l.longitude);
$BODY$ LANGUAGE sql STRICT;
As a LANGUAGE sql function it is more efficient than as a PL/pgSQL function, plus you can do your paging on the outside:
SELECT *
FROM eventidswithinradius(121.056, 14.582, 3000)
LIMIT 15 OFFSET 1;
Internally the query planner will resolve the function call to its underlying query and apply the paging directly to that level.
Get the total with the obvious:
SELECT count(id)
FROM eventidswithinradius(121.056, 14.582, 3000);