Parse field form xml clob - sql

Ciao,
i've one problem with sql.
I've some table with datatype clob where is stored a clob.
In the same table if we have the xml format we use one function for take the xml_fied
CREATE OR REPLACE FUNCTION gettagvalue (
XMLBody IN CLOB, TagXml IN VARCHAR2) RETURN VARCHAR2 IS
BEGIN
return TO_CHAR (SUBSTR (XMLBody,
INSTR (XMLBody, '<'||TagXml||'>') + length(TagXml) + 2,
INSTR (XMLBody, '</'||TagXml||'>')
- INSTR (XMLBody, '<'||TagXml||'>')
- (length(TagXml) + 2)
)
);
END GetTagValue;
/
example :
Select errorhandler.GetTagValue(xml_content,'ORDERID')
from table_order
On the same table we have also some xml in json.
How i can create a copy of same function for take the field?
On xml is eay because we have the field with same name that start with and finish with but with json
I cannot understand how define the end of field

Do NOT try to parse XML or JSON as strings; use a proper XML or JSON parser.
If you have the table with the sample data:
CREATE TABLE table_name (
xml CLOB,
json CLOB CHECK ( json IS JSON )
);
INSERT INTO table_name ( xml, json ) VALUES (
'<a><b>BBB</b><c>CCC</c><d>DDD</d></a>',
'{"a":"aaa","b":"bbb","c":[1,2,3,4],"d":"ddd"}'
);
Then you can get the c values from both using the XMLQUERY or JSON_QUERY functions:
SELECT XMLQUERY(
'*/c/text()'
PASSING XMLTYPE(xml)
RETURNING CONTENT
) AS c_xml_value,
JSON_QUERY(
json,
'$.c'
RETURNING VARCHAR2(50)
) AS c_json_value
FROM table_name
Which outputs:
| C_XML_VALUE | C_JSON_VALUE |
| :---------- | :----------- |
| CCC | [1,2,3,4] |
If you have XML and JSON values in the same column then look at whether the first character is < or not and use the appropriate parsing function; do not try and create your own function to parse the values using substring matching.
For example:
CREATE TABLE table_name ( value CLOB );
INSERT INTO table_name ( value )
SELECT '<a><b>BBB</b><c>CCC</c><d>DDD</d></a>' FROM DUAL UNION ALL
SELECT '{"a":"aaa","b":"bbb","c":[1,2,3,4],"d":"ddd"}' FROM DUAL;
Then:
SELECT CASE
WHEN value LIKE '<%'
THEN CAST(
XMLQUERY( '*/c/text()' PASSING XMLTYPE(value) RETURNING CONTENT )
AS VARCHAR2(50)
)
ELSE JSON_QUERY( value, '$.c' RETURNING VARCHAR2(50) )
END AS c_value
FROM table_name
Outputs:
| C_VALUE |
| :-------- |
| CCC |
| [1,2,3,4] |
db<>fiddle here
Update
You can also use JSON_TABLE and XMLTABLE to get all the values out:
SELECT COALESCE( j.sourceChannel, x.sourceChannel ) AS sourceChannel,
COALESCE( j.transactionId, x.transactionId ) AS transactionId,
COALESCE( j.sessionId, x.sessionId ) AS transactionId,
COALESCE( j.status, x.status ) AS status,
COALESCE( j.errorcode, x.errorcode ) AS errorcode,
COALESCE( j.errordescription, x.errordescription ) AS errordescription
FROM table_name t
OUTER APPLY JSON_TABLE(
t.value,
'$.header'
COLUMNS (
sourceChannel VARCHAR2( 50) PATH '$.sourceChannel',
transactionId VARCHAR2( 50) PATH '$.transactionId',
sessionId VARCHAR2( 50) PATH '$.sessionId',
status VARCHAR2( 50) PATH '$.status',
errorcode VARCHAR2( 50) PATH '$.errorcode',
errordescription VARCHAR2(200) PATH '$.errordescription'
)
) j
LEFT OUTER JOIN LATERAL(
SELECT *
FROM XMLTABLE(
'/header'
PASSING XMLTYPE( value )
COLUMNS
sourceChannel VARCHAR2( 50) PATH 'sourceChannel',
transactionId VARCHAR2( 50) PATH 'transactionId',
sessionId VARCHAR2( 50) PATH 'sessionId',
status VARCHAR2( 50) PATH 'status',
errorcode VARCHAR2( 50) PATH 'errorcode',
errordescription VARCHAR2(200) PATH 'errordescription'
)
) x
ON ( t.value LIKE '<%' )
Which for the sample data:
CREATE TABLE table_name ( value CLOB );
INSERT INTO table_name ( value )
SELECT '<header>
<sourceChannel>xaaa</sourceChannel>
<transactionId>xbbb</transactionId>
<sessionId>xccc</sessionId>
<status>xddd</status>
<errorcode>xeee</errorcode>
<errordescription>xfff</errordescription>
</header>' FROM DUAL UNION ALL
SELECT '{"header":{"sourceChannel":"jaaa","transactionId":"jbbb","sessionId":"jccc","status":"jddd","errorcode":"jeee","errordescription":"jfff"}}' FROM DUAL;
Outputs:
SOURCECHANNEL
TRANSACTIONID
TRANSACTIONID
STATUS
ERRORCODE
ERRORDESCRIPTION
xaaa
xbbb
xccc
xddd
xeee
xfff
jaaa
jbbb
jccc
jddd
jeee
jfff

Related

Retrive an object from json string using JSON_QUERY in oracle

Below the code which I'm trying but getting null value.
SELECT json_query(object_data,'$.AOF.LEAD_DATA.DIRECTOR[*]')
FROM TB_COP_BUSS_OBJ_TXN FD,
JSON_TABLE(FD.OBJECT_DATA,'$.AOF.LEAD_DATA.DIRECTOR[*]' columns
( AUS_FLAG VARCHAR2(40) PATH '$.CHECKBOX.AUS_FLAG.value')) j
WHERE FD.OBJECT_PRI_KEY_1 = 'XXXXXXX' and j.AUS_FLAG ='Y'
I'm trying to get full data which is inside director object/array. when I'm using 0,1,2, instead of * then I'm getting the data but I need to check aus flag and need those index data of that array object. please help
please help.
If you have the sample data:
CREATE TABLE TB_COP_BUSS_OBJ_TXN (
OBJECT_PRI_KEY_1 VARCHAR2(20) PRIMARY KEY,
OBJECT_DATA CLOB CHECK (OBJECT_DATA IS JSON)
);
INSERT INTO TB_COP_BUSS_OBJ_TXN (
OBJECT_PRI_KEY_1,
OBJECT_DATA
) VALUES (
'XXXXXX',
'{"AOF":{"LEAD_DATA":{"DIRECTOR":[1,2,3]}}}'
);
Then you can use:
SELECT JSON_QUERY(OBJECT_DATA,'$.AOF.LEAD_DATA."DIRECTOR"')
from TB_COP_BUSS_OBJ_TXN FD
WHERE OBJECT_PRI_KEY_1 = 'XXXXXX'
Which outputs:
JSON_QUERY(OBJECT_DATA,'$.AOF.LEAD_DATA."DIRECTOR"')
[1,2,3]
Or you can use:
SELECT value
from TB_COP_BUSS_OBJ_TXN FD
CROSS APPLY JSON_TABLE(
fd.object_data,
'$.AOF.LEAD_DATA."DIRECTOR"[*]'
COLUMNS (
value NUMBER PATH '$'
)
)
WHERE OBJECT_PRI_KEY_1 = 'XXXXXX'
Which outputs:
VALUE
1
2
3
db<>fiddle here

Postgres: reading the results of 'returning *' into a variable

All the examples I have found for the Postgres 'returning' functionality (https://www.postgresql.org/docs/current/dml-returning.html) return values for a single row.
How do I read multiple result rows into a variable?
Executing the following outside a function gives the desired results:
create sequence core.test_id_seq start with 10000;
create table core.test (
test_id integer not null default nextval('core.test_id_seq'),
field integer not null
);
insert into core.test ( field )
select unnest( array[1, 2] ) as id
returning *
;
test_id | field
---------+-------
10000 | 1
10001 | 2
(2 rows)
But I want to read the results into a variable or table to work with:
do $$
declare
recs ??;
begin
create sequence core.test_id_seq start with 10000;
create table core.test (
test_id integer not null default nextval('core.test_id_seq'),
field integer not null
);
insert into core.test ( field )
select unnest( array[1, 2] ) as id
returning * into recs
;
end $$;
Is this possible?
Thanks
You need to use an array of integers:
do $$
declare
new_ids int[];
begin
with new_rows as (
insert into core.test ( field )
select unnest( array[1, 2] ) as id
returning *
)
select array_agg(field)
into new_ids
from new_rows;
... work with the new_ids array ...
end
$$;

Oracle Query to select separate alphabet+numbers and numbers after special character

I am writing a query in oracle where I have requirement to separate alphabet+numbers and numbers after special character as 2 different columns
Eg.
Colum Value is
ABC 123#78800,XYZ#4666,PQR 444#9900
Output Required
Column 1 : ABC 123,XYZ,PQR 444
Column 2 : 78800,4666, 9900
I tried following query:
select TRANSLATE('ABC 123#78800,XYZ#4666,PQR 444#9900 ','ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789#','ABCDEFGHIJKLMNOPQRSTUVWXYZ') from dual.
Output is "ABC ,XYZ,PQR " numbers are missing
What you need is regex
with table_name as
(
select 'ABC 123#78800,XYZ#4666,PQR 444#9900' col_name from dual
)
select
REGEXP_REPLACE(col_name,'#([^#,])*', null) alphabet_num
,REGEXP_REPLACE(col_name,'([^#,])*#', null) num_value
from
table_name;
Editted: Remove some redundant character as suggested by MTO
Oracle Setup:
CREATE TABLE table_name ( col VARCHAR2(100) );
INSERT INTO table_name VALUES ( 'ABC 123#78800,XYZ#4666,PQR 444#9900' );
CREATE TYPE key_value_pair AS OBJECT(
key VARCHAR2(20),
value NUMBER(10)
);
/
CREATE TYPE key_value_pair_table AS TABLE of key_value_pair;
/
Query:
SELECT *
FROM table_name t,
TABLE(
CAST(
MULTISET(
SELECT REGEXP_SUBSTR( t.col, '(^|,)([^,#]+)#(\d+)', 1, LEVEL, NULL, 2 ),
TO_NUMBER(
REGEXP_SUBSTR( t.col, '(^|,)([^,#]+)#(\d+)', 1, LEVEL, NULL, 3 )
)
FROM DUAL
CONNECT BY LEVEL <= REGEXP_COUNT( t.col, '(^|,)([^,#]+)#(\d+)' )
)
AS key_value_pair_table
)
);
Output:
COL KEY VALUE
----------------------------------- ------- -----
ABC 123#78800,XYZ#4666,PQR 444#9900 ABC 123 78800
ABC 123#78800,XYZ#4666,PQR 444#9900 XYZ 4666
ABC 123#78800,XYZ#4666,PQR 444#9900 PQR 444 9900

How to return only numbers from query where column is nvarchar

I have a simple query that is returning records where "column2" > 0
Here is the data in the database
Column1 Column2
1 123456789
2 123456781
3 13-151-1513
4 alsdjf
5
6 000000000
Her is the query
select column1, replace(a.Payroll_id,'-','')
from table1
where isnumeric(column2) = 1
I'd like to return the following:
Column1 Column2
1 123456789
2 123456781
3 131511513
This mean, I won't select any records when the column is blank (or null), will not return a row if it's not an integer, and will drop out the '-', and would not show row 6 since it's all 0.
How can I do this?
I think you can use something like this :
USE tempdb
GO
CREATE TABLE #Temp
(
ID INT IDENTITY
,VALUE VARCHAR(30)
)
INSERT INTO #Temp (VALUE) VALUES ('1213213'), ('1213213'), ('121-32-13'), ('ASDFASF2123')
GO
WITH CteData
AS
(
SELECT REPLACE(VALUE,'-','') as Valor FROM #Temp
)
SELECT * FROM CteData WHERE (ISNUMERIC(Valor) = 1 AND valor not like '%[0-0]%')
DROP TABLE #Temp
then you can apply validations for empty, NULL,0 etc
If you are using SQL2012 or above you can also use TRY_PARSE that is more selective in its parsing. This function will return NULL if a record can't be converted. You could use it like this:
CREATE TABLE #temp
(
ID INT IDENTITY ,
VALUE VARCHAR(30)
)
INSERT INTO #temp
( VALUE )
VALUES ( '1213213' ),
( '1213213' ),
( '121-32-13' ),
( 'ASDFASF2123' ),
( '0000000' )
SELECT ParsedValue
FROM #temp
CROSS APPLY ( SELECT TRY_PARSE(
Value AS INT ) AS ParsedValue
) details
WHERE ParsedValue IS NOT NULL
AND ParsedValue>0

How to have AND & OR condition in SQL Merge statement for DB2

I have a merge statement which works when I did not have to consider null values :
This works :
MERGE INTO LTABLE L
USING (SELECT 1392 UCL_USER_ID,11 REGISTER_ID ,5 REGION_ID FROM DUAL ) B
ON ( L.UCL_USER_ID = B.UCL_USER_ID
AND L.REGISTER_ID = B.REGISTER_ID
AND (L.REGION_ID = B.REGION_ID)
)
WHEN NOT MATCHED
THEN
INSERT (
L.LTABLE_ID
,L.UCL_USER_ID
,L.REGISTER_ID
,L.REGION_ID
)
VALUES (
SEQ_LTABLE_ID.NEXTVAL
,1392
,11
,5);
When I have to consider null values for REGION_ID the below works :
MERGE INTO LTABLE L
USING (SELECT 1392 UCL_USER_ID,11 REGISTER_ID ,NULL REGION_ID FROM DUAL ) B
ON ( L.UCL_USER_ID = B.UCL_USER_ID
AND L.REGISTER_ID = B.REGISTER_ID
AND (L.REGION_ID IS NULL AND B.REGION_ID IS NULL)
)
WHEN NOT MATCHED
THEN
INSERT (
L.LTABLE_ID
,L.UCL_USER_ID
,L.REGISTER_ID
,L.REGION_ID
)
VALUES (
SEQ_LTABLE_ID.NEXTVAL
,1392
,11
,NULL);
Question is how can I combine these two conditions when it can be null or some numeric value. I tried the below but sql developer gives the error that query is not right.
AND ((L.REGION_ID = B.REGION_ID) OR (L.REGION_ID IS NULL AND B.REGION_ID IS NULL))
ERROR :
SQL Error: DB2 SQL Error: SQLCODE=-418, SQLSTATE=42610, SQLERRMC=FCS already resolved to different type, DRIVER=4.17.29
This is just a guess, but perhaps the problem is the default type for NULL. Perhaps a cast will fix the problem:
MERGE INTO LTABLE L
USING (SELECT 1392 UCL_USER_ID, 11 as REGISTER_ID,
CAST(NULL as VARCHAR(255)) as REGION_ID
FROM DUAL ) B
ON ( L.UCL_USER_ID = B.UCL_USER_ID
AND L.REGISTER_ID = B.REGISTER_ID
AND (L.REGION_ID IS NULL AND B.REGION_ID IS NULL)
)
Or whatever the appropriate type is for REGION_ID.