Split Clob containing XML - sql

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

Related

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.

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

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.

SQL select specific (4th) part of column BLOB data, separated by specific pattern

There is a BLOB column that contains data like:
{{Property1 {property1_string}} {Property2 {property2_string}} {Property3 {property3_string}} {Property4 {property4_string}} {Property5 {property5_string}}}
I select the above column to display the BLOB data, as follows:
utl_raw.cast_to_varchar2(dbms_lob.substr(blobColumn))
I need to display only the data of 4th Property of BLOB column, so the following:
{Property4 {property4_string}}
So, I need help to create the necessary select for this purpose.
Thank you.
this will work:
select substr(cast(blobfieldname as
varchar2(2000)),instr(cast(blobfieldname as
varchar2(2000)),'{',1,8)),instr(cast(blobfieldname as
varchar2(2000)),'}',1,8))-
instr(cast(blobfieldname as varchar2(2000)),'{',1,8))) from tablename;
You may use REGEXP_SUBSTR.
select REGEXP_SUBSTR(s,'[^{} ]+', 1, 2 * :n) FROM t;
Where n is the nth property string you want to extract from your data.
n = 1 gives property1_string
n = 2 gives property2_string
..
and so on
Note that s should be the output of utl_raw.cast_to_varchar2
Demo

Get max on comma separated values in column

How to get max on comma separated values in Original_Ids column and get max value in one column and remaining ids in different column.
|Original_Ids | Max_Id| Remaining_Ids |
|123,534,243,345| 534 | 123,234,345 |
Upadte -
If I already have Max_id and just need below equation?
Remaining_Ids = Original_Ids - Max_id
Thanks
Thanks to the excellent possibilities of array manipulation in Postgres, this could be done relatively easy by converting the string to an array and from there to a set.
Then regular queries on that set are possible. With max() the maximum can be selected and with EXCEPT ALL the maximum can be removed from the set.
A set can then be converted to an array and with array_to_string() and the array can be converted to a delimited string again.
SELECT ids original_ids,
(SELECT max(un.id::integer)
FROM unnest(string_to_array(ids,
',')) un(id)) max_id,
array_to_string(ARRAY((SELECT un.id::integer
FROM unnest(string_to_array(ids,
',')) un(id)
EXCEPT ALL
SELECT max(un.id::integer)
FROM unnest(string_to_array(ids,
',')) un(id))),
',') remaining_ids
FROM elbat;
Another option would have been regexp_split_to_table() which directly produces a set (or regexp_split_to_array() but than we'd had the possible regular expression overhead and still had to convert the array to a set).
But nevertheless you just should (almost) never use delimited lists (nor arrays). Use a table, that's (almost) always the best option.
SQL Fiddle
You can use a window function (https://www.postgresql.org/docs/current/static/tutorial-window.html) to get the max element per unnested array. After that you can reaggregate the elements and remove the calculated max value from the array.
Result:
a max_elem remaining
123,534,243,345 534 123,243,345
3,23,1 23 3,17
42 42
56,123,234,345,345 345 56,123,234
This query needs only one split/unnest as well as only one max calculation.
SELECT
a,
max_elem,
array_remove(array_agg(elements), max_elem) as remaining -- C
FROM (
SELECT
*,
MAX(elements) OVER (PARTITION BY a) as max_elem -- B
FROM (
SELECT
a,
unnest((string_to_array(a, ','))::int[]) as elements -- A
FROM arrays
)s
)s
GROUP BY a, max_elem
A: string_to_array converts the string list into an array. Because the arrays are treated as string arrays you need the cast them into integer arrays by adding ::int[]. The unnest() expands all array elements into own rows.
B: window function MAX gives the maximum value of the single arrays as max_elem
C: array_agg reaggregates the elements through the GROUP BY id. After that array_remove removes the max_elem value from the array.
If you do not like to store them as pure arrays but as string list again you could add array_to_string. But I wouldn't recommend this because your data are integer arrays and not strings. For every further calculation you would need this string cast. A even better way (as already stated by #stickybit) is not to store the elements as arrays but as unnested data. As you can see in nearly every operation should would do the unnest before.
Note:
It would be better to use an ID to adress the columns/arrays instead of the origin string as in SQL Fiddle with IDs
If you install the extension intarray this is quite easy.
First you need to create the extension (you have to be superuser to do that):
create extension intarray;
Then you can do the following:
select original_ids,
original_ids[1] as max_id,
sort(original_ids - original_ids[1]) as remaining_ids
from (
select sort_desc(string_to_array(original_ids,',')::int[]) as original_ids
from bad_design
) t
But you shouldn't be storing comma separated values to begin with

determine DB2 text string length

I am trying to find out how to write an SQL statement that will grab fields where the string is not 12 characters long. I only want to grab the string if they are 10 characters.
What function can do this in DB2?
I figured it would be something like this, but I can't find anything on it.
select * from table where not length(fieldName, 12)
From similar question DB2 - find and compare the lentgh of the value in a table field - add RTRIM since LENGTH will return length of column definition. This should be correct:
select * from table where length(RTRIM(fieldName))=10
UPDATE 27.5.2019: maybe on older db2 versions the LENGTH function returned the length of column definition. On db2 10.5 I have tried the function and it returns data length, not column definition length:
select fieldname
, length(fieldName) len_only
, length(RTRIM(fieldName)) len_rtrim
from (values (cast('1234567890 ' as varchar(30)) ))
as tab(fieldName)
FIELDNAME LEN_ONLY LEN_RTRIM
------------------------------ ----------- -----------
1234567890 12 10
One can test this by using this term:
where length(fieldName)!=length(rtrim(fieldName))
This will grab records with strings (in the fieldName column) that are 10 characters long:
select * from table where length(fieldName)=10
Mostly we write below statement
select * from table where length(ltrim(rtrim(field)))=10;