postgreSQL - ERROR: cannot extract elements from an object - json

I am a newbie to PostgreSQL. I have the below function and it is giving me an error. basically, I am passing JSON as a parameter to the function, extracting values, and inserting it into 2 tables. it is one to many relationships. so based on that one invoice has multiple line items:
PostgreSQL function:
-- FUNCTION: public.insertorupdateinvoice(jsonb)
-- DROP FUNCTION IF EXISTS public.insertorupdateinvoice(jsonb);
CREATE OR REPLACE FUNCTION public.insertorupdateinvoice(
invoice jsonb)
RETURNS void
LANGUAGE 'plpgsql'
COST 100
VOLATILE PARALLEL UNSAFE
AS $BODY$
Declare _invoiceid bigint;
begin
insert into invoicemaster (expenseid, invoiceno, transactiondate, totalinvoiceamount, invoicedoc, createdby, createdon)
select (j.invoice->>'expenseid')::bigint,
j.invoice->>'invoiceno'::character,
(j.invoice->>'transactiondate')::date,
(j.invoice->>'totalinvoiceamount')::double precision,
j.invoice->>'invoicedoc'::character,
(j.invoice->>'createdby')::bigint,
NOW()
from jsonb_array_elements(invoice) as j(invoice)
returning invoiceid into _invoiceid;
insert into lineitemmaster (invoiceid, transactiondate, merchantname, amount, departmentid, policyid, itemdescription,
itemcategory, itemtype, status, isrejected, createdby, createdon)
select _invoiceid::bigint,
(x.invoice->>'transactiondate')::date,
x.invoice->>'merchantname',
(x.invoice->>'amount')::double precision,
(x.invoice->>'departmentid')::integer,
(x.invoice->>'policyid')::integer,
x.invoice->>'itemdescription',
(x.invoice->>'itemcategory')::integer,
(x.invoice->>'itemtype')::integer,
(x.invoice->>'status')::boolean,
(x.invoice->>'isrejected')::boolean,
(x.invoice->>'createdby')::bigint,
NOW()
from jsonb_array_elements(invoice ->'lineitems') as x;
end;
$BODY$;
ALTER FUNCTION public.insertorupdateinvoice(jsonb)
OWNER TO postgres;
calling function as :
select * from insertorupdateinvoice('{"expenseid":1,
"invoiceno":"04012022",
"transactiondate":"2022-01-04",
"totalinvoiceamount":1000.00,
"invoicedoc":"invoicedoc",
"createdby":1,
"lineitems":[
{"transactiondate":"2022-01-01", "merchantname":"Apple", "amount":"100.50", "departmentid":"1","policyid":"1", "itemdescription":"iphone 14 pro max", "itemcategory":"55", "itemtype":"499", "status":"true", "isrejected":"false", "createdby":"1"},
{"transactiondate":"2022-01-02", "merchantname":"Samsung", "amount":"1050.35", "departmentid":"2","policyid":"2", "itemdescription":"samsung galaxy tab", "itemcategory":"40", "itemtype":"50", "status":"true", "isrejected":"false", "createdby":"1"},
{"transactiondate":"2022-01-03", "merchantname":"Big bazar", "amount":"555.75", "departmentid":"3","policyid":"3", "itemdescription":"grocerry", "itemcategory":"5", "itemtype":"90", "status":"false", "isrejected":"false", "createdby":"1"}
]}');
getting error as below:
ERROR: cannot extract elements from an object
CONTEXT: SQL statement "insert into invoicemaster (expenseid, invoiceno, transactiondate, totalinvoiceamount, invoicedoc, createdby, createdon)
select (j.invoice->>'expenseid')::bigint,
j.invoice->>'invoiceno'::character,
(j.invoice->>'transactiondate')::date,
(j.invoice->>'totalinvoiceamount')::double precision,
j.invoice->>'invoicedoc'::character,
(j.invoice->>'createdby')::bigint,
NOW()
from jsonb_array_elements(invoice) as j(invoice)
returning invoiceid"
PL/pgSQL function insertorupdateinvoice(jsonb) line 5 at SQL statement
SQL state: 22023
Thanks

try this :
CREATE OR REPLACE FUNCTION public.insertorupdateinvoice(
invoice jsonb)
RETURNS void
LANGUAGE 'plpgsql'
COST 100
VOLATILE PARALLEL UNSAFE
AS $BODY$
Declare _invoiceid bigint;
begin
insert into invoicemaster (expenseid, invoiceno, transactiondate, totalinvoiceamount, invoicedoc, createdby, createdon)
values ( (invoice->>'expenseid') :: bigint,
invoice->>'invoiceno',
(invoice->>'transactiondate') :: date,
(invoice->>'totalinvoiceamount') :: double precision,
invoice->>'invoicedoc',
(invoice->>'createdby') :: bigint,
NOW()
)
returning invoiceid into _invoiceid;
insert into lineitemmaster (invoiceid, transactiondate, merchantname, amount, departmentid, policyid, itemdescription,
itemcategory, itemtype, status, isrejected, createdby, createdon)
select _invoiceid::bigint,
(x->>'transactiondate')::date,
x->>'merchantname',
(x->>'amount')::double precision,
(x->>'departmentid')::integer,
(x->>'policyid')::integer,
x->>'itemdescription',
(x->>'itemcategory')::integer,
(x->>'itemtype')::integer,
(x->>'status')::boolean,
(x->>'isrejected')::boolean,
(x->>'createdby')::bigint,
NOW()
from jsonb_array_elements(invoice ->'lineitems') as x;
end;
$BODY$;
see dbfiddle

Related

Week date range

There is a table objects, which stores data on real estate objects. Me need to use a query to calculate a new field that will display the date range from Monday to Sunday, which includes the date the object was created (for example, “2020-11-16 - 2020-11-22”)
create table objects(
object_id int NOT NULL PRIMARY KEY ,
city_id int not null ,
price int ,
area_total int ,
status varchar(50) ,
class varchar(50) ,
action varchar(50) ,
date_create timestamp,
FOREIGN KEY(city_id) references avg_price_square_city(city_id)
);
Data in the table:
INSERT INTO objects (object_id, city_id, price, area_total, status, class, action, date_create)
VALUES (1, 1, 4600000, 72, 'active', 'Secondary', 'Sale', '2022-05-12 21:49:34');
INSERT INTO objects (object_id, city_id, price, area_total, status, class, action, date_create)
VALUES (2, 2, 5400000, 84, 'active', 'Secondary', 'Sale', '2022-05-19 21:49:35');
The query should display two fields: the object number and a range that includes the date it was created. How can this be done ?
P.S
I wrote this query,but he swears at the "-" sign:
SET #WeekRangeStart ='2022/05/10';
SET #WeekRangeEnd = '2022/05/17';
select object_id,#range := #WeekRangeStart '-' #WeekRangeEnd
FROM objects where #range = #WeekRangeStart and date_create between #WeekRangeStart and #WeekRangeEnd
UNION
select object_id,#range from objects where #`range` = #WeekRangeEnd;
Error:[42000][1064] You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '#WeekRangeEnd FROM objects where #range = #WeekRangeStart and date_create betwee' at line 1
I want to receive in query:
object_id #range
1 2022/05/10 - 2022/05/17
The column #range must contain the date from the "date_create"
SET #WeekRangeStart = CAST('2022/05/10' as DATE);
SET #WeekRangeEnd = CAST('2022/05/17' as DATE);
SET #range = CONCAT(#WeekRangeStart,' - ',#WeekRangeEnd) ;
-- select #range;
select
object_id,
#range
FROM objects
where DATE(date_create) between #WeekRangeStart and #WeekRangeEnd
UNION
select object_id,#range from objects
;
Gives next result:
object_id
#range
1
2022-05-10 - 2022-05-17
2
2022-05-10 - 2022-05-17
This result is the output of the SQL part that is put after the UNION. Because date_create is not between your WeekRangeStart and WeekRangeEnd.
You should take some time, and read the UNION documentation.
The variable #range is calculated before the SQL statement, because the value is a constant.
see: DBFIDDLE
NOTE: You should try to use the same dateformat everywhere, and not mix date like '2022-05-19 21:49:35' and 2022/05/10. Use - OR use /, but do not mix them...
EDIT: After the calirification "Me need to use a query to calculate a new field that will display the date range from Monday to Sunday,...":
You probably wanted to do:
SET #WeekDate = CAST('2022/05/10' as DATETIME);
SELECT
ADDDATE(#WeekDate, -(DAYOFWEEK(#WeekDate)-1) +1) as Monday,
DATE_ADD(ADDDATE(#WeekDate, -(DAYOFWEEK(#WeekDate)-1) +9), INTERVAL -1 SECOND) as Sunday;
output:
Monday
Sunday
2022-05-09 00:00:00
2022-05-16 23:59:59

Teradata SQL Split Single String into Table Rows

I have one string element, for example : "(1111, Tem1), (0000, Tem2)" and hope to generate a data table such as
var1
var2
1111
Tem1
0000
Tem2
This is my code, I created the lag token and filter with odd rows element.
with var_ as (
select '(1111, Tem1), (0000, Tem2)' as pattern_
)
select tbb1.*, tbb2.result_string as result_string_previous
from(
select tb1.*,
min(token) over(partition by 1 order by token asc rows between 1 preceding and 1 preceding) as min_token
from
table (
strtok_split_to_table(1, var_.pattern_, '(), ')
returns (outkey INTEGER, token INTEGER, result_string varchar(20))
) as tb1) tbb1
inner join (select min_token, result_string from tbb1) tbb2
on tbb1.token = tbb2.min_token
where (token mod 2) = 0;
But it seems that i can't generate new variables in "from" step and applied it directly in "join" step.
so I wanna ask is still possible to get the result what i want in my procedure? or is there any suggestion?
Thanks for all your assistance.
I wouldn't split / recombine the groups. Split each group to a row, then split the values within the row, e.g.
with var_ as (
select '(1111, Tem1), (0000, Tem2)' as pattern_
),
split1 as (
select trim(leading '(' from result_string) as string_
from
table ( /* split at & remove right parenthesis */
regexp_split_to_table(1, var_.pattern_, '\)((, )|$)','c')
returns (outkey INTEGER, token_nbr INTEGER, result_string varchar(256))
) as tb1
)
select *
from table(
csvld(split1.string_, ',', '"')
returns (var1 VARCHAR(16), var2 VARCHAR(16))
) as tb2
;

reference a calculated field in mysql view

Need to refer a calculated field again in the statement. Have used # to create a variable and call it back. the statement gives a error saying "#1351 - View's SELECT contains a variable or parameter" have created three variables and referred them twice. what need to be corrected in the statement
CREATE OR REPLACE view finvoice AS SELECT
`Item_tax`,
`Date`,
`Invoice_No.`,
`Order_Id`,
`Buyer_name`,
`SKU_Code`,
`Product`,
#`price` := (round((((fkdaily.`Invoice_Amount`/ fkdaily.`Quantity`)-fkdaily.`Shipping_Charge_per_item`)/1.05),2)) as `price`,
`Quantity`,
#`vat_total` := (#price)*0.05)*Quantity),2)) As `vat_total`,
#`shipping` := (`Shipping_Charge_per_item`*`Quantity`) As `shipping`,
' ' as `roundoff`,
#`price`+#'vat_total`+#`shipping` as final
`Order_Status`,
`Invoice_No.` as `Invoice_No.2` ,
`Date` as date2,
'update_sku' as `TALLY_SKU`,
'Customer Sales (f)' As `Ledger`,
'Shipping Charges fk' As `Shipping Ledger`,
'FPC' As `Portal`
From fkdaily

passing comma deligma string to IN clause in sql server

SQL statement:
(select top 1 [egrp_name] from [Enotify Group] where [egrp_id] in (a.grp_id) )
e value of a.grp_id is '0,1145' and i am getting error
Conversion failed when converting the varchar value '0,1145' to data type int.
Can anybody tell me how can i change '0,1145' to 0,1145 in above case, so my query does work and also if their is any other way to do this
You can use a split-string function to change your comma delimited string into a table.
select top(1) [egrp_name]
from [Enotify Group]
where [egrp_id] in (
select Value
from dbo.SplitInts(a.grp_id)
);
One version of a split-string function that you can use if you like:
create function dbo.SplitInts(#Values nvarchar(max)) returns table with schemabinding
as
return
(
select T2.X.value(N'.', N'int') as Value
from (select cast(N'<?X '+replace(#Values, N',', N'?><?X ') + N'?>' as xml).query(N'.')) as T1(X)
cross apply T1.X.nodes(N'/processing-instruction("X")') as T2(X)
);
Or you can use like.
select top(1) [egrp_name]
from [Enotify Group]
where ','+a.grp_id +',' like '%,'+cast(egrp_id as varchar(11))+',%' ;

Error in plpgsql function: array value must start with "{" or dimension information

I'm trying to format the results of this query:
CREATE OR REPLACE FUNCTION "alarmEventList"(sampleid integer
, starttime timestamp without time zone
, stoptime timestamp without time zone)
RETURNS text[] AS
$BODY$DECLARE
result text[];
BEGIN
select into result array_agg(res::text)
from (
select to_char("Timestamp", 'YYYY-MM-DD HH24:MI:SS')
,"AlertLevel"
,"Timestamp" - lag("Timestamp") over (order by "Timestamp")
from "Judgements"
WHERE "SampleID" = sampleid
and "Timestamp" >= starttime
and "Timestamp" <= stoptime
) res
where "AlertLevel" > 0;
select into result array_to_string(result,',');
return result;
END
$BODY$
LANGUAGE plpgsql VOLATILE
Right now without array_to_string() I get something like this:
{"(\"2013-10-16 15:10:40\",1,00:00:00)","(\"2013-10-16 15:11:52\",1,00:00:48)"}
and I want something like this:
2013-10-16 15:10:40,1,00:00:00 | 2013-10-16 15:11:52,1,00:00:48 |
But when I run the query I get error:
array value must start with "{" or dimension information
You do not actually want an array type, but a string representation.
Can be achieved like this:
CREATE OR REPLACE FUNCTION "alarmEventList"(sampleid integer
, starttime timestamp
, stoptime timestamp
, OUT result text) AS
$func$
BEGIN
SELECT INTO result string_agg(concat_ws(','
,to_char("Timestamp", 'YYYY-MM-DD HH24:MI:SS')
,"AlertLevel"
,"Timestamp" - ts_lag)
, ' | ')
FROM (
SELECT "Timestamp"
,"AlertLevel"
,lag("Timestamp") OVER (ORDER BY "Timestamp") AS ts_lag
FROM "Judgements"
WHERE "SampleID" = sampleid
AND "Timestamp" >= starttime
AND "Timestamp" <= stoptime
) res
WHERE "AlertLevel" > 0;
END
$func$ LANGUAGE plpgsql
The manual on string_agg() and concat_ws().