How to update XML attribute in clob Oracle using XMLQuery - sql

Oracle table name: SR_DATA;
Table field name: XMLDATA type CLOB;
Field value:
<module xmlns="http://www.mytest.com/2008/FMSchema">
<tmEsObjective modelCodeScheme="A" modelCodeSchemeVersion="01" modelCodeValue="ES_A"></tmEsObjective>
</module>
I need to update the value of the attribute "modelCodeValue" into ES_B.
This is the code:
UPDATE SR_DATA
SET XMLDATA =
XMLQuery('copy $i := $p1 modify
((for $j in $i/module/tmEsObjective/#modelCodeValue
return replace value of node $j with $p2))
)
return $i'
PASSING XMLType(REPLACE(xmldata, 'xmlns="http://www.mytest.com/2008/FMSchema"', '')) AS "p1",
'ES_B' AS "p2"
RETURNING CONTENT);
This code returns the error code: ORA-00932: inconsistent datatypes: expected CLOB got -

Use getclobval() like this:
UPDATE SR_DATA
SET XMLDATA =
XMLTYPE.GETCLOBVAL(XMLQuery('copy $i := $p1 modify
((for $j in $i/module/tmEsObjective/#modelCodeValue
return replace value of node $j with $p2))
return $i'
PASSING XMLType(REPLACE(xmldata, 'xmlns="http://www.mytest.com/2008/FMSchema"', '')) AS "p1",
'ES_B' AS "p2"
RETURNING CONTENT ));

Related

Create JSON from XML - JSON_AGG OUTPUT PROBLEM

I have a problem with converting XML content to JSON format (with plain oracle select statement), where more then 1 sub level of data is present in the original XML - with my code the result of level 2+ is presented as string and not as JSON_OBJECT. Please, could someone tell me, where is fault in my code or what I'm doing wrong:
source:
<envelope>
<sender>
<name>IZS</name>
<country>SU</country>
<address>LOCATION 10B</address>
<address>1000 CITY</address>
<sender_identifier>SU46794093</sender_identifier>
<sender_address>
<sender_agent>SKWWSI20XXX</sender_agent>
<sender_mailbox>SI56031098765414228</sender_mailbox>
</sender_address>
</sender>
</envelope>
transformation select statement:
WITH SAMPLE AS (SELECT XMLTYPE ('
<envelope>
<sender>
<name>IZS</name>
<country>SU</country>
<address>LOCATION 10B</address>
<address>1000 CITY</address>
<sender_identifier>SU46794093</sender_identifier>
<sender_address>
<sender_agent>SKWWSI20XXX</sender_agent>
<sender_mailbox>SI56031098765414228</sender_mailbox>
</sender_address>
</sender>
</envelope>') XMLDOC FROM DUAL)
SELECT JSON_SERIALIZE (
JSON_OBJECT (
KEY 'envelope' VALUE
JSON_OBJECTAGG (
KEY ID_LEVEL1 VALUE
CASE ID_LEVEL1
WHEN 'sender' THEN
( SELECT JSON_OBJECTAGG (
KEY ID_LEVEL2 VALUE
CASE ID_LEVEL2
WHEN 'sender_address' THEN
( SELECT JSON_OBJECTagg (KEY ID_LEVEL22 VALUE TEXT_LEVEL22)
FROM XMLTABLE ('/sender/sender_address/*'
PASSING XML_LEVEL2
COLUMNS ID_LEVEL22 VARCHAR2 (128) PATH './name()',
TEXT_LEVEL22 VARCHAR2 (128) PATH './text()'
)
)
ELSE
TEXT_LEVEL2
END)
FROM XMLTABLE ('/sender/*'
PASSING XML_LEVEL2
COLUMNS ID_LEVEL2 VARCHAR2 (1024) PATH './name()',
TEXT_LEVEL2 VARCHAR2 (1024) PATH './text()'
)
)
ELSE
'"' || TEXT_LEVEL1 || '"'
END FORMAT JSON)
) PRETTY
)JSON_DOC
FROM SAMPLE, XMLTABLE ('/envelope/*'
PASSING XMLDOC
COLUMNS ID_LEVEL1 VARCHAR2 (1024) PATH './name()',
TEXT_LEVEL1 VARCHAR2 (1024) PATH './text()',
XML_LEVEL2 XMLTYPE PATH '.'
);
wrong result:
{
"envelope" :
{
"sender" :
{
"name" : "IZS",
"country" : "SU",
"address" : "LOCATION 10B",
"address" : "1000 CITY",
"sender_identifier" : "SU46794093",
"sender_address" : "{\"sender_agent\":\"SKWWSI20XXX\",\"sender_mailbox\":\"SI56031098765414228\"}"
}
}
}
wrong part:
***"sender_address" : "{\"sender_agent\":\"SKWWSI20XXX\",\"sender_mailbox\":\"SI56031098765414228\"}"***
For the level 1 text you're wrapping the value in double-quotes and specifying format json; you aren't doing that for level 2. If you change:
ELSE
TEXT_LEVEL2
END
to:
ELSE
'"' || TEXT_LEVEL2 || '"'
END FORMAT JSON)
then the result is:
{
  "envelope" :
  {
    "sender" :
    {
      "name" : "IZS",
      "country" : "SU",
      "address" : "LOCATION 10B",
      "address" : "1000 CITY",
      "sender_identifier" : "SU46794093",
      "sender_address" :
      {
        "sender_agent" : "SKWWSI20XXX",
        "sender_mailbox" : "SI56031098765414228"
      }
    }
  }
}
fiddle
The problem is that you need kind of conditional "FORMAT JSON" in the "SELECT JSON_OBJECTAGG ( KEY ID_LEVEL2 VALUECASE ID_LEVEL2": when the ID_LEVEL2 is 'sender_address' but not in the ELSE part, but the syntax requires you put after the END of CASE, and of course this fails for the "ELSE TEXT_LEVEL2" part.

ORACLE : renaming a key in a json field by oracle

I have a table in which in one of the columns there is a json data as below
COLUMN NAME : JSON_CODE
{
"DataElaborazione" : "20220718",
"DataMovimento" : "20220531",
"DataRiferimento" : "20220715",
}
I want to rename DataRiferimento to datarif
expected result
COLUMN NAME : JSON_CODE
{
"DataElaborazione" : "20220718",
"DataMovimento" : "20220531",
"datarif" : "20220715",
}
see image for structure
thank you
You can use JSON_TRANSFORM function:
select JSON_TRANSFORM('{ "DataElaborazione" : "20220718", "DataMovimento" : "20220531", "DataRiferimento" : "20220715" }',
RENAME '$.DataRiferimento' = 'datarif')
from dual
Or JSON_OBJECT_T:
declare
v_json JSON_OBJECT_T;
begin
v_json := JSON_OBJECT_T('{ "DataElaborazione" : "20220718", "DataMovimento" : "20220531", "DataRiferimento" : "20220715" }');
v_json.rename_key('DataRiferimento', 'datarif');
dbms_output.put_line(v_json.stringify);
end;
You can create function and use it in your query:
create or replace function renameJsonKey(p_json IN VARCHAR2,
p_old_key IN VARCHAR2,
p_new_key IN VARCHAR2) return VARCHAR2
RESULT_CACHE RELIES_ON (your_table)
is
v_json JSON_OBJECT_T;
begin
v_json := JSON_OBJECT_T(p_json);
v_json.rename_key(p_old_key, p_new_key);
return v_json.stringify;
end;
select renameJsonKey(col, 'DataRiferimento', 'datarif') from your table

Update XML Clob node

I have a table Transaction , in which I have a clob xclob for which ,
I want to update the "property" node's "record_start_dll_date" value to record_start_date(i.e I want to remove the _dll part )
and "record_dll_end_date" to record_end_date .
I am using oracle database . How can I modify these node values??
<?xml version ="1.0"?>
<properties>
<property name ="record_start_dll_date">
<value>1/1/2021</value>
</property>
<property name ="record_dll_end_date">
<value>21/12/2021</value>
</property>
</properties>
You can use an XMLQuery update, either with the specific attribute values:
xmlquery ('copy $i := $p1 modify (
(for $j in $i/properties/property[#name="record_start_dll_date"]/#name
return replace value of node $j with $p2),
(for $j in $i/properties/property[#name="record_dll_end_date"]/#name
return replace value of node $j with $p3)
) return $i'
passing xmltype(xclob) as "p1",
'record_start_date' as "p2",
'record_end_date' as "p3"
returning content)
or to strip any _dll:
xmlquery ('copy $i := $p1 modify (
for $j in $i/properties/property[contains(#name, "_dll")]/#name
return replace value of node $j with replace($j, "_dll", "")
) return $i'
passing xmltype(xclob) as "p1"
returning content)
Either way you can incorporate that into an update statement, with a matching XMLExists clause to avoid unnecessary updates:
update transaction
set xclob = xmlquery ('copy $i := $p1 modify (
for $j in $i/properties/property[contains(#name, "_dll")]/#name
return replace value of node $j with replace($j, "_dll", "")
) return $i'
passing xmltype(xclob) as "p1"
returning content).getclobval()
where xmlexists('$p1/properties/property[contains(#name, "_dll")]'
passing xmltype(xclob) as "p1")
db<>fiddle

Parsing XML containing name space

I am having problems with parsing XML containing name-space.
This is my XML structure (snipped, with modified data):
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02 ./camt.053.001.02.xsd">
<BkToCstmrStmt>
<Stmt>
<Ntry>
<Amt Ccy="EUR">0.0</Amt>
<CdtDbtInd>CCCC</CdtDbtInd>
<Sts>BBBB</Sts>
<BookgDt>
<Dt>2011-01-01</Dt>
</BookgDt>
<ValDt>
<Dt>2011-01-01</Dt>
</ValDt>
<AcctSvcrRef>325569685ASDAS</AcctSvcrRef>
<BkTxCd>
<Prtry>
<Cd>NOTPROVIDED</Cd>
</Prtry>
</BkTxCd>
<NtryDtls>
<TxDtls>
<Refs>
<EndToEndId>DD545454545-54545-54</EndToEndId>
<TxId>46545445G5GG54DD5S</TxId>
</Refs>
<RltdPties>
<Dbtr>
<Nm>TEST</Nm>
<PstlAdr>
<Ctry>JJ</Ctry>
<AdrLine>TEST ADD</AdrLine>
<AdrLine>TEST ADD2</AdrLine>
</PstlAdr>
</Dbtr>
<DbtrAcct>
<Id>
<IBAN>000000000000000</IBAN>
</Id>
</DbtrAcct>
<Cdtr>
<Nm>TEST NAME</Nm>
<PstlAdr>
<Ctry>JJ</Ctry>
<AdrLine>TEST ADD3</AdrLine>
<AdrLine>TEST ADD4</AdrLine>
</PstlAdr>
</Cdtr>
<CdtrAcct>
<Id>
<IBAN>22222222222222</IBAN>
</Id>
</CdtrAcct>
</RltdPties>
<Purp>
<Cd>IIII</Cd>
</Purp>
<RmtInf>
<Strd>
<CdtrRefInf>
<Ref>GG56565656565656</Ref>
</CdtrRefInf>
<AddtlRmtInf>TEST TEST 123</AddtlRmtInf>
</Strd>
</RmtInf>
<RltdDts>
<IntrBkSttlmDt>2011-01-01</IntrBkSttlmDt>
</RltdDts>
</TxDtls>
</NtryDtls>
</Ntry>
</Stmt>
</BkToCstmrStmt>
</Document>
This is the code i use that works if root element is without namespace ():
DECLARE
l_clob CLOB;
l_bfile BFILE := BFILENAME('XML_IMPORT', 'test_import.xml');
l_dest_offset INTEGER := 1;
l_src_offset INTEGER := 1;
l_bfile_csid NUMBER := 0;
l_lang_context INTEGER := 0;
l_warning INTEGER := 0;
l_xml xmltype;
BEGIN
DBMS_LOB.createtemporary (l_clob, TRUE);
DBMS_LOB.fileopen(l_bfile, DBMS_LOB.file_readonly);
DBMS_LOB.loadclobfromfile (
dest_lob => l_clob,
src_bfile => l_bfile,
amount => DBMS_LOB.lobmaxsize,
dest_offset => l_dest_offset,
src_offset => l_src_offset,
bfile_csid => l_bfile_csid ,
lang_context => l_lang_context,
warning => l_warning);
DBMS_LOB.fileclose(l_bfile);
l_xml := xmltype(l_clob);
BEGIN
FOR r IN (
SELECT ExtractValue(Value(p),'/Ntry/Amt/text()') as TEMP_COL1
,ExtractValue(Value(p),'/Ntry/Amt/#Ccy/text()') as TEMP_COL2
,ExtractValue(Value(p),'/Ntry/ValDt/Dt/text()') as TEMP_COL3
,ExtractValue(Value(p),'/Ntry/BookgDt/Dt/text()') as TEMP_COL4
FROM TABLE(XMLSequence(Extract(l_xml,'/Document/BkToCstmrStmt/Stmt/Ntry'))) p
)
LOOP
dbms_output.put_line('Some value 1: ' || r.TEMP_COL1);
dbms_output.put_line('Some value 2: ' || r.TEMP_COL2);
dbms_output.put_line('Some value 3: ' || r.TEMP_COL3);
dbms_output.put_line('Some value 4: ' || r.TEMP_COL4);
END LOOP;
END;
END;
I would like to know how to modify my code to work with name space.
Google provided few examples but so far i had no luck implementing them into my solution.
I believe you need to pass the namespace to both the Extract() and ExtractValue() functions e.g.
FOR r IN (
SELECT ExtractValue(Value(p),'/Ntry/Amt/text()', 'xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02"') as TEMP_COL1
,ExtractValue(Value(p),'/Ntry/Amt/#Ccy','xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02"') as TEMP_COL2
,ExtractValue(Value(p),'/Ntry/ValDt/Dt/text()','xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02"') as TEMP_COL3
,ExtractValue(Value(p),'/Ntry/BookgDt/Dt/text()','xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02"') as TEMP_COL4
FROM TABLE(XMLSequence(Extract(l_xml,'/Document/BkToCstmrStmt/Stmt/Ntry', 'xmlns="urn:iso:std:iso:20022:tech:xsd:camt.053.001.02"'))) p
)
LOOP
dbms_output.put_line('Some value 1: ' || r.TEMP_COL1);
dbms_output.put_line('Some value 2: ' || r.TEMP_COL2);
dbms_output.put_line('Some value 3: ' || r.TEMP_COL3);
dbms_output.put_line('Some value 4: ' || r.TEMP_COL4);
END LOOP;
I don't think there is a way to specify a default namespace for the ExtractValue function, so my personal preference would be to do something like this instead which avoids repeatedly declaring the namespace:
FOR r IN (
SELECT p.temp_col1,
p.temp_col2,
p.temp_col3,
p.temp_col4
FROM xmltable(
--define a default namespace to be used for all values
xmlnamespaces(
DEFAULT 'urn:iso:std:iso:20022:tech:xsd:camt.053.001.02'
),
--define an xpath expression for the values you want to extract
'/Document/BkToCstmrStmt/Stmt/Ntry'
--pass the xml type in here
PASSING l_xml
--declare the columns you would like to extract and the formats they are expected to be in
COLUMNS
temp_col1 VARCHAR2(128) path '/Ntry/Amt/text()',
temp_col2 VARCHAR2(128) path '/Ntry/Amt/#Ccy',
temp_col3 VARCHAR2(128) path '/Ntry/ValDt/Dt/text()',
temp_col4 VARCHAR2(128) path '/Ntry/BookgDt/Dt/text()'
) p
)
LOOP
dbms_output.put_line('Some value 1: ' || r.temp_col1);
dbms_output.put_line('Some value 2: ' || r.temp_col2);
dbms_output.put_line('Some value 3: ' || r.temp_col3);
dbms_output.put_line('Some value 4: ' || r.temp_col4);
END LOOP;

How do you get the max_length for a mysql field

MySQL is returning the current field value length in property max_length.
I there a way to get the correct values ?
ie Field part_code is a varchar(32) that returns 3 if it contains the value of "ABC" instead of the expected result of 32
EDIT
original mysql
//--------------------------------------------------------------------------
function build_fields(){
$idx = 0;
$num = #mysql_num_fields($this->qry);
while($idx < $num){
$field = mysql_fetch_field($this->qry, $idx);
$this->fields[$field->name] = $field;
$idx++;
}
} // function build_fields()
//--------------------------------------------------------------------------
new mysqli
//--------------------------------------------------------------------------
function build_fields(){
foreach(mysqli_fetch_fields($this->qry) as $name => $value){
$this->fields[$name] = (array) $value;
}
} // function build_fields()
//--------------------------------------------------------------------------
Used
DESCRIBE table_name
and parsed the varchar(32) and int(4,2)
SELECT CHARACTER_MAXIMUM_LENGTH,
COLUMN_TYPE
FROM `information_schema`.`COLUMNS`
WHERE TABLE_SCHEMA = 'MySchemaName'
AND TABLE_NAME = 'MyTableName'
AND COLUMN_NAME = 'MyColumnName'
In addition to Mark Baker's method , you can also get column information in several other ways:
SHOW COLUMNS FROM table_name where field='field_name';
// OR
DESCRIBE table_name field_name;
Then you need to parse 'Type' column (it's returned as varchar(32)) to get the max length.