Oracle - JSON_OBJECT - ORA-40478: output value too large (maximum: 4000) - sql

I am using Oracle 18c database.
For one of my Query, I am trying generate JSon from 3 level of tables.
pages_tbl
page_regions_tbl
region_items_tbl
For that I have prepared below query. But it is giving me error ORA-40478: output value too large (maximum: 4000)
SELECT
JSON_ARRAYAGG(
JSON_OBJECT(
'page' VALUE p.name,
'regions' VALUE(
SELECT
JSON_ARRAYAGG(
JSON_OBJECT(
'region' VALUE r.name,
'items' VALUE(
SELECT
JSON_ARRAYAGG(
JSON_OBJECT(
'item_name' VALUE i.item_name, 'item_value' VALUE i.item_value
) RETURNING CLOB
)
FROM region_items_tbl i
WHERE i.region_id = r.region_id
AND i.enabled = 1
)
) RETURNING CLOB
)
FROM page_regions_tbl r
WHERE r.page_id = p.page_id
AND r.enabled = 1
)
) RETURNING CLOB
)
FROM pages_tbl p
WHERE p.category_id = 10150
AND p.enabled = 1
I have already written RETURNING CLOB so I was expecting smooth result but getting error. Can any one point me out what I am doing wrong or how I can fix this query

I had the same issue and the same result (still failing, even with RETURNING CLOB added to every JSON_OBJECT), however I've found that you also need to add RETURNING CLOB to your JSON_ARRAYAGG functions (but not your JSON_ARRAY functions, if you had any). This fixed the issue for me and it displays the returned data as "(CLOB)", but expands to the real data when you click into it.

add RETURNING CLOB before all JSON_OBJECT's closing brackets
https://docs.oracle.com/en/database/oracle/oracle-database/19/sqlrf/JSON_OBJECT.html
JSON_returning_clause
Use this clause to specify the type of return value. One of :
VARCHAR2 specifying the size as a number of bytes or characters. The default is bytes. If you omit this clause, or specify the clause without specifying the size value, then JSON_ARRAY returns a character string of type VARCHAR2(4000). Refer to VARCHAR2 Data Type for more information. Note that when specifying the VARCHAR2 data type elsewhere in SQL, you are required to specify a size. However, in the JSON_returning_clause you can omit the size.
CLOB to return a character large object containing single-byte or multi-byte characters.

Related

How to transform pl sql Json value into a string

Have a project for work & my SQL skills are improving, but I still struggle with basic stuff every now and then. I need to get 'fundingCode' to show up as a string rather than a number as it is now.
(i.e. currently the outcome is '"fundingCode": 100001' & I need it to show up as '"fundingCode": "100001"
cursor json_index_data (c_org_code VARCHAR2) is
select json_object(
'_dtoName' VALUE 'AeSFndOrganization',
'companyId' VALUE oa.location_desc,
'deptId' VALUE OA.DEPT,
'ocCode' VALUE OA.ORGANIZATION_LEVEL_6,
nvl('seq', 1) VALUE imi.seq,
'fundingCode' VALUE (I.ACCOUNT_INDEX)
FORMAT JSON)
as json_row_value
The way you would typically do a select statement resulting in JSON would be like this:
select json_object(
key '_dtoName' is 'AeSFndOrganization',
key 'companyId' is oa.location_desc,
key 'deptId' is OA.DEPT,
key 'ocCode' is OA.ORGANIZATION_LEVEL_6,
key 'seq' is imi.seq,
key 'fundingCode' is to_char(I.ACCOUNT_INDEX)
) as YOUR_JSON_ALIAS
from YOUR_TABLENAME;
So you define your key on the left and your value (with is) on the right. I don't think that would act any differently in a cursor if you just put cursor json_index_data is in front of it.
If you need to convert a value, use the typical conversion functions like to_char(the_value) or to_number(the_string), etc. That's what I did in the sample query above for:
...
key 'fundingCode' is to_char(I.ACCOUNT_INDEX)
...
So a coworker managed to give me a pretty good explanation. I had previously tried to use concatenation, but I was using bad syntax. Here is how it was finally accomplished.
cursor json_index_data (c_org_code VARCHAR2) is
select json_object(
'multitenantId' VALUE '1',
'_dtoName' VALUE 'AeSFndOrganization',
'companyId' VALUE oa.campus_desc,
'deptId' VALUE OA.DEPT,
'ocCode' VALUE OA.ORGANIZATION_LEVEL_6,
nvl('seq', 1) VALUE imi.seq,
'fundingCode' VALUE '"' || I.ACCOUNT_INDEX || '"'
FORMAT JSON)
as json_row_value

How to cast postgres JSON column to int without key being present in JSON (simple JSON values)?

I am working on data in postgresql as in the following mytable with the fields id (type int) and val (type json):
id
val
1
"null"
2
"0"
3
"2"
The values in the json column val are simple JSON values, i.e. just strings with surrounding quotes and have no key.
I have looked at the SO post How to convert postgres json to integer and attempted something like the solution presented there
SELECT (mytable.val->>'key')::int FROM mytable;
but in my case, I do not have a key to address the field and leaving it empty does not work:
SELECT (mytable.val->>'')::int as val_int FROM mytable;
This returns NULL for all rows.
The best I have come up with is the following (casting to varchar first, trimming the quotes, filtering out the string "null" and then casting to int):
SELECT id, nullif(trim('"' from mytable.val::varchar), 'null')::int as val_int FROM mytable;
which works, but surely cannot be the best way to do it, right?
Here is a db<>fiddle with the example table and the statements above.
Found the way to do it:
You can access the content via the keypath (see e.g. this PostgreSQL JSON cheatsheet):
Using the # operator, you can access the json fields through the keypath. Specifying an empty keypath like this {} allows you to get your content without a key.
Using double angle brackets >> in the accessor will return the content without the quotes, so there is no need for the trim() function.
Overall, the statement
select id
, nullif(val#>>'{}', 'null')::int as val_int
from mytable
;
will return the contents of the former json column as int, respectvely NULL (in postgresql >= 9.4):
id
val_int
1
NULL
2
0
3
2
See updated db<>fiddle here.
--
Note: As pointed out by #Mike in his comment above, if the column format is jsonb, you can also use val->>0 to dereference scalars. However, if the format is json, the ->> operator will yield null as result. See this db<>fiddle.

delimit the output of xml extract function in oracle [duplicate]

I have a CLOB column that contains XML type data. For example XML data is:
<A><B>123</b><C>456</C><B>789</b></A>
I have tried the concat function:
concat(xmltype (a.xml).EXTRACT ('//B/text()').getStringVal (),';'))
or
xmltype (a.xml).EXTRACT (concat('//B/text()',';').getStringVal ()))
But they are giving ";" at end only not after each <B> tag.
I am currently using
xmltype (a.xml).EXTRACT ('//B/text()').getStringVal ()
I want to concatenate all <B> with ; and expected result should be 123;789
Please suggest me how can I concatenate my data.
The concat() SQL function concatenates two values, so it's just appending the semicolon to each extracted value independently. But you're really trying to do string aggregation of the results (which could, presumably, really be more than two extracted values).
You can use XMLQuery instead of extract, and use an XPath string-join() function to do the concatentation:
XMLQuery('string-join(/A/B, ";")' passing xmltype(a.xml) returning content)
Demo with fixed XMl end-node tags:
-- CTE for sample data
with a (xml) as (
select '<A><B>123</B><C>456</C><B>789</B></A>' from dual
)
-- actual query
select XMLQuery('string-join(/A/B, ";")' passing xmltype(a.xml) returning content) as result
from a;
RESULT
------------------------------
123;789
You could also extract all of the individual <B> values using XMLTable, and then use SQL-level aggregation:
-- CTE for sample data
with a (xml) as (
select '<A><B>123</B><C>456</C><B>789</B></A>' from dual
)
-- actual query
select listagg(x.b, ';') within group (order by null) as result
from a
cross join XMLTable('/A/B' passing xmltype(a.xml) columns b number path '.') x;
RESULT
------------------------------
123;789
which gives you more flexibility and would allow grouping by other node values more easily, but that doesn't seem to be needed here based on your example value.

Split Clob containing XML

I have a table containing, in a clob column, a value like this: <root><node><a>text1a</a><b>text1b</b></node><node><a>text2a</a><b>text2b</b></node></root>
Using PL/SQL I need to query it and obtain this output in two rows:
<node><a>text1a</a><b>text1b</b></node>
<node><a>text2a</a><b>text2b</b></node>
It could be more the 4000 chars each one.
Tag must be included in the output.
Convert clob to xmltype and use xmltable to parse it:
with s as (select '<root><node><a>text1a</a><b>text1b</b></node><node><a>text2a</a><b>text2b</b></node></root>' c from dual)
select x.node node_xml, x.node.getclobval() node_clob
from s,
xmltable(
'/root/node'
passing xmltype(s.c)
columns
node xmltype path '.'
) x;
NODE_XML NODE_CLOB
------------------------------------------ ------------------------------------------
<node><a>text1a</a><b>text1b</b></node> <node><a>text1a</a><b>text1b</b></node>
<node><a>text2a</a><b>text2b</b></node> <node><a>text2a</a><b>text2b</b></node>
Original data is not in varchar; it is in some LOB. If you are ok with returning 2 lobs, look up PLSQL table functions (to put a function in from clause) and utilizing dbms_lob package return 2 rows where each row is a LOB as well.
If you want to return it as varchar data, then you have the 4000 limit. All you can do is return in multiple rows of 4000 and combine them all in your client software.
You can see this link where there is a solution to split blob into 4000 byte strings. https://medium.com/#thesaadahmad/a-blobs-journey-from-the-database-to-the-browser-98884261e137

Oracle VIEW - More than 4000 bytes in Column

Iam using this part of an SQL Satement to fetch Information from an N:N Relationship.
The Goal is to have an view with an column like: "STRING1,STRING2,STRING3". This works fine but i have sometimes more than 4000 Bytes in the Column.
(SELECT
(RTRIM(XMLAGG(xmlelement(X, TABLE1.STRING||',') order by TABLE1.STRING).extract('//text()'),','))
FROM
STRING_HAS_TABLE1
JOIN TABLE1 STRING_HAS_TABLE1.STRING_ID = TABLE1.ID
WHERE
STRING_HAS_TABLE1.USER_ID = X.ID) AS STRINGS,
Oracle throws "Buffer overflow". I think the problem is the columntype inside the view: VARCHAR2(4000).
ERROR: ORA 19011 - Character string buffer to small
Any ideas to handle this without changing the whole application logic?
This is a problem converting implicitly between data types. You can get around it by treating it as a CLOB before trimming, by adding a getClobVal() call:
SELECT RTRIM(XMLAGG(xmlelement(X, TABLE1.STRING||',')
order by TABLE1.STRING).extract('//text()').getClobVal(),',')
FROM ...
The RTRIM documentation shows the types it accepts, and since XMLTYPE isn't listed that means it has to be doing an implicit conversion, apparently to VARCHAR2. (The same applies to the other TRIM functions).
But it does accept CLOB, so doing an explicit conversion to CLOB means RTRIM doesn't do an implicit conversion to a type that's too small.