Xpath and Oracle XMLTABLE - oracle

I have a CLOB containing an XML.
<attrs>
<attr name="1">
<string>stringvalue</string>
</attr>
<attr name="2">
<integer>1</integer>
</attr>
<attr name="3">
<integer>2</integer>
</attr>
<attr name="4">
<boolean>false</boolean>
</attr>
</attrs>
How can I get only boolean attributs and the name attribute ?
XMLTABLE('/attrs/attr/boolean'
PASSING XMLTYPE(CLOB)
COLUMNS ATTRIBUTENAME VARCHAR2(50) PATH '???',
ATTRIBUTEVALUE VARCHAR2(5) PATH '.'
) X
This way, I only have the value, how can I also get the parent #name ?
I can use '/attrs/attr' but I don't know how to get only the boolean (maybe I need to filter after ?)
Thanks!

You can walk back up the tree in the column path with:
'./../#name'
so that gives:
XMLTABLE('/attrs/attr/boolean'
PASSING XMLTYPE(CLOB)
COLUMNS ATTRIBUTENAME VARCHAR2(50) PATH './../#name',
ATTRIBUTEVALUE VARCHAR2(5) PATH '.'
) X
which with your data gets:
ATTRIBUTENAME ATTRIBUTEVALUE
------------- --------------
4 false
You could also apply a child-node filter in the main XPath with:
'/attrs/attr[boolean]'
and then get that attr node's name and its child boolean node, giving:
XMLTABLE('/attrs/attr[boolean]'
PASSING XMLTYPE(your_CLOB)
COLUMNS ATTRIBUTENAME VARCHAR2(50) PATH '#name',
ATTRIBUTEVALUE VARCHAR2(5) PATH 'boolean'
) X
which gets the same result.
db<>fiddle

Related

How can I modify object's attribute datatype

Say I have this TYPE:
create or replace TYPE type1
AS OBJECT
(order_date DATE
,order_status VARCHAR2(50)
,offer NUMBER
)
How can I change datatype of 'offer' to VARCHAR2(20)?
Only widening is possible with ALTER TYPE syntax:
ALTER TYPE type1 MODIFY ATTRIBUTE (offer VARCHAR2(20));
-- only widening of attribute 'OFFER' constraints is allowed
db<>fiddle demo
You need to recreate type:
create or replace TYPE type1 AS OBJECT(
order_date DATE
,order_status VARCHAR2(50)
,offer VARCHAR2(20)
);
You don't need to recreate a type for your case, only DROP/ADD(with cascade or invalidate option) an ATTRIBUTE is enough
ALTER TYPE type1 DROP ATTRIBUTE offer [cascade|invalidate];
ALTER TYPE type1 ADD ATTRIBUTE offer varchar2(20) [cascade|invalidate];

"Error at Command Line : 1 Column : 698 Error report - SQL Error: ORA-00984: column not allowed here

INSERT INTO
FLAG ("OPT_FLAG_KEY","H_KEY","FIRST_NAME","LAST_NAME",
"MIDDLE_NAME","TITLE","CREDENTIALS","ADDRESS_LINE_1",
"ADDRESS_LINE_2","ADDRESS_LINE_3","CITY","STATE",
"POSTAL_CODE","PHONE_NUMBER","BUSIN_PHONE","DECEASED",
"OPT_FLAG","OPT_FLAG_DATE","SOU_KEY","SOU_FILE_ID",
"SOU_FILE_ID_TEXT","BAT_ID","PHONE_NUMBER_SOURCE","BIRTH_DATE")
VALUES(37009326,4,'Daniel','Boyle',NULL,NULL,NULL,'368 Road',
NULL,NULL,'Doylown','BVBV',1801,NULL,NULL,'NO','OUT',
TO_CHAR('10-AUG-16','DD/MM/YYYY'),201,
TO_DATE(SUBSTR('vhic_pavir_20160810.txt',12,8),YYYYMMDD),
'2016-08-10',598441,NULL,TO_DATE('03-FEB-1952',DD-MM-YYYY));
I have run this query in my DB am getting some error like below
Error at Command Line : 1 Column : 698
Error report
SQL Error: ORA-00984: column not allowed here
00984. 00000 - "column not allowed here"
*Cause:
*Action:"
Edit: this is my desc of my table:
Name Null Type
------------------- ---- -------------
OPT_FLAG_KEY NUMBER(14)
H_KEY NUMBER(14)
FIRST_NAME VARCHAR2(50)
LAST_NAME VARCHAR2(50)
MIDDLE_NAME VARCHAR2(50)
TITLE VARCHAR2(50)
CREDENTIALS VARCHAR2(50)
ADDRESS_LINE_1 VARCHAR2(100)
ADDRESS_LINE_2 VARCHAR2(100)
ADDRESS_LINE_3 VARCHAR2(100)
CITY VARCHAR2(50)
STATE VARCHAR2(20)
POSTAL_CODE VARCHAR2(20)
PHONE_NUMBER VARCHAR2(100)
BUSIN_PHONE VARCHAR2(100)
DECEASED VARCHAR2(5)
OPT_FLAG VARCHAR2(10)
OPT_FLAG_DATE DATE
SOU_KEY NUMBER(14)
SOU_FILE_ID DATE
SOU_FILE_ID_TEXT VARCHAR2(20)
BATCH_ID NUMBER(14)
PHONE_NUMBER_SOURCE VARCHAR2(100)
BIRTH_DATE DATE
There are several issue with your statement. First, double-quotes are not required on column names (unless you have mixed cases or space), remove them.
TO_CHAR('10-AUG-16','DD/MM/YYYY') -> 10-AUG-16 is a string, not a date. Thus it does not make any sense to convert a string to a string
TO_DATE(SUBSTR('vhic_pavir_20160810.txt',12,8),YYYYMMDD) -> Format must be in single-quotes, i.e. TO_DATE(SUBSTR('vhic_pavir_20160810.txt',12,8),'YYYYMMDD')
'2016-08-10' -> do you like to insert a DATE value or a string? You provided a string, not a date.
TO_DATE('03-FEB-1952',DD-MM-YYYY)) -> Format must be in single-quotes, see above. MM means the month number, not the month name.

Error when selecting timestamp from XMLType column in Oracle 11g

I have 2 Oracle 11g databases with a table containing a XMLType column and some test data differing only in the separator (.,) for the milliseconds of the timestamp:
create table TEST_TIMESTAMP (
ID number(19,0) constraint "NN_TEST_TIMESTAMP_ID" not null,
DOC xmltype constraint "NN_TEST_TIMESTAMP_DOC" not null
);
insert into TEST_TIMESTAMP values ( 1, xmltype('<?xml version="1.0" encoding="utf-8"?><test><ts>2015-04-08T04:55:33.11</ts></test>'));
insert into TEST_TIMESTAMP values ( 2, xmltype('<?xml version="1.0" encoding="utf-8"?><test><ts>2015-04-08T04:55:33,11</ts></test>'));
When I try to extract the timestamp with the following statements, it fails either with the first document on one database or with the second document on the other database.
select x.*
from TEST_TIMESTAMP t,
xmltable(
'/test'
passing t.DOC
columns
ORIGINAL varchar2(50) path 'ts',
RESULT timestamp with time zone path 'ts'
) x
where t.ID = 1;
select x.*
from TEST_TIMESTAMP t,
xmltable(
'/test'
passing t.DOC
columns
ORIGINAL varchar2(50) path 'ts',
RESULT timestamp with time zone path 'ts'
) x
where t.ID = 2;
The error I get:
ORA-01858: a non-numeric character was found where a numeric was expected
01858. 00000 - "a non-numeric character was found where a numeric was expected"
*Cause: The input data to be converted using a date format model was
incorrect. The input data did not contain a number where a number was
required by the format model.
*Action: Fix the input data or the date format model to make sure the
elements match in number and type. Then retry the operation.
The only differences between those databases I've found are:
DB1: version=11.2.0.1.0, NLS_CHARACTERSET=AL32UTF8 -> fails on document 2
DB2: version=11.2.0.2.0, NLS_CHARACTERSET=WE8MSWIN1252 -> fails on document 1
DB1 has the behaviour that I would expect. Does anybody know why those databases behave differently and how to fix the issue in DB2?
Thanks in advance,
Oliver
My guess is that the nls_timestamp_format is different between the two databases.
However, rather than forcing the implicit conversion down at the XMLTABLE level, I would do an explicit conversion in the select list:
with test_timestamp as (select 1 id, xmltype('<?xml version="1.0" encoding="utf-8"?><test><ts>2015-04-08T04:55:33.11</ts></test>') doc from dual union all
select 2 id, xmltype('<?xml version="1.0" encoding="utf-8"?><test><ts>2015-04-08T04:55:33,11</ts></test>') doc from dual)
select x.original,
to_timestamp(x.original, 'yyyy-mm-dd"T"hh24:mi:ss,ff2') result
from test_timestamp t,
xmltable('/test' passing t.doc
columns original varchar2(50) path 'ts') x;
ORIGINAL RESULT
-------------------------------------------------- --------------------------------------------------
2015-04-08T04:55:33.11 08/04/2015 04:55:33.110000000
2015-04-08T04:55:33,11 08/04/2015 04:55:33.110000000
N.B. I found that using "ss.ff2" errored, but "ss,ff2" handled both cases just fine. I'm not sure if that's reliant on some other nls setting or not, though.

How can I set the maximum length of an xmltype?

I've created a table with the following:
create table mytable
(
primary_key varchar(30),
Myxml xmltype,
mydate DATE
);
When run describe mytable I get:
Name Null Type
------------------ ---- ------------
PRIMARY_KEY VARCHAR2(30)
MYXML XMLTYPE()
MYDATE DATE
The problem is, I'm using a propertery software that wants to convert a dataset and upload it to this table, when it run it it gives me a proprietary error:
Value too large for column myxml. Max length = 0, actual length = 1721
Is there a way that I can manually set the max length of MYXML to avoid this?

what is the difference between nested table and object type in oracle?

I have following object tables in oracle DB.
create type deposit_ty as object
(
depno number(6),
depcategory ref depcategory_ty,
amount number(6),
period number(2)
);
create type deposit_ntty as table of deposit_ty;
create type address_ty as object
(
homeno number,
street varchar(30),
city varchar(30)
);
create type customer_ty as object
(
cusid char(4),
custname varchar(40),
address address_ty,
dob DATE,
deposits deposit_ntty
);
can any one tell what is the difference between column address and deposits in customer_ty object table?
An object type/abstract data type/record is like a row or tuple: it contains an ordered set of attributes. To populate address you must set one and only one value for each of homeno, street, and city.
A nested table is like a table: it contains an unordered set of rows. Usually a nested table only contains a set of simple values, like a number. In your example, it is a set of object types. To populate deposits you can create any number of deposit_ty.
For example:
declare
customer customer_ty :=
customer_ty(
'ABC',
'Name',
address_ty('123', 'fake street', 'Springfield'),
sysdate,
deposit_ntty(
deposit_ty(1, null, 100, 1),
deposit_ty(2, null, 200, 2)
)
);
begin
null;
end;
Also, you probably want to use a VARCHAR2 instead of VARCHAR or CHAR. And if it's not too late, throw out all this object stuff and use tables like everyone else.

Resources