postgresql function - how to push new json object to json array? - sql

I need to push new json object to existing json.
response json ='{"success":[{"aaa":"bbb"}]}'::json;
newitem json ='{"ccc":"ddd"}'::json;
Final response json should like below
{"success":[{"aaa":"bbb"},{"ccc":"ddd"}]}
Here is the complete code:
DROP FUNCTION orgname.testfunction();
CREATE OR REPLACE FUNCTION orgname.testfunction()
RETURNS json
LANGUAGE 'plpgsql'
VOLATILE
AS $RESPONSE$
DECLARE
response json ='{"success":[]}'::json;
newitem json ='{"ccc":"ddd"}'::json;
BEGIN
with c(response,newitem) as (values('{"success":[{"aaa":"bbb"}]}'::json,'{"ccc":"ddd"}'::json))
, m as (select json_array_elements(response->'success') from c union all select newitem from c)
select concat('{"success":',json_agg(json_array_elements),'}')::json from m;
return '{"expecting":"result"}'::json;
END;
$RESPONSE$

with 9.4 solution will not look neat. smth like:
t=# with c(response,newitem) as (values('{"success":[{"aaa":"bbb"}]}'::json,'{"ccc":"ddd"}'::json))
, m as (select json_array_elements(response->'success') from c union all select newitem from c)
select concat('{"success":',json_agg(json_array_elements),'}')::json from m;
concat
--------------------------------------------
{"success":[{"aaa":"bbb"}, {"ccc":"ddd"}]}
(1 row)
update
for your function:
t=# CREATE OR REPLACE FUNCTION orgname.testfunction()
RETURNS json
LANGUAGE 'plpgsql'
VOLATILE
AS $RESPONSE$
DECLARE
response json ='{"success":[{"aaa":"bbb"}]}'::json;
newitem json ='{"ccc":"ddd"}'::json;
BEGIN
return (with c(_response,_newitem) as (values(response,newitem))
, m as (select json_array_elements(_response->'success') from c union all select _newitem from c)
select concat('{"success":',json_agg(json_array_elements),'}')::json from m);
END;
$RESPONSE$
;
CREATE FUNCTION
t=# select * from orgname.testfunction();
testfunction
--------------------------------------------
{"success":[{"aaa":"bbb"}, {"ccc":"ddd"}]}
(1 row)

Related

How to avoid floating point overflows in PostgreSQL?

In PostgreSQL, when running a computation like this:
select 1E300::float * 1E300::float
I'd like to receive an 'Infinity'::float value, similar to when I do that in Java or CockroachDB. Instead, I'm getting:
SQL Error [22003]: ERROR: value out of range: overflow
SQLFiddle here. How can I do this?
if you can encapsulate your select statement in a plpgsql function then you can manage the error like this :
CREATE OR REPLACE FUNCTION multiply_by_float(a float, b float)
RETURNS float LANGUAGE plpgsql IMMUTABLE AS
$$
DECLARE
res float ;
BEGIN
SELECT a * b INTO res ;
RETURN res ;
EXCEPTION WHEN OTHERS THEN
CASE
WHEN (a > 0 and b < 0) or (a < 0 and b > 0)
THEN RETURN '-Infinity';
ELSE RETURN 'Infinity' ;
END CASE ;
END ;
$$ ;
Then SELECT multiply_by_float(1E300::float, 1E300::float) returns Infinity.
you can use numeric and decimal types PG doc
select 1E300::decimal * 1E300::decimal;
result
select 1E300::decimal * 1E300::decimal;
?column?
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Postgresql column reference is ambiguous

I want to call my function but I get this error:
ERROR: column reference "list" is ambiguous LINE 3: SET
list = ARRAY_APPEND(list, input_list2),
the error is on the second list inside array_append function.
My function:
CREATE OR REPLACE FUNCTION update_order(input_id uuid,input_sku text,input_store_id uuid,input_order_date bigint,input_asin text,input_amount int,input_list text[],input_price real,input_list2 text) RETURNS void LANGUAGE plpgsql AS
$body$
#variable_conflict use_column
BEGIN
INSERT INTO orders_summary(id,sku,store_id,order_date,asin,amount,list,price)
VALUES(input_id,input_sku,input_store_id,to_timestamp(input_order_date / 1000.0),input_asin,input_amount,input_list,input_price) ON CONFLICT(sku,order_date) DO UPDATE
SET list = ARRAY_APPEND(list, input_list2),
amount = amount + input_amount,
price = input_price
WHERE NOT list #> input_list;
END
$body$;
You have to use the alias name in the insert query because list has two references, one reference in EXCLUDED.list and another reference to the column for an update statement.
Please check the below query (I append the alias with name os in query):
CREATE OR REPLACE FUNCTION update_order(input_id uuid,input_sku text,input_store_id uuid,input_order_date bigint,input_asin text,input_amount int,input_list text[],input_price real,input_list2 text) RETURNS void LANGUAGE plpgsql AS
$body$
#variable_conflict use_column
BEGIN
INSERT INTO orders_summary as os (id,sku,store_id,order_date,asin,amount,list,price)
VALUES(input_id,input_sku,input_store_id,to_timestamp(input_order_date / 1000.0),input_asin,input_amount,input_list,input_price) ON CONFLICT(sku,order_date) DO UPDATE
SET list = ARRAY_APPEND(os.list, input_list2),
amount = os.amount + input_amount,
price = input_price
WHERE NOT os.list #> input_list;
END
$body$;
Or you can use table name:
CREATE OR REPLACE FUNCTION update_order(input_id uuid,input_sku text,input_store_id uuid,input_order_date bigint,input_asin text,input_amount int,input_list text[],input_price real,input_list2 text) RETURNS void LANGUAGE plpgsql AS
$body$
#variable_conflict use_column
BEGIN
INSERT INTO orders_summary (id,sku,store_id,order_date,asin,amount,list,price)
VALUES(input_id,input_sku,input_store_id,to_timestamp(input_order_date / 1000.0),input_asin,input_amount,input_list,input_price) ON CONFLICT(sku,order_date) DO UPDATE
SET list = ARRAY_APPEND(orders_summary.list, input_list2),
amount = orders_summary.amount + input_amount,
price = input_price
WHERE NOT orders_summary.list #> input_list;
END
$body$;

PostgreSQL: How to pass and array to a function and using it in a query with the IN operator

I have a problem, I want to pass an array to a postgres function and use that array so returns values in a SELECT IN clause.
But It shows me this error:
An error occurred executing the SQL command :
SELECT
*
FROM
get_invtransferences_porders_fporders (300001300 , array [ 300093753 , 300094126 , 300093349 , 300093838 , 300094128 ] ...
ERROR: operator does not exist : integer = integer [ ]
Hint : No operator matches the name and type of arguments. You may need to add explicit type conversions .
Where : PL / pgSQL get_invtransferences_porders_fporders (numeric , integer []) function on line 8 FOR loop around rows of a SELECT
This is my function:
CREATE OR REPLACE FUNCTION public.get_invtransferences_porders_fporders(p_product_id numeric, P_INVTRANSFERENCES_IDS integer[])
RETURNS SETOF record
LANGUAGE plpgsql
AS
$body$
DECLARE
PORDER_PRODUCT RECORD;
COMPONENT RECORD;
COMPONENT2 RECORD;
COMPONENT3 RECORD;
BEGIN
FOR PORDER_PRODUCT IN (
SELECT
'porder' AS "operation_type"
,porders.id AS "porder_id"
,porders.user_id AS "porder_user_id"
,(SELECT name FROM users WHERE users.id = porders.user_id) AS "porder_user_name"
,porders.delivery_datetime AS "porder_delivery_datetime"
,porders_products.requested AS "product_requested"
,porders_products.produced AS "product_produced"
,products.code AS "product_code"
,products.NAME AS "product_name"
,(
SELECT products.name
FROM products
WHERE id = product_components.component_product_id
) AS "component_product_name"
,product_components.quantity AS "component_quantity"
,(
SELECT products.um_id
FROM products
WHERE id = product_components.component_product_id
) AS "component_um_id"
,(product_components.quantity / products.production_base) * porders_products.requested AS "total"
FROM porders
,porders_products
,products
,product_components
WHERE porders.id = porders_products.porder_id
AND porders_products.product_id = products.id
AND porders_products.product_id = product_components.product_id
AND porders.id IN (
SELECT rawm_audit_porders.porder_id
FROM rawm_audit_invtransferences
,rawm_audits
,rawm_audit_porders
WHERE rawm_audit_invtransferences.rawm_audits_id = rawm_audits.id
AND rawm_audit_porders.rawm_audits_id = rawm_audits.id
AND rawm_audit_invtransferences.invtransference_id IN
(
SELECT
invtransferences.id
FROM invtransferences
,invtransferences_products
,products
WHERE invtransferences.id = invtransferences_products.invtransference_id
AND products.id = invtransferences_products.product_id
AND invtransferences.id IN (P_INVTRANSFERENCES_IDS)
)
)
AND product_components.component_product_id = p_product_id
) LOOP
IF(PORDER_PRODUCT.porder_id IS NOT NULL)THEN
RETURN NEXT PORDER_PRODUCT;
END IF;
END LOOP;
RETURN;
END;
$body$
VOLATILE
COST 100
ROWS 1000
I think the error it here `invtransferences.id IN (P_INVTRANSFERENCES_IDS)
This is the select that calls the function:
SELECT
*
FROM
get_invtransferences_porders_fporders(300001300 , array[300093753, 300094126, 300093349, 300093838, 300094128] )
AS
(
"operation_type" varchar,
"porder_id" numeric,
"porder_user_id" numeric,
"porder_user_name" varchar,
"porder_delivery_datetime" date,
"product_requested" numeric,
"product_produced" numeric,
"product_code" varchar,
"product_name" varchar,
"component_product_name" varchar,
"component_quantity" numeric,
"component_um_id" varchar,
"total" numeric
)
ORDER BY
"porder_id";
EDIT: I remove the VARIADIC words that were in the function and in the select that calls the function
Can you hep me Please.
You don't need to declare your function as VARIADIC to pass array to it.
Try this
CREATE OR REPLACE FUNCTION xxx(
p_product_id integer,
P_INVTRANSFERENCES_IDS integer[])
RETURNS SETOF record
LANGUAGE sql
AS
$body$
select p_product_id = ANY(P_INVTRANSFERENCES_IDS)
$body$;
Note there is no VARIADIC before P_INVTRANSFERENCES_IDS.
You also need to use ANY instead of IN to check membership in array.
SqlFiddle

How to handle cases where array is empty in postgresql?

I have a function written in plpythonu.
CREATE OR REPLACE FUNCTION temp(t_x integer[])
RETURNS void AS
$BODY$
.
.
.
x=plpy.execute("""select array_to_string(select id from A where id=any(array%s) ), ',')"""%t_x)
On some cases when t_x is empty i get an error:
ERROR: cannot determine type of empty array
How do I fix it?
if-else statement
Pseudocode:
if t_x is empty
x=null
else
x=plpy.execute....
cast
x=plpy.execute("""select array_to_string(select id from A where id=any(array%s::integer[]) ), ',')"""%t_x)
Why cast help?
postgres=# select pg_typeof(array[1,2]);
pg_typeof
-----------
integer[]
array[1,2] has type integer[]
postgres=# select array[];
ERROR: cannot determine type of empty array
LINE 1: select array[];
array[] has no type.
postgres=# select pg_typeof(array[]::integer[]);
pg_typeof
-----------
integer[]
array[]::integer[] has type integer[]
Maybe this can help someone in future.
Table function may receive NULL or Empty Array in that case (equivalent to ALL):
CREATE OR REPLACE FUNCTION temp(t_x integer[])
RETURNS void AS
$BODY$
.
.
.
x=plpy.execute("""select array_to_string(select id from A where id=any(array%s) or cardinality(coalesce(array%s,array[]::integer[])) = 0), ',')"""%t_x)

PostgreSQL: get count of occurrences of specified element in array

I need to calculate the count of occurrences of specified element in array, something like:
elem_occurrences_count(ARRAY[a,b,c,a,a], a) = 3
elem_occurrences_count(ARRAY[a,b,c], d) = 0
Is there any function in PostgreSQL that can be used to solve the problem? Any help is appreciated.
You will need to unnest the array and then count the occurrences.
with elements (element) as (
select unnest(ARRAY['a','b','c','a','a'])
)
select count(*)
from elements
where element = 'a';
This can easily be embedded into a function:
create or replace function count_elements(elements text[], to_find text)
returns bigint
as
$body$
select count(*)
from unnest(elements) element
where element = to_find;
$body$
language sql;
Update
Since Postgres 9.5 this can also be done using array_positions() which returns an array of positions where an element was found. The length of that array is the number of occurrences:
select cardinality(array_positions(ARRAY['a','b','c','a','a'], 'a'));
9.5+
There is an easier method now
SELECT
sArray,
c,
coalesce(array_length( array_positions(sArray, c), 1 ),0) AS count
FROM ( VALUES
(ARRAY['a','b','c','a','a'], 'a'),
(ARRAY['a','b','c'], 'd')
) AS t(sArray,c);
sarray | c | count
-------------+---+-------
{a,b,c,a,a} | a | 3
{a,b,c} | d | 0
(2 rows)
The occurrence of all elements in an array can be found with this query:
SELECT count(id), UNNEST(array) as element
FROM myTable
GROUP BY element;
To count the occurrence of a specific element, for example 'c', add a WHERE clause:
SELECT count(id), UNNEST(array) as element
FROM myTable
WHERE EXISTS (SELECT * FROM UNNEST(array) AS x WHERE x='c')
GROUP BY element;
You can use the FILTER clause to count the occurrences.
Let's say we have a questions table with tags (array type) and you want to count the questions with postgresql tag:
SELECT COUNT(*) FILTER (WHERE '{postgresq}' <# (tags)) as tagCount
FROM posts;
More generic function is here;
CREATE FUNCTION count_array_elements (
i_elements pg_catalog.anyarray,
i_element_to_find pg_catalog.anyelement,
out count bigint
)
RETURNS bigint AS
$body$
BEGIN
SELECT count(*) INTO count
FROM unnest(i_elements) u
WHERE u = i_element_to_find;
END;
$body$
LANGUAGE 'plpgsql'
IMMUTABLE
RETURNS NULL ON NULL INPUT;
With this way, we can query like this one below;
SELECT * FROM count_array_elements(array [ TRUE, TRUE, FALSE, FALSE, FALSE ], TRUE);
Thanks to all contributors here, I learnt a few things.
I am building on work of others in this thread and others in stackoverflow.
I tried to create a function that will count for all the unique elements in the array.
I was targeting returning a json but it seems you can only return as SETOF.
result of count_element_3
CREATE OR REPLACE FUNCTION count_element_3(str_array text[])
RETURNS setof text
AS
$$
DECLARE
unique_element_array text[];
cardinality_array int[];
retArray text[];
BEGIN
-- Find unique items first
unique_element_array := array(select distinct unnest(str_array));
FOR I IN array_lower(unique_element_array, 1)..array_upper(unique_element_array, 1)
LOOP
cardinality_array[I] := (select cardinality(array_positions(str_array, unique_element_array[I])));
retArray[I] := concat(unique_element_array[I],':',cardinality_array[I]);
END LOOP;
RETURN QUERY SELECT(retArray::text);
END;
$$
LANGUAGE plpgsql
VOLATILE
RETURNS NULL ON NULL INPUT;
with t1 as (SELECT
sArray,
c,
coalesce(array_length( array_positions(sArray, c), 1 ),0) AS count
FROM ( VALUES
(ARRAY['a','b','c','a','a'], 'a'),
(ARRAY['a','b','c'], 'd')
) AS t(sArray,c)
)
select sarray, count_element_3(sarray) from t1
sarray count_element_3
text[] text
-------------------------------------
"{a,b,c,a,a}" "{c:1,a:3,b:1}"
"{a,b,c}" "{c:1,a:1,b:1}"