Вы находитесь на странице: 1из 66

1

XML in Oracle
A practical introduction to the primary features of Oracle XDB. Presented be Geoff Wiland. Based on article from Oracle by Tim Quinlan

www.skillbuilders.com

V2.1

Agenda
Brief history Intro XML DB SQL/XML Standard Structured vs Unstructured XML Storage Namespaces Schema Typed vs Untyped Data Schemas & Validation XQuery, XPATH, & FLOWR XML DB Repository Converting XML to Relational Data Converting Relational to XML Data XML Performance Tuning 11g New Features

www.skillbuilders.com

V2.1

A Bit of History
Starting a few years ago, lots of XML entering databases No formal vendor tools or solution Had to store, query, and update XML data as unstructured LOBs Alternatively, shred data into relational tables
Then

put it back together

www.skillbuilders.com

When stored as LOBs, we had to use the DBMS_LOB package to process the XML data.

V2.1

Enter XML DB
1st delivered Oracle 9i Database Release 2 Built-in features for XML data
Storing

as unstructured LOBs Retrieving Manipulating XML data in the database

Oracle 10g Release 2 XML DB greatly extends features

www.skillbuilders.com

V2.1

Do I Have XML DB?


XML DB included with XE, SE and EE Check if installed:
SQL> SQL> 2 2 3 3 4 4 select select comp_name, comp_name, status, status, schema schema from dba_registry from dba_registry where where comp_name comp_name like like '%XML%' '%XML%' / / STATUS STATUS --------------------VALID VALID SCHEMA SCHEMA ----------XDB XDB
@xmldb @xmldb
www.skillbuilders.com

COMP_NAME COMP_NAME --------------------------------------Oracle Oracle XML XML Database Database

@xmldb set linesize 150 set pagesize 32000 column comp_name format a20 column schema format a6 select comp_name, status, schema from dba_registry where comp_name like '%XML%'; COMP_NAME STATUS Schema -------------------- ----------- -----Oracle XML Database VALID XDB

V2.1

SQL/XML Standard
SQL/XML ANSI ISO standard

See www.sqlx.org

SQL/XML defines a standardized mechanism for using SQL and XML together Store XML in RDBMS Query with XQuery and XPath Generate XML from SQL query / relational data 10g R2 implements SQL/XML 2003

Plus some features

www.skillbuilders.com

V2.1

XMLType
Use XMLType to create table, column ,view Built-in methods to create, extract, index XML data PL/SQL, Java and .NET support Store as
Unstructured

single LOB column Structured set of objects


SQL> SQL> create create table table test_xml test_xml of of XMLtype XMLtype 2 XMLType store as CLOB; 2 XMLType store as CLOB;

@test_xml @test_xml

Table Table created. created.


www.skillbuilders.com

@test_xml drop table test_xml; create table test_xml of XMLtype XMLType store as CLOB; desc test_xml Name Null? Type ----------------------- -------- ---------------TABLE of SYS.XMLTYPE

V2.1

Structured Storage
Implemented as set of objects Document is in effect stored as a virtual document through the relational tables Maintains Document Object Model (DOM) fidelity
(But

not a byte-for-byte representation of doc)

Creates an XMLType view over existing relational data


www.skillbuilders.com

V2.1

Structured Storage
Performance advantages
No

tags, so reduced memory & storage Granular retrieval Better b-tree, function-based indexing In-place, granular updates using XPath rewrite

Disadvantages
Increased

insert, select overhead for whole doc Not a byte-for-byte copy of original doc Order of data in doc not maintained
www.skillbuilders.com

V2.1

10

Unstructured Storage
CLOB storage Advantages
Match

byte-for-byte original document Can perform better when read or update entire doc

Disadvantages
Extra

overhead to update small piece of doc Constraints cannot be implemented Less efficient memory management

www.skillbuilders.com

V2.1

11

Namespaces
Namespace describes set of related attributes or elements Can be used to ensure document constructs have unique names XMLType methods and XML functions use namespace prefixes If no target namespace

Prefix is in the noNameSpace namespace http://www.w3.org/2001/XMLSchema


Special namespaces

overall XMLSchema namespace Oracle-supplied XML DB namespace

http://xmlns.oracle.com/xdb

www.skillbuilders.com

V2.1

12

.Namespaces
Specify a namespace when defining an element
<xy:elementName <xy:elementName xmlns:xy="http.name.com" xmlns:xy="http.name.com" /> /> xmlns is a reserved word telling us this is a namespace definition http.name.com is any URI identifying the namespace xy is a short prefix bound to the URI The prefix is used to indicate that an XML construct belongs to a specific URI

www.skillbuilders.com

V2.1

13

Namespace Example
<xs:schema <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xdb= xmlns:xdb= "http://xmlns.oracle.com/xdb" "http://xmlns.oracle.com/xdb" version="1.0"> version="1.0"> <xs:element name="INVOICESCHEMA" <xs:element name="INVOICESCHEMA" xdb:defaultTable="INVOICESCHEMA"> xdb:defaultTable="INVOICESCHEMA"> <xs:complexType> <xs:complexType> <xs:sequence> <xs:sequence> <xs:element <xs:element name="MailAddressTo"> name="MailAddressTo"> <xs:complexType> <xs:complexType> <xs:sequence> <xs:sequence> Create schema <xs:element <xs:element name="Person" name="Person" type="xs:string"/> type="xs:string"/> <xs:element <xs:element name="Street" name="Street" type="xs:string"/> type="xs:string"/> </xs:sequence> </xs:sequence> <xs:attribute <xs:attribute name="id" name="id" type="xs:string" type="xs:string" use="required"/> use="required"/> </xs:complexType> </xs:complexType> </xs:element> </xs:element> </xs:sequence> </xs:sequence> </xs:complexType> </xs:complexType> </xs:element> </xs:element> Declare prefixes xs and xdb </xs:schema> </xs:schema>
www.skillbuilders.com

Declare prefixes: xs prefix binds to http://www.w3.org/2001/XMLSchema URI xdb prefix binds to http://xmlns.oracle.com/xdb URI

V2.1

14

Loading XML Data


Support for SQL, PL/SQL, Java, C, SQL*Loader Example: SQL Insert from OS file Create Oracle directory
create create or or replace replace directory directory xmldir xmldir as as 'c:\oraclass'; 'c:\oraclass';

Create table with XML column


create create table table invoiceXML_col invoiceXML_col ( ( inv_id number inv_id number primary primary key, key, inv_doc inv_doc XMLType); XMLType);
@invoiceXML_col @invoiceXML_col
www.skillbuilders.com

@invoiceXML_col create or replace directory xmldir as 'c:\oraclass'; drop table invoiceXML_col; create table invoiceXML_col (inv_id number primary key,inv_doc XMLType); desc invoiceXML_col Name Null? ----------------------- -------INV_ID NOT NULL INV_DOC Type ---------------NUMBER XMLTYPE

V2.1

15

Loading XML Data


Or create XMLType table
create create table table invoiceXML_tbl invoiceXML_tbl of of XMLtype; XMLtype;

See indexes created


(Two

on each table)

select select index_name, index_name, table_name table_name from user_indexes from user_indexes where where table_name table_name in in ('INVOICEXML_COL', ('INVOICEXML_COL', 'INVOICEXML_TBL'); 'INVOICEXML_TBL');
@invoiceXML_tbl @invoiceXML_tbl
www.skillbuilders.com

@invoiceXML_tbl drop table invoiceXML_tbl; create table invoiceXML_tbl of XMLtype; desc invoiceXML_tbl Name Null? Type ----------------------------- -------- -------------------TABLE of XMLTYPE -- Two indexes are created for each table: 1 for the primary key and -- one for the LOB: select index_name, table_name from user_indexes where table_name in ('INVOICEXML_COL', 'INVOICEXML_TBL'); INDEX_NAME -----------------------------SYS_IL0000063107C00003$$ SYS_C009739 SYS_IL0000063099C00003$$ SYS_C009737 4 rows selected. TABLE_NAME -----------------------------INVOICEXML_TBL INVOICEXML_TBL INVOICEXML_COL INVOICEXML_COL

V2.1

16

Loading XML Data


Insert with SQL
insert insert into into invoicexml_col invoicexml_col values values (1, (1, XMLType(bfilename('XMLDIR', XMLType(bfilename('XMLDIR', 'invoicexml.txt'), 'invoicexml.txt'), nls_charset_id('AL32UTF8') nls_charset_id('AL32UTF8') )); )); select select * * from from invoicexml_col; invoicexml_col; insert insert into into invoicexml_tbl invoicexml_tbl values values (XMLType(bfilename('XMLDIR', (XMLType(bfilename('XMLDIR', 'invoicexml.txt'), 'invoicexml.txt'), nls_charset_id('AL32UTF8'))); nls_charset_id('AL32UTF8'))); select select object_value object_value from from invoicexml_tbl; invoicexml_tbl; --- Note Note the the pseudocolumn pseudocolumn object_value. object_value.
@insert1 @insert1
www.skillbuilders.com

@insert1 set long 32000 truncate table invoicexml_col; truncate table invoicexml_tbl; insert into invoicexml_col values (1, XMLType(bfilename('XMLDIR', 'invoicexml.txt'), nls_charset_id('AL32UTF8') )); col inv_doc format a100 select * from invoicexml_col; INV_ID INV_DOC ---------- ---------------------------------------1 <Invoice> <MailAddressTo id="PA"> <Person>Joe Smith</Person> . . . . . </Invoice> insert into invoicexml_tbl values (XMLType(bfilename('XMLDIR', 'invoicexml.txt'), nls_charset_id('AL32UTF8'))); -- Note the pseudocolumn object_value. select object_value from invoicexml_tbl; OBJECT_VALUE ---------------------------------------------<Invoice> <MailAddressTo id="PA"> <Person>Joe Smith</Person> . . . . . </Invoice>

V2.1

17

Schema-Typed Data
XMLType data implemented as schematyped or untyped data XMLSchema provides schema-typed
Supplies

information about document structure and

contents

Better documentation, validation, and controls Potentially more-efficient query and update processing

www.skillbuilders.com

V2.1

18

Schema-Typed Data
XMLSchema enables structured storage data
Document

can be decomposed into a set of

objects

XMLType methods schemaValidate() and isSchemaValid()


Provide

validation of an XMLType document using the schema definition.

www.skillbuilders.com

V2.1

19

Inserting Schema-Typed Docs


Create a schema Register the schema Create an XMLSchema-based table Insert data

www.skillbuilders.com

V2.1

20

Create Schema
<xs:schema <xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xdb="http://xmlns.oracle.com/xdb xmlns:xdb="http://xmlns.oracle.com/xdb version=1.0"> version=1.0"> <xs:element <xs:element name="INVOICEFORMTEST"> name="INVOICEFORMTEST"> <xs:complexType> <xs:complexType> <xs:sequence> <xs:sequence> <xs:element <xs:element name="MailAddressTo"> name="MailAddressTo"> <xs:complexType> <xs:complexType> <xs:sequence> <xs:sequence> <xs:element <xs:element name="Person" name="Person" type="xs:string"/> type="xs:string"/> <xs:element <xs:element name="Street" name="Street" type="xs:string"/> type="xs:string"/> <xs:element <xs:element name="City" name="City" type="xs:string"/> type="xs:string"/> <xs:element name="State" type="xs:string"/> <xs:element name="State" type="xs:string"/> <xs:element <xs:element name="Zipcode" name="Zipcode" type="xs:string"/> type="xs:string"/> </xs:sequence> </xs:sequence> <xs:attribute <xs:attribute name="id" name="id" type="xs:string" type="xs:string" use="required"/> use="required"/> </xs:complexType> </xs:complexType> </xs:element> </xs:element> </xs:sequence> </xs:sequence> </xs:complexType> </xs:complexType> </xs:element> Create in invoiceformtest.xsd in directory XML_DIR </xs:element> </xs:schema> </xs:schema>

www.skillbuilders.com

V2.1

21

Register Schema
BEGIN BEGIN DBMS_XMLSCHEMA.registerSchema( DBMS_XMLSCHEMA.registerSchema( SCHEMAURL SCHEMAURL => => 'http://xmlns.oracle.com/xdb/invoiceformtest.xsd', 'http://xmlns.oracle.com/xdb/invoiceformtest.xsd', SCHEMADOC SCHEMADOC => => bfilename('XMLDIR','invoiceformtest.xsd'), bfilename('XMLDIR','invoiceformtest.xsd'), GENTABLES GENTABLES => => false, false, CSID CSID => => nls_charset_id('AL32UTF8')); nls_charset_id('AL32UTF8')); END; END; / / To To view view your your registered registered schemas: schemas: SELECT schema_url SELECT schema_url FROM FROM user_xml_schemas; user_xml_schemas;

@register_schema @register_schema

www.skillbuilders.com

The SCHEMAURL above is any URI. It does not have to exist anywhere. @register_schema BEGIN DBMS_XMLSCHEMA.deleteSchema( SCHEMAURL => 'http://xmlns.oracle.com/xdb/invoiceformtest.xsd', DELETE_OPTION => dbms_xmlschema.DELETE_CASCADE_FORCE ); END; / BEGIN DBMS_XMLSCHEMA.registerSchema( SCHEMAURL => 'http://xmlns.oracle.com/xdb/invoiceformtest.xsd', SCHEMADOC => bfilename('XMLDIR','invoiceformtest.xsd'), GENTABLES => false, CSID => nls_charset_id('AL32UTF8')); END; / -- To view your registered schemas: SELECT schema_url FROM user_xml_schemas; SCHEMA_URL -----------------------------------------------http://xmlns.oracle.com/xdb/invoiceformtest.xsd

V2.1

22

Create Schema-Based XMLType Table


create create table table invoiceformtestXML_tbl invoiceformtestXML_tbl of of xmltype xmltype xmlschema xmlschema "http://xmlns.oracle.com/xdb/invoiceformtest.xsd" "http://xmlns.oracle.com/xdb/invoiceformtest.xsd" Element Element "INVOICEFORMTEST"; "INVOICEFORMTEST";

@create_schema_table @create_schema_table

www.skillbuilders.com

@create_schema_table drop table invoiceformtestXML_tbl; create table invoiceformtestXML_tbl of xmltype xmlschema "http://xmlns.oracle.com/xdb/invoiceformtest.xsd" -- Note: these are in double quotes, not single quotes! Element "INVOICEFORMTEST"; desc invoiceformtestXML_tbl Name -------------------------------------------------------------------TABLE of SYS.XMLTYPE( XMLSchema "http://xmlns.oracle.com/xdb/invoiceformtest.xsd" Element "INVOICEFORMTEST") STORAGE Object-relational TYPE "INVOICEFORMTEST332_T"

V2.1

23

Insert Data
insert insert into into invoiceformtestXML_tbl invoiceformtestXML_tbl values values ( XMLType(bfilename ('XMLDIR', (XMLType(bfilename ('XMLDIR', 'invoiceformtest.txt'), 'invoiceformtest.txt'), nls_charset_id('AL32UTF8'))); nls_charset_id('AL32UTF8'))); --- Query Query data data set set long long 32000 32000 select select object_value object_value from from invoiceformtestXML_tbl; invoiceformtestXML_tbl;

@insert2 @insert2

www.skillbuilders.com

@insert2 insert into invoiceformtestXML_tbl values (XMLType(bfilename('XMLDIR', 'invoiceformtest.txt'), nls_charset_id('AL32UTF8'))); -- Query data set long 32000 select object_value from invoiceformtestXML_tbl; OBJECT_VALUE ---------------------------------------<INVOICEFORMTEST> <MailAddressTo id="1"> <Person>Joe Smith</Person> <Street>10 Apple Tree Lane</Street> <City>New York</City> <State>NY</State> <Zipcode>12345</Zipcode> </MailAddressTo> </INVOICEFORMTEST>

V2.1

24

Validation at Insert

<INVOICEFORMTEST> validation is <INVOICEFORMTEST> essentially an <MailAddressTo <MailAddressTo id="1"> id="1"> extension of <Person>Joe <Person>Joe Smith</Person> Smith</Person> Oracles <Street>10 Apple Tree Lane</Street> <Street>10 Apple Tree Lane</Street> constraint <City>New checking! <City>New York</City> York</City> <State>NY</State> <State>NY</State> <Zipcode>12345</Zipcode> <Zipcode>12345</Zipcode> Doc does not adhere to <Country>USA</Country> <Country>USA</Country> XMLSchema; no Country </MailAddressTo> </MailAddressTo> tag in schema. </INVOICEFORMTEST> </INVOICEFORMTEST> @insert3 @insert3 insert insert into into invoiceformtestXML_tbl invoiceformtestXML_tbl values values (XMLType(bfilename('XMLDIR', (XMLType(bfilename('XMLDIR', 'invoiceformtestinvalid.txt'), 'invoiceformtestinvalid.txt'), nls_charset_id('AL32UTF8'))); nls_charset_id('AL32UTF8'))); Insert SQL INSERT fails Insert into into invoiceformtestXML_tbl invoiceformtestXML_tbl values values * * ERROR : ERROR at at line line 1 1 : ORA-30937: No schema ORA-30937: No schema definition definition for for 'Country' 'Country' (namespace (namespace www.skillbuilders.com '##local') '##local') in in parent parent '/INVOICEFORMTEST/MailAddressTo' '/INVOICEFORMTEST/MailAddressTo'

Schema

Now lets try to insert an XML document that is not in the format specified by the implemented schema. Such a file is invoiceformtestinvalid.txt: <INVOICEFORMTEST> <MailAddressTo id="1"> <Person>Joe Smith</Person> <Street>10 Apple Tree Lane</Street> <City>New York</City> <State>NY</State> <Zipcode>12345</Zipcode> <Country>USA</Country> No Country tag in schema! </MailAddressTo> </INVOICEFORMTEST> @insert3 insert into invoiceformtestXML_tbl values (XMLType(bfilename('XMLDIR', 'invoiceformtestinvalid.txt'), nls_charset_id('AL32UTF8'))); Insert into invoiceformtestXML_tbl values * ERROR at line 1: ORA-30937: No schema definition for 'Country' (namespace '##local') in parent '/INVOICEFORMTEST/MailAddressTo'

Note that Oracle validates the data. Schema validation is essentially an extension of Oracles constraint checking!

V2.1

25

Create Schema-Based XMLType Column


create create table table invoiceformtestXML_col invoiceformtestXML_col ( ( inv_id number inv_id number primary primary key, key, inv_doc inv_doc XMLType) XMLType) XMLTYPE XMLTYPE COLUMN COLUMN inv_doc inv_doc xmlschema xmlschema "http://xmlns.oracle.com/xdb/invoiceformtest.xsd" "http://xmlns.oracle.com/xdb/invoiceformtest.xsd" Element Element "INVOICEFORMTEST"; "INVOICEFORMTEST";

@create_schema_column @create_schema_column

www.skillbuilders.com

@create_schema_column drop table invoiceformtestXML_col; create table invoiceformtestXML_col ( inv_id number primary key, inv_doc XMLType) XMLTYPE COLUMN inv_doc "http://xmlns.oracle.com/xdb/invoiceformtest.xsd" Element "INVOICEFORMTEST"; set linesize 80 desc invoiceformtestXML_col Name Null? Type -------- -------- -------INV_ID NOT NULL NUMBER INV_DOC SYS.XMLTYPE(XMLSchema "http://xmlns.oracle.com/xdb/ invoiceformtest.xsd" Element "INVOICEFORMTEST") STORAGE Object-relational TYPE "INVOICEFORMTEST332_T" set linesize 150

xmlschema

V2.1

26

Insert Data
insert insert into into invoiceformtestXML_col invoiceformtestXML_col values values (1, XMLType(bfilename ('XMLDIR', (1, XMLType(bfilename ('XMLDIR', 'invoiceformtest.txt'), 'invoiceformtest.txt'), nls_charset_id('AL32UTF8'))); nls_charset_id('AL32UTF8'))); --- Query Query set set long long select select * * data data 32000 32000 from from invoiceformtestXML_tbl; invoiceformtestXML_tbl;
@insert4 @insert4

www.skillbuilders.com

@insert4 truncate table invoiceformtestXML_col; insert into invoiceformtestXML_col values (1, XMLType(bfilename('XMLDIR', 'invoiceformtest.txt'), nls_charset_id('AL32UTF8'))); -- Query data set long 32000 column inv_doc format a40 select * from invoiceformtestXML_col; INV_ID INV_DOC ---------- ---------------------------------------1 <INVOICEFORMTEST> <MailAddressTo id="1"> <Person>Joe Smith</Person> <Street>10 Apple Tree Lane</Street> <City>New York</City> <State>NY</State> <Zipcode>12345</Zipcode> </MailAddressTo> </INVOICEFORMTEST> clear columns

V2.1

Validation at Insert

27

<INVOICEFORMTEST> <INVOICEFORMTEST> <MailAddressTo <MailAddressTo id="1"> id="1"> @insert5 @insert5 <Person>Joe <Person>Joe Smith</Person> Smith</Person> <Street>10 <Street>10 Apple Apple Tree Tree Lane</Street> Lane</Street> <City>New <City>New York</City> York</City> <State>NY</State> <State>NY</State> <Zipcode>12345</Zipcode> <Zipcode>12345</Zipcode> <Country>USA</Country> <Country>USA</Country> </MailAddressTo> </MailAddressTo> Doc does not adhere to XMLSchema; </INVOICEFORMTEST> </INVOICEFORMTEST>
no Country tag in schema.

insert insert into into invoiceformtestXML_col invoiceformtestXML_col values values (2, XMLType(bfilename('XMLDIR', SQL INSERT fails (2, XMLType(bfilename('XMLDIR', 'invoiceformtestinvalid.txt'), 'invoiceformtestinvalid.txt'), nls_charset_id('AL32UTF8'))); nls_charset_id('AL32UTF8'))); Insert Insert into into invoiceformtestXML_col invoiceformtestXML_col values values * * ERROR : ERROR at at line line 1 1 : ORA-30937: No schema ORA-30937: No schema definition definition for for 'Country' 'Country' (namespace (namespace '##local') in parent '/INVOICEFORMTEST/MailAddressTo' '##local') in parent '/INVOICEFORMTEST/MailAddressTo'
www.skillbuilders.com

Now lets try to insert an XML document that is not in the format specified by the implemented schema. Such a file is invoiceformtestinvalid.txt: <INVOICEFORMTEST> <MailAddressTo id="1"> <Person>Joe Smith</Person> <Street>10 Apple Tree Lane</Street> <City>New York</City> <State>NY</State> <Zipcode>12345</Zipcode> <Country>USA</Country> No Country tag in schema! </MailAddressTo> </INVOICEFORMTEST> @insert5 insert into invoiceformtestXML_col values (2, XMLType(bfilename('XMLDIR', 'invoiceformtestinvalid.txt'), nls_charset_id('AL32UTF8'))); Insert into invoiceformtestXML_col values * ERROR at line 1: ORA-30937: No schema definition for 'Country' (namespace '##local') in parent '/INVOICEFORMTEST/MailAddressTo'

Note again that Oracle validates the data. Schema validation is essentially an extension of Oracles constraint checking!

V2.1

28

XQuery
XQuery standard developed by W3C Query language to extract info from XML source
physical

XML documents XML stored in relational database

XQuery is to XML as SQL is to relational databases


Just

more complex

XQuery uses XPath used to search


www.skillbuilders.com

V2.1

29

XPath
Language for finding information in XML doc
Part

of W3C standard See www.w3c.org

Used to navigate XML documents

www.skillbuilders.com

V2.1

30

XPath Examples
select select object_value object_value from from invoicexml_tbl; invoicexml_tbl;

@xpath @xpath

XML source doc for XPath examples OBJECT_VALUE OBJECT_VALUE <Invoice> <Invoice> <MailAddressTo <MailAddressTo id="PA"> id="PA"> <Person>Joe <Person>Joe Smith</Person> Smith</Person> <Street>10 <Street>10 Apple Apple Tree Tree Lane</Street> Lane</Street> <City>New York</City> <City>New York</City> <State>NY</State> <State>NY</State> <Zipcode>12345</Zipcode> <Zipcode>12345</Zipcode> </MailAddressTo> </MailAddressTo> <MailAddressFrom <MailAddressFrom id="PA"> id="PA"> <Person>Ed <Person>Ed Jones</Person> Jones</Person> <Street>11 <Street>11 Cherry Cherry Lane</Street> Lane</Street> <City>Newark</City> <City>Newark</City> (etcetera) (etcetera)
www.skillbuilders.com

@xpath select object_value from invoicexml_tbl; OBJECT_VALUE <Invoice> <MailAddressTo id="PA"> <Person>Joe Smith</Person> <Street>10 Apple Tree Lane</Street> <City>New York</City> <State>NY</State> <Zipcode>12345</Zipcode> </MailAddressTo> <MailAddressFrom id="PA"> <Person>Ed Jones</Person> <Street>11 Cherry Lane</Street> <City>Newark</City> <State>NJ</State> <Zipcode>67890</Zipcode> </MailAddressFrom> <Details id="2006Sept1to30PA"> <FromTo>Sept 1, 2006 to Sept 30, 2006</FromTo> <Hours>70</Hours> <Rate>30</Rate> <Taxes>210</Taxes> <TotalDue>2310</TotalDue> <InvDate>Oct 1, 2006</InvDate> <Contractor>Ed Jones</Contractor> </Details></Invoice> </Invoice>

V2.1

31

extract
Use extract function to select
Individual @extract @extract

node and its leaf nodes Or a set of nodes and their leaf nodes
select select extract(object_value, extract(object_value, '/Invoice/MailAddressTo') '/Invoice/MailAddressTo') from invoicexml_tbl; from invoicexml_tbl;
XPath expression

EXTRACT(OBJECT_VALUE,'/INVOICE/MAILADDRESSTO') EXTRACT(OBJECT_VALUE,'/INVOICE/MAILADDRESSTO') <MailAddressTo <MailAddressTo id="PA"><Person>Joe id="PA"><Person>Joe Smith</Person><Street>10 Smith</Person><Street>10 Apple Apple Tree Tree Lane</Street><City>New Lane</Street><City>New York</City><State>NY</Stat York</City><State>NY</Stat e><Zipcode>12345</Zipcode></MailAddressTo> e><Zipcode>12345</Zipcode></MailAddressTo>
select select extract(object_value, extract(object_value, './/Person') './/Person') from from invoicexml_tbl; invoicexml_tbl; XPath expression EXTRACT(OBJECT_VALUE,'.//PERSON') EXTRACT(OBJECT_VALUE,'.//PERSON') <Person>Joe <Person>Joe Smith</Person><Person>Ed Smith</Person><Person>Ed Jones</Person> Jones</Person>
www.skillbuilders.com

@extract select extract(object_value, '/Invoice/MailAddressTo') from invoicexml_tbl; EXTRACT(OBJECT_VALUE,'/INVOICE/MAILADDRESSTO') <MailAddressTo id="PA"><Person>Joe Smith</Person> <Street>10Apple Tree Lane</Street> <City>NewYork</City><State>NY</State><Zipcode>12345</Zipcode> </MailAddressTo> select extract(object_value, './/Person') from invoicexml_tbl; EXTRACT(OBJECT_VALUE,'.//PERSON') <Person>Joe Smith</Person><Person>Ed Jones</Person>

V2.1

32

extractValue
Select data value that exists in a leaf node with extractValue Higher level node cannot be extracted output not in XML-syntax format @extractValue @extractValue
select select extractValue(object_value, extractValue(object_value, '/Invoice/MailAddressTo/Person') '/Invoice/MailAddressTo/Person') as as Person Person from from invoicexml_tbl; invoicexml_tbl; PERSON PERSON Joe Joe Smith Smith
www.skillbuilders.com

@extractValue select extractValue(object_value, '/Invoice/MailAddressTo/Person') as Person from invoicexml_tbl; PERSON Joe Smith select extractValue(object_value, '/Invoice/MailAddressTo/@id') as Id from invoicexml_tbl; ID PA select extractValue(object_value, '/Invoice/MailAddressTo') as MailAddressTo from invoicexml_tbl; from invoicexml_tbl * ERROR at line 2: ORA-19025: EXTRACTVALUE returns value of only one node

V2.1

33

existsNode
Search for specific values at the node level

and only the node level

@existsNode @existsNode

Returns True (1) or False (0)


select select count(*) count(*) from from invoicexml_tbl invoicexml_tbl where where existsNode( existsNode( object_value, object_value, '/Invoice/MailAddressTo[Person="Joe '/Invoice/MailAddressTo[Person="Joe Smith"]') Smith"]') = = 1; 1; COUNT(*) COUNT(*) --------------1 1 select select count(*) count(*) from from invoicexml_tbl invoicexml_tbl where where existsNode( existsNode( object_value, object_value, '/Invoice/MailAddressTo[Person="Marv '/Invoice/MailAddressTo[Person="Marv Albert"]') Albert"]') = = 1; 1; COUNT(*) COUNT(*) --------------0 0

www.skillbuilders.com

@existsNode select count(*) from invoicexml_tbl where existsNode( object_value, '/Invoice/MailAddressTo[Person="Joe Smith"]') = 1; COUNT(*) 1 select count(*) from invoicexml_tbl where existsNode( object_value, '/Invoice/MailAddressTo[Person="Marv Albert"]') = 1; COUNT(*) 0 select count(*) from invoicexml_tbl where existsNode( object_value, '/Invoice/MailAddressTo[@id="PA"]') = 1; COUNT(*) 1

V2.1

34

XMLSequence
Use to look at multiple nodes or a fragment Creates a virtual table of XMLType objects
select select value(people) value(people) XMLSequence returns an array, and the table from from invoicexml_tbl invoicexml_tbl i, i, function converts the array into a table table(XMLSequence( table(XMLSequence( extract(i.object_value, extract(i.object_value, './/Person'))) './/Person'))) people; people; VALUE(PEOPLE) VALUE(PEOPLE) <Person>Joe <Person>Joe Smith</Person> Smith</Person> <Person>Ed <Person>Ed Jones</Person> Jones</Person> 2 2 rows rows selected. selected.
@XMLSequence @XMLSequence
www.skillbuilders.com

Returns multiple rows

@XMLSequence -- Lets compare extractValue and extract with XMLSequence using the -- Person nodes. -- 1. extractValue: set feedback 1 select extractValue(object_value, './/Person') from invoicexml_tbl; Select extractValue(object_value, './/Person') from invoicexml_tbl * ERROR at line 1: ORA-19025: EXTRACTVALUE returns value of only one node -- 2. extract: select extract(object_value, './/Person') from invoicexml_tbl; EXTRACT(OBJECT_VALUE,'.//PERSON') <Person>Joe Smith</Person><Person>Ed Jones</Person> 1 row selected. ----Note that while extract does return all the Person nodes, it returns them as a single row. We can fortunately overcome this limitation by restructuring the query and using XMLSequence, as shown below:

-- 3. XMLSequence: select value(people) from invoicexml_tbl i, table(XMLSequence( extract(i.object_value, './/Person'))) people; VALUE(PEOPLE) <Person>Joe Smith</Person> <Person>Ed Jones</Person> 2 rows selected.

V2.1

35

FLOWR
Say flower FOR, LET, WHERE, ORDER BY, and RETURN FOR binds one or more variables in an iterative manner LET binds variables, can also be used to perform joins WHERE filters data ORDER BY sorts RETURN returns final result

www.skillbuilders.com

FLOWR acts like SQL for XML documents, but more complex.

V2.1

36

ora: Functions
XML DB provides 5 more XQuery functions
view,

contains, matches, replace, and sqrt

Implemented in http://xmlns.oracle.com/xdb namespace Use the prefix ora: (i.e. ora:view, etc.) ora:view particularly valuable
it

can be used to transform relational data into XML examples will be shown later

www.skillbuilders.com

V2.1

37

ora:contains Function
Structural search with a text predicate
@oracontains @oracontains

SELECT SELECT count(*) count(*) FROM FROM invoicexml_tbl invoicexml_tbl WHERE WHERE existsNode(object_value, existsNode(object_value, '/Invoice/MailAddressTo/Person '/Invoice/MailAddressTo/Person [ora:contains(text(), [ora:contains(text(), "Smith") "Smith") > > 0]', 0]', 'xmlns:ora="http://xmlns.oracle.com/xdb"') 'xmlns:ora="http://xmlns.oracle.com/xdb"') = = 1; 1; COUNT(*) COUNT(*) 1 1 SELECT SELECT count(*) count(*) FROM FROM invoicexml_tbl invoicexml_tbl WHERE existsNode(object_value, WHERE existsNode(object_value, '/Invoice/MailAddressTo/Person '/Invoice/MailAddressTo/Person [ora:contains(text(), [ora:contains(text(), "Einstein") "Einstein") > > 0]', 0]', 'xmlns:ora="http://xmlns.oracle.com/xdb"') 'xmlns:ora="http://xmlns.oracle.com/xdb"') = = 1; 1; COUNT(*) COUNT(*) 0 0
www.skillbuilders.com

The ora:contains function returns a positive number if the text is found (my tests indicate that is usually returns a 1). If the text is not found, then 0 is returned. ora:contains is generally used with existsNode, extract, or extractValue. You need to include the namespace mapping parameter xmlns:ora="http://xmlns.oracle.com/xdb" to identify the ora namespace prefix. @oracontains SELECT count(*) FROM invoicexml_tbl WHERE existsNode(object_value, '/Invoice/MailAddressTo/Person [ora:contains(text(), "Smith") > 0]', 'xmlns:ora="http://xmlns.oracle.com/xdb"') = 1; COUNT(*) 1 SELECT count(*) FROM invoicexml_tbl WHERE existsNode(object_value, '/Invoice/MailAddressTo/Person [ora:contains(text(), "Einstein") > 0]', 'xmlns:ora="http://xmlns.oracle.com/xdb"') = 1; COUNT(*) 0

V2.1

38

Other ora: Functions


ora:matches
Regular

expression search Like REGEXP_LIKE

ora:replace
Regular

expression replace Like REGEXP_REPLACE

ora:sqrt
Square

root

www.skillbuilders.com

V2.1

39

XML DB Repository
XML doc stored in XML DB Repository is called an XML DB Resource 2 important functions
fn:doc

XQuery function that can obtain a repository file containing XML data Returns a single XML document stored in XML DB repository Returns documents stored in same folder in the repository
www.skillbuilders.com

fn:collection

V2.1

40

Repository Example
@resources @resources

Create the orders and partys resources


DECLARE DECLARE res res BOOLEAN; BOOLEAN; ordersxmlstring ordersxmlstring VARCHAR2(500):= VARCHAR2(500):= '<?xml '<?xml version="1.0"?> version="1.0"?> <orders> <orders> <order <order orderno="15" orderno="15" partyno="1111" partyno="1111" itemname="Widget" itemname="Widget" amt="500 amt="500 <order <order orderno="25" orderno="25" partyno="1111" partyno="1111" itemname="Do itemname="Do dad" dad" amt="2000 amt="2000 <order <order orderno="35" orderno="35" partyno="2222" partyno="2222" itemname="All itemname="All purpose purpose item" item" <order <order orderno="45" orderno="45" partyno="3333" partyno="3333" itemname="The itemname="The best best thing" thing" a a </orders>'; </orders>'; (etcetera) (etcetera) Retrieve resources created

select select any_path, any_path, res res from resource_view from resource_view where where any_path any_path like like '%partys%'; '%partys%';
www.skillbuilders.com

@resources exec DBMS_XDB.DELETERESOURCE('/public/orders.xml') exec DBMS_XDB.DELETERESOURCE('/public/partys.xml') exec DBMS_XDB.DELETERESOURCE('/public/ordersnamespace.xml')


DECLARE res BOOLEAN; ordersxmlstring VARCHAR2(500):= '<?xml version="1.0"?> <orders> <order orderno="15" partyno="1111" itemname="Widget" amt="5000"/> <order orderno="25" partyno="1111" itemname="Do dad" amt="2000"/> <order orderno="35" partyno="2222" itemname="All purpose item" amt="7000"/> <order orderno="45" partyno="3333" itemname="The best thing" amt="15000"/> </orders>'; partysxmlstring VARCHAR2(500):= '<?xml version="1.0"?> <partys> <party partyno="1111" partyname="ABC Corp" partycity="Toronto"/> <party partyno="2222" partyname="Freds Inc" partycity="Chicago"/> <party partyno="3333" partyname="Gofaster Corp" partycity="Montreal"/> </partys>'; ordersxmlnsstring VARCHAR2(500):= '<?xml version="1.0"?> <orders xmlns="http://order.com"> <order orderno="15" partyno="1111" itemname="Widget" amt="5000"/> <order orderno="25" partyno="1111" itemname="Do dad" amt="2000"/> <order orderno="35" partyno="2222" itemname="All purpose item" amt="7000"/> <order orderno="45" partyno="3333" itemname="The best thing" amt="15000"/> </orders>'; BEGIN res := DBMS_XDB.createResource('/public/orders.xml', ordersxmlstring); res := DBMS_XDB.createResource('/public/partys.xml', partysxmlstring); res := DBMS_XDB.createResource('/public/ordersnamespace.xml', ordersxmlnsstring); END; /

select any_path, res from resource_view where any_path like '%public%';

V2.1

41

Repository Example
Display a single partys.xml document using fn:doc
More

on XMLQuery and FLOWR later

SELECT ("/public/partys.xml") SELECT XMLQuery('for XMLQuery('for $p $p in in fn:doc fn:doc ("/public/partys.xml") return return $p' $p' RETURNING RETURNING CONTENT)as CONTENT)as partys partys FROM FROM DUAL; DUAL;
PARTYS PARTYS --------------------------------------------------------------------------------------------------------------------------<partys><party <partys><party partyno="1111" partyno="1111" partyname="ABC partyname="ABC Corp" Corp" partycity="Toronto"/><party partycity="Toronto"/><party partyno="2222" partyno="2222" partyname="Freds partyname="Freds Inc" Inc" partycity="Chicago"/> <party partyno="3333" partyname="Gofaster partycity="Chicago"/> <party partyno="3333" partyname="Gofaster Corp" Corp" partycity="Montreal"/></partys> partycity="Montreal"/></partys>

@fndoc @fndoc
www.skillbuilders.com

@fndoc SELECT XMLQuery('for $p in fn:doc("/public/partys.xml") return $p' RETURNING CONTENT) as partys FROM DUAL; PARTYS --------------------------------------------------------------------<partys><party partyno="1111" partyname="ABC Corp" partycity="Toronto"/><party partyno="2222" partyname="Freds Inc" partycity="Chicago"/> <party partyno="3333" partyname="Gofaster Corp" partycity="Montreal"/></partys>

V2.1

42

fn:collection
Return all docs stored in the same folder in the repository
Returned

as one row, not pretty-printed


@fncollection @fncollection

SELECT SELECT XMLQuery('for XMLQuery('for $p $p in in fn:collection("/public") fn:collection("/public") return $p' return $p' RETURNING RETURNING CONTENT)as CONTENT)as collection_public collection_public FROM FROM DUAL; DUAL; COLLECTION_PUBLIC COLLECTION_PUBLIC --------------------------------------------------------------------------------------------------------------------------<orders> <orders> <order <order orderno="15" orderno="15" partyno="1111" partyno="1111" itemname="Widget" itemname="Widget" amt="5 amt="5 <order orderno="25" partyno="1111" itemname="Do <order orderno="25" partyno="1111" itemname="Do dad" dad" amt="2 amt="2 (etcetera) (etcetera)
www.skillbuilders.com

@fncollection SELECT XMLQuery('for $p in fn:collection("/public") return $p' RETURNING CONTENT) as collection_public FROM DUAL; COLLECTION_PUBLIC ---------------------------------------------------------------------<orders> <order orderno="15" partyno="1111" itemname="Widget" amt="5000"/> <order orderno="25" partyno="1111" itemname="Do dad" amt="2000"/> <order orderno="35" partyno="2222" itemname="All purpose item" amt="7000"/> <order orderno="45" partyno="3333" itemname="The best thing" amt="15000"/> </orders> <orders xmlns="http://order.com"> <order orderno="15" partyno="1111" <order orderno="25" partyno="1111" <order orderno="35" partyno="2222" <order orderno="45" partyno="3333" </orders> itemname="Widget" amt="5000"/> itemname="Do dad" amt="2000"/> itemname="All purpose item" amt="7000"/> itemname="The best thing" amt="15000"/>

<partys> <party partyno="1111" partyname="ABC Corp" partycity="Toronto"/> <party partyno="2222" partyname="Freds Inc" partycity="Chicago"/> <party partyno="3333" partyname="Gofaster Corp" partycity="Montreal"/> </partys> 1 row selected.

Note: Output from XMLQuery is returned as one row, and is not pretty-printed. Output above was reformatted to make it easier to read.

V2.1

43

XMLQuery() and XMLTable()


Functions introduced in Oracle 10g Release 2 Interface between SQL and XML Allow query, construct, and transform relational data like it is XML

And XML like it is relational

XMLQuery() can be used to generate XML document from XML or Relational data

Can query the XML result with XQuery Can the query the relational result with SQL
www.skillbuilders.com

XMLTable() to create relational views from XML data


V2.1

44

XMLQuery() and FLOWR


Query and join two XML documents:
partys.xml, orders.xml On partyno key

@XMLQuery1 @XMLQuery1

SELECT SELECT XMLQuery('for XMLQuery('for $p $p in in fn:doc("/public/partys.xml")/partys/party/@partyno fn:doc("/public/partys.xml")/partys/party/@partyno let let $o $o := := fn:doc("/public/orders.xml")/orders/order[@partyno fn:doc("/public/orders.xml")/orders/order[@partyno = = $p] $p] where where fn:count($o) fn:count($o) > >1 1 order order by by fn:avg($o/@amt) fn:avg($o/@amt) descending descending return return <big-party><partyno>{$p}</partyno> <big-party><partyno>{$p}</partyno> <ordercount>{fn:count($o)}</ordercount> <ordercount>{fn:count($o)}</ordercount> <avgamt>{xs:integer(fn:avg($o/@amt))}</avgamt> <avgamt>{xs:integer(fn:avg($o/@amt))}</avgamt> </big-party>' </big-party>' RETURNING RETURNING CONTENT) CONTENT) as as ORDERS ORDERS FROM FROM DUAL; DUAL; ORDERS ORDERS <big-party><partyno>1111</partyno><ordercount>2</ordercount> <big-party><partyno>1111</partyno><ordercount>2</ordercount> <avgamt>3500</avgamt></big-party> <avgamt>3500</avgamt></big-party>
www.skillbuilders.com

Take a look at the partys.xml and orders.xml documents shown earlier. Each party is identified by a partyno. There can be multiple orders for each party. This query above locates all parties with more than one order, and generates an XML document with a big-party element containing the partyno, the number of orders, and the average order amount. @XMLQuery1 SELECT XMLQuery('for $p in fn:doc("/public/partys.xml")/partys/party/@partyno let $o := fn:doc("/public/orders.xml")/orders/order[@partyno = $p] where fn:count($o) > 1 order by fn:avg($o/@amt) descending return <big-party><partyno>{$p}</partyno> <ordercount>{fn:count($o)}</ordercount> <avgamt>{xs:integer(fn:avg($o/@amt))}</avgamt> </big-party>' RETURNING CONTENT) as ORDERS FROM DUAL; ORDERS ------------------------------------------------<big-party><partyno>1111</partyno><ordercount>2 </ordercount><avgamt>3500</avgamt></big-party>

V2.1

45

XMLQuery() on Relational Data


First, user ora:view to view relational tables as XML @XMLQuery2 @XMLQuery2
SELECT SELECT XMLQuery( XMLQuery( 'for 'for $dep $dep in in ora:view("DEPARTMENT") ora:view("DEPARTMENT") return return $dep' $dep' RETURNING RETURNING CONTENT) CONTENT) AS AS ORAVIEW ORAVIEW FROM dual; FROM dual; ORAVIEW ORAVIEW --------------------------------------------------------------------------------------------------------------------------------<ROW><DEPT_NO>1</DEPT_NO><DEPT_NAME>President</DEPT_NAME></ROW><ROW> <ROW><DEPT_NO>1</DEPT_NO><DEPT_NAME>President</DEPT_NAME></ROW><ROW> <DEPT_NO>2</DEPT_NO><DEPT_NAME>Scientist</DEPT_NAME></ROW> <DEPT_NO>2</DEPT_NO><DEPT_NAME>Scientist</DEPT_NAME></ROW>

--- Convert Convert a a relational relational table table to to XML XML output: output:

www.skillbuilders.com

-- First. convert a relational table to XML output: -- Set up test tables. @XMLQuery2 drop table department cascade constraints; create table department (dept_no number, dept_name varchar2(20)); insert into department values (1, 'President'); insert into department values (2, 'Scientist'); drop table employee cascade constraints; create table employee (emp_no number, firstname varchar2(20), lastname varchar2(20), dept_no number); insert into employee values (1, 'Albert', 'Einstein', 2); insert into employee values (2, 'Neils', 'Bohr', 2); insert into employee values (3, 'Marie', 'Curie', 2); insert into employee values (4, 'Franklin', 'Roosevelt', 1); -- Convert a relational table to XML output: SELECT XMLQuery( 'for $dep in ora:view("DEPARTMENT") return $dep' RETURNING CONTENT) AS ORAVIEW FROM dual; ORAVIEW -------------------------------------------------------------------<ROW><DEPT_NO>1</DEPT_NO><DEPT_NAME>President</DEPT_NAME></ROW><ROW> <DEPT_NO>2</DEPT_NO><DEPT_NAME>Scientist</DEPT_NAME></ROW> (etcetera)

V2.1

46

XMLQuery() on Relational Data


Second, use XQuery expressions to generate XML from Relational Data

Join relational tables

@XMLQuery3 @XMLQuery3

SELECT SELECT XMLQuery( XMLQuery( '<Departments> '<Departments> {for {for $dep $dep in in ora:view("DEPARTMENT") ora:view("DEPARTMENT") let let $dept_no $dept_no := := $dep/ROW/DEPT_NO/text(), $dep/ROW/DEPT_NO/text(), $dept_name $dept_name := := $dep/ROW/DEPT_NAME/text() $dep/ROW/DEPT_NAME/text() return return <Department <Department id="{$dept_no}"> id="{$dept_no}"> <DepartmentName>{$dept_name}</DepartmentName> <DepartmentName>{$dept_name}</DepartmentName> <Employees> <Employees> {for {for $emp $emp in in ora:view("EMPLOYEE") ora:view("EMPLOYEE") let let $emp_no $emp_no := := $emp/ROW/EMP_NO/text(), $emp/ROW/EMP_NO/text(), $emp_dept_no $emp_dept_no := := $emp/ROW/DEPT_NO/text(), $emp/ROW/DEPT_NO/text(), $lastname := $lastname := $emp/ROW/LASTNAME/text(), $emp/ROW/LASTNAME/text(), $firstname := $emp/ROW/FIRSTNAME/text() $firstname := $emp/ROW/FIRSTNAME/text() where return where $emp_dept_no $emp_dept_no == $dept_no $dept_no return <Employee <Employee id="{$emp_no}"> id="{$emp_no}"> <FirstName>{$firstname}</FirstName> <FirstName>{$firstname}</FirstName> <LastName>{$lastname}</LastName> <LastName>{$lastname}</LastName> </Employee> </Employee> }} </Employees> </Employees> </Department> </Department> }} </Departments>' </Departments>' RETURNING RETURNING CONTENT) CONTENT) AS AS XML_FROM_JOIN XML_FROM_JOIN FROM FROM dual; dual; www.skillbuilders.com

-- Second, user XQuery expressions to generate XML from Relational data: @XMLQuery3 SELECT XMLQuery( '<Departments> {for $dep in ora:view("DEPARTMENT") let $dept_no := $dep/ROW/DEPT_NO/text(), $dept_name := $dep/ROW/DEPT_NAME/text() return <Department id="{$dept_no}"> <DepartmentName>{$dept_name}</DepartmentName> <Employees> {for $emp in ora:view("EMPLOYEE") let $emp_no := $emp/ROW/EMP_NO/text(), $emp_dept_no := $emp/ROW/DEPT_NO/text(), $lastname := $emp/ROW/LASTNAME/text(), $firstname := $emp/ROW/FIRSTNAME/text() where $emp_dept_no = $dept_no return <Employee id="{$emp_no}"> <FirstName>{$firstname}</FirstName> <LastName>{$lastname}</LastName> </Employee> } </Employees> </Department> } </Departments>' RETURNING CONTENT) AS XML_FROM_JOIN FROM dual; XML_FROM_JOIN <Departments><Department id="1"><DepartmentName>President</DepartmentName><Emplo yees><Employee id="4"><FirstName>Franklin</FirstName><LastName>Roosevelt</LastNa me></Employee></Employees></Department><Department id="2"><DepartmentName>Scient ist</DepartmentName><Employees><Employee id="1"><FirstName>Albert</FirstName><La stName>Einstein</LastName></Employee><Employee id="2"><FirstName>Neils</FirstNam e><LastName>Bohr</LastName></Employee><Employee id="3"><FirstName>Marie</FirstNa me><LastName>Curie</LastName></Employee></Employees></Department></Departments>

V2.1

47

XMLTable() Function
Enables an XML value to be interpreted as a table or a set Used to return table and columns from XQuery expressions Rather than returning a sequence as XQuery would normally do XMLTable() function used in a SQL From clause
www.skillbuilders.com

V2.1

XMLTable() Function

48

Use XMLTable function to treat an XMLType column as a relational table @XMLTable1 @XMLTable1 Use SQL to query the resulting relational table
SELECT SELECT inv_id, inv_id, a.PersonName, a.PersonName, a.StreetName, a.StreetName, a.CityName, a.CityName, a.State, a.State, a.Zipcode a.Zipcode FROM FROM invoicexml_col, invoicexml_col, XMLTABLE('/Invoice' XMLTABLE('/Invoice' PASSING PASSING invoicexml_col.inv_doc invoicexml_col.inv_doc COLUMNS COLUMNS PersonName PersonName varchar2(10) varchar2(10) PATH PATH '/Invoice/MailAddressTo/Person', '/Invoice/MailAddressTo/Person', StreetName StreetName varchar2(20) varchar2(20) PATH PATH '/Invoice/MailAddressTo/Street', '/Invoice/MailAddressTo/Street', CityName CityName varchar2(10) varchar2(10) PATH PATH '/Invoice/MailAddressTo/City', '/Invoice/MailAddressTo/City', State State varchar2(5) varchar2(5) PATH PATH '/Invoice/MailAddressTo/State', '/Invoice/MailAddressTo/State', Zipcode Zipcode varchar2(7) varchar2(7) PATH PATH '/Invoice/MailAddressTo/Zipcode' '/Invoice/MailAddressTo/Zipcode' ) )a a WHERE WHERE a.CityName a.CityName like like 'New%'; 'New%'; INV_ID INV_ID PERSONNAME PERSONNAME ----------- ------------------1 1 Joe Joe Smith Smith STREETNAME CITYNAME STREETNAME CITYNAME STATE STATE ------------------------------------- --------10 10 Apple Apple Tree Tree Lane Lane New New York York NY NY
www.skillbuilders.com

ZIPCODE ZIPCODE --------------12345 12345

Previously we used ora:view to treat relational data as XML data. were able to use XQuery on the resulting XML data. Here we use XMLTable to treat XML data as relational data. able to use SQL on the resulting relational data. See next page for full code example.

We then

We then are

V2.1

XMLTable() Function code:


@XMLTable1 desc invoicexml_col Name Null? ----------------------- -------INV_ID NOT NULL INV_DOC

Type ---------------NUMBER XMLTYPE

column inv_doc format a60 select * from invoicexml_col; INV_ID INV_DOC ---------- -------------------------------------------------1 <Invoice> <MailAddressTo id="PA"> <Person>Joe Smith</Person> <Street>10 Apple Tree Lane</Street> <City>New York</City> <State>NY</State> <Zipcode>12345</Zipcode> </MailAddressTo> <MailAddressFrom id="PA"> <Person>Ed Jones</Person> <Street>11 Cherry Lane</Street> <City>Newark</City> <State>NJ</State> <Zipcode>67890</Zipcode> </MailAddressFrom> <Details id="2006Sept1to30PA"> <FromTo>Sept 1, 2006 to Sept 30, 2006</FromTo> <Hours>70</Hours> <Rate>30</Rate> <Taxes>210</Taxes> <TotalDue>2310</TotalDue> <InvDate>Oct 1, 2006</InvDate> <Contractor>Ed Jones</Contractor> </Details> </Invoice> clear columns set linesize 150 SELECT inv_id, a.PersonName, a.StreetName, a.CityName, a.State, a.Zipcode FROM invoicexml_col, XMLTABLE('/Invoice' PASSING invoicexml_col.inv_doc COLUMNS PersonName varchar2(10) PATH '/Invoice/MailAddressTo/Person', StreetName varchar2(20) PATH '/Invoice/MailAddressTo/Street', CityName varchar2(10) PATH '/Invoice/MailAddressTo/City', State varchar2(5) PATH '/Invoice/MailAddressTo/State', Zipcode varchar2(7) PATH '/Invoice/MailAddressTo/Zipcode' ) a WHERE a.CityName like 'New%'; INV_ID PERSONNAME STREETNAME CITYNAME STATE ZIPCODE ---------- ---------- -------------------- ---------- ----- ------1 Joe Smith 10 Apple Tree Lane New York NY 12345

V2.1

50

XML Performance
Create indexes to help improve the performance
Improve

performance of XPath functions like weve tuned SQL in the past

Use function-based indexes on structured and unstructured XMLType's Consider binary indexes for functions like existsNode
Return

flags of 0 or 1
www.skillbuilders.com

V2.1

Tuning Example
@tuning1 @tuning1
set set autotrace autotrace on on SELECT SELECT XMLQuery( XMLQuery( '<Departments> '<Departments> {for {for $dep $dep in in ora:view("DEPARTMENT") ora:view("DEPARTMENT") let let $dept_no $dept_no := := $dep/ROW/DEPT_NO/text(), $dep/ROW/DEPT_NO/text(), $dept_name $dept_name := := $dep/ROW/DEPT_NAME/text() $dep/ROW/DEPT_NAME/text() where where $dep/ROW/DEPT_NO/text() $dep/ROW/DEPT_NO/text() == 33 return return (etcetera) (etcetera)

51

Index not used

Execution Execution Plan Plan ------------------------------------------------------------------------------------------------------------------Plan Plan hash hash value: value: 3895618712 3895618712
----------------------------------------------------------------------------------------------------------------------------------------------------------------|| Id || Name || Rows || Id || Operation Operation Name Rows || Bytes Bytes || Cost Cost (%CPU)| (%CPU)| Time Time ----------------------------------------------------------------------------------------------------------------------------------------------------------------|| 00 || SELECT STATEMENT | | 1 | | 2 (0)| 00:00:01 SELECT STATEMENT | | 1 | | 2 (0)| 00:00:01 || || 11 || SORT || || 11 || 50 || || SORT AGGREGATE AGGREGATE 50 || |* |* 22 || TABLE TABLE ACCESS ACCESS FULL| FULL| EMPLOYEE EMPLOYEE || 5270 5270 || 257K| 257K| 3648 3648 (2)| (2)| 00:00:44 00:00:44 || || 33 || SORT || || 11 || 25 || || SORT AGGREGATE AGGREGATE 25 || |* 11 || 25 33 (0)| |* 44 || TABLE TABLE ACCESS ACCESS FULL| FULL| DEPARTMENT DEPARTMENT || 25 || (0)| 00:00:01 00:00:01 || || 55 || FAST || || 11 || || 22 (0)| FAST DUAL DUAL (0)| 00:00:01 00:00:01 || ----------------------------------------------------------------------------------------------------------------------------------------------------------------www.skillbuilders.com

@tuning1 drop table department cascade constraints; create table department (dept_no number, dept_name varchar2(20)); insert into department values (1, 'President'); insert into department values (2, 'Scientist'); insert into department values (3, 'Wannabepresident'); drop table employee cascade constraints; create table employee (emp_no number, firstname varchar2(20), lastname varchar2(20), dept_no number, big_char_column char(200)); insert into employee values (1, 'Albert', 'Einstein', 2, 'junk'); insert into employee values (2, 'Neils', 'Bohr', 2, 'junk'); insert into employee values (3, 'Marie', 'Curie', 2, 'junk'); insert into employee values (4, 'Franklin', 'junk', 1, 'Einstein'); insert into EMPLOYEE select * from employee; / / / / / / / / / / / / / / / / insert insert insert insert into into into into employee employee employee employee values values values values (5, (6, (7, (8, 'Hillary', 'Clinton', 3, 'junk'); 'Barack', 'Obama', 3, 'junk'); 'John', 'McCain', 3, 'junk'); 'Rudy', 'Guiliani', 3, 'junk'); -- continued on next page

V2.1

exec dbms_stats.gather_table_stats(user, 'DEPARTMENT') exec dbms_stats.gather_table_stats(user, 'EMPLOYEE') select count(*) from DEPARTMENT; COUNT(*) ---------3 select count(*) from EMPLOYEE; COUNT(*) ---------524292 set linesize 150 set autotrace on SELECT XMLQuery( '<Departments> {for $dep in ora:view("DEPARTMENT") let $dept_no := $dep/ROW/DEPT_NO/text(), $dept_name := $dep/ROW/DEPT_NAME/text() where $dep/ROW/DEPT_NO/text() = 3 return <Department id="{$dept_no}"> <DepartmentName>{$dept_name}</DepartmentName> <Employees> {for $emp in ora:view("EMPLOYEE") let $emp_no := $emp/ROW/EMP_NO/text(), $emp_dept_no := $emp/ROW/DEPT_NO/text(), $lastname := $emp/ROW/LASTNAME/text(), $firstname := $emp/ROW/FIRSTNAME/text() where $emp_dept_no = $dept_no return <Employee id="{$emp_no}"> <FirstName>{$firstname}</FirstName> <LastName>{$lastname}</LastName> </Employee> } </Employees> </Department> } </Departments>' RETURNING CONTENT) AS XML_FROM_JOIN FROM dual; set autotrace off XML_FROM_JOIN ============= <Departments><Department id="3"><DepartmentName>Wannabepresi dent</DepartmentName><Employees><Employee id="5"><FirstName> Hillary</FirstName><LastName>Clinton</LastName></Employee><E mployee id="6"><FirstName>Barack</FirstName><LastName>Obama< /LastName></Employee><Employee id="7"><FirstName>John</First Name><LastName>McCain</LastName></Employee><Employee id="8"> <FirstName>Rudy</FirstName><LastName>Guiliani</LastName></Em ployee></Employees></Department></Departments> Execution Plan ---------------------------------------------------------Plan hash value: 3895618712 --------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | | 2 (0)| 00:00:01 | | 1 | SORT AGGREGATE | | 1 | 50 | | | |* 2 | TABLE ACCESS FULL| EMPLOYEE | 5270 | 257K| 3648 (2)| 00:00:44 | | 3 | SORT AGGREGATE | | 1 | 25 | | | |* 4 | TABLE ACCESS FULL| DEPARTMENT | 1 | 25 | 3 (0)| 00:00:01 | | 5 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | --------------------------------------------------------------------------------Predicate Information (identified by operation id): --------------------------------------------------2 - filter("DEPT_NO"=:B1) 4 - filter("DEPT_NO"=3) Note ----- dynamic sampling used for this statement

Index not used

V2.1

Tuning Example
Create indexes to improve performance Run query with filter on DEPARTMENT table Create indexes

53

@tuning2 @tuning2

create create index index emp_idx1 emp_idx1 on on employee employee (dept_no); (dept_no); create index dept_idx1 on create index dept_idx1 on department department (dept_no); (dept_no); exec exec dbms_stats.gather_table_stats(user, dbms_stats.gather_table_stats(user, 'DEPARTMENT') 'DEPARTMENT') exec exec dbms_stats.gather_table_stats(user, dbms_stats.gather_table_stats(user, 'EMPLOYEE') 'EMPLOYEE')
Execution Plan Execution Plan ------------------------------------------------------------------------------------------------------------------Plan hash value: 3532437522 Plan hash value: 3532437522 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | | 2 (0)| 00:00:01 | | 0 | SELECT STATEMENT | | 1 | | 2 (0)| 00:00:01 | | 1 | SORT AGGREGATE | | 1 | 21 | | | | 1 | SORT AGGREGATE | | 1 | 21 | | | |* 2 | TABLE ACCESS FULL | EMPLOYEE | 262K| 5374K| 3648 (2)| 00:00:44 | |* 2 | TABLE ACCESS FULL | EMPLOYEE | 262K| 5374K| 3648 (2)| 00:00:44 | | 3 | SORT AGGREGATE | | 1 | 15 | | | | 3 | SORT AGGREGATE | | 1 | 15 | | | | 4 | TABLE ACCESS BY INDEX ROWID| DEPARTMENT | 1 | 15 | 2 (0)| 00:00:01 | | 4 | TABLE ACCESS BY INDEX ROWID| DEPARTMENT | 1 | 15 | 2 (0)| 00:00:01 | |* 5 | INDEX RANGE SCAN | DEPT_IDX1 | 1 | | 1 (0)| 00:00:01 | |* 5 | INDEX RANGE SCAN | DEPT_IDX1 | 1 | | 1 (0)| 00:00:01 | | 6 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | | 6 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Predicate Information (identified by operation id): Predicate Information (identified by operation id): ----------------------------------------------------------------------------------------------------2 - filter("DEPT_NO"=:B1) 2 - filter("DEPT_NO"=:B1) 5 - access("DEPT NO"=3) 5 ("DEPT NO" 3)

Retest query, index used, but minimal improvement because only 3 rows in DEPARTMENT table

www.skillbuilders.com

Note that the index on the DEPARTMENT table is used. However, performance is not significantly improved since there are only 3 rows in the DEPARTMENT table.
@tuning2 -- Create index and compute statistics. drop index emp_idx1; drop index dept_idx1; create index emp_idx1 on employee (dept_no); create index dept_idx1 on department (dept_no); exec dbms_stats.gather_table_stats(user, 'DEPARTMENT') exec dbms_stats.gather_table_stats(user, 'EMPLOYEE') -- Rerun query with filter on DEPARTMENT table. set autotrace on SELECT XMLQuery( '<Departments> {for $dep in ora:view("DEPARTMENT") let $dept_no := $dep/ROW/DEPT_NO/text(), $dept_name := $dep/ROW/DEPT_NAME/text() where $dep/ROW/DEPT_NO/text() = 3 return <Department id="{$dept_no}"> <DepartmentName>{$dept_name}</DepartmentName> <Employees> {for $emp in ora:view("EMPLOYEE") let $emp_no := $emp/ROW/EMP_NO/text(), $emp_dept_no := $emp/ROW/DEPT_NO/text(), $lastname := $emp/ROW/LASTNAME/text(), $firstname := $emp/ROW/FIRSTNAME/text() where $emp_dept_no = $dept_no return <Employee id="{$emp_no}"> <FirstName>{$firstname}</FirstName> <LastName>{$lastname}</LastName> </Employee> } </Employees> </Department> } </Departments>' RETURNING CONTENT) AS XML_FROM_JOIN FROM dual; set autotrace off -- Code example continued on next page . . .

V2.1

XML_FROM_JOIN <Departments><Department id="3"><DepartmentName>Wannabepresi dent</DepartmentName><Employees><Employee id="5"><FirstName> Hillary</FirstName><LastName>Clinton</LastName></Employee><E mployee id="6"><FirstName>Barack</FirstName><LastName>Obama< /LastName></Employee><Employee id="7"><FirstName>John</First Name><LastName>McCain</LastName></Employee><Employee id="8"> <FirstName>Rudy</FirstName><LastName>Guiliani</LastName></Em ployee></Employees></Department></Departments> Execution Plan ---------------------------------------------------------Plan hash value: 3532437522 --------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | | 2 (0)| 00:00:01 | | 1 | SORT AGGREGATE | | 1 | 21 | | | |* 2 | TABLE ACCESS FUL | EMPLOYEE | 262K| 5374K| 3648 (2)| 00:00:44 | | 3 | SORT AGGREGATE | | 1 | 15 | | | | 4 | TBL ACCESS BY INDEX ROWID| DEPARTMENT | 1 | 15 | 2 (0)| 00:00:01 | |* 5 | INDEX RANGE SCAN | DEPT_IDX1 | 1 | | 1 (0)| 00:00:01 | | 6 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | --------------------------------------------------------------------------------------Predicate Information (identified by operation id): --------------------------------------------------2 - filter("DEPT_NO"=:B1) 5 - access("DEPT_NO"=3)

Retest query, index used, but minimal improvement because only 3 rows in DEPARTMENT table

www.skillbuilders.com

V2.1

55

Tuning Example
Rerun query with filter on EMPLOYEE table:

@tuning3 @tuning3

Execution Plan Execution Plan ------------------------------------------------------------------------------------------------------------------Plan hash value: 3184291738 Plan hash value: 3184291738 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | | 2 (0)| 00:00:01 | | 0 | SELECT STATEMENT | | 1 | | 2 (0)| 00:00:01 | | 1 | SORT AGGREGATE | | 1 | 21 | | | | 1 | SORT AGGREGATE | | 1 | 21 | | | | 2 | TABLE ACCESS BY INDEX ROWID| EMPLOYEE | 1 | 21 | 4 (0)| 00:00:01 | | 2 | TABLE ACCESS BY INDEX ROWID| EMPLOYEE | 1 | 21 | 4 (0)| 00:00:01 | |* 3 | INDEX RANGE SCAN | EMP_IDX1 | 1 | | 3 (0)| 00:00:01 | |* 3 | INDEX RANGE SCAN | EMP_IDX1 | 1 | | 3 (0)| 00:00:01 | | 4 | SORT AGGREGATE | | 1 | 15 | | | | 4 | SORT AGGREGATE | | 1 | 15 | | | | 5 | TABLE ACCESS FULL | DEPARTMENT | 3 | 45 | 3 (0)| 00:00:01 | | 5 | TABLE ACCESS FULL | DEPARTMENT | 3 | 45 | 3 (0)| 00:00:01 | | 6 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | | 6 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Predicate Information (identified by operation id): Predicate Information (identified by operation id): ----------------------------------------------------------------------------------------------------3 - access("DEPT_NO"=3) 3 - access("DEPT_NO"=3) filter("DEPT_NO"=:B1) filter("DEPT_NO"=:B1)

Retest query, index used, with significant improvement in performance

www.skillbuilders.com

Note that the index on the EMPLOYEE table is used. Performance is significantly improved.
@tuning3

-- Rerun query with filter on EMPLOYEE table. set autotrace on SELECT XMLQuery( '<Departments> {for $dep in ora:view("DEPARTMENT") let $dept_no := $dep/ROW/DEPT_NO/text(), $dept_name := $dep/ROW/DEPT_NAME/text() return <Department id="{$dept_no}"> <DepartmentName>{$dept_name}</DepartmentName> <Employees> {for $emp in ora:view("EMPLOYEE") let $emp_no := $emp/ROW/EMP_NO/text(), $emp_dept_no := $emp/ROW/DEPT_NO/text(), $lastname := $emp/ROW/LASTNAME/text(), $firstname := $emp/ROW/FIRSTNAME/text() where ($emp_dept_no = $dept_no) and($emp_dept_no = 3) return <Employee id="{$emp_no}"> <FirstName>{$firstname}</FirstName> <LastName>{$lastname}</LastName> </Employee> } </Employees> </Department> } </Departments>' RETURNING CONTENT) AS XML_FROM_JOIN FROM dual / set autotrace off
-- Code example continued on next page . . .

V2.1

XML_FROM_JOIN -----------------------------------------------------------<Departments><Department id="1"><DepartmentName>President</D epartmentName><Employees></Employees></Department><Departmen t id="2"><DepartmentName>Scientist</DepartmentName><Employee s></Employees></Department><Department id="3"><DepartmentNam e>Wannabepresident</DepartmentName><Employees><Employee id=" 5"><FirstName>Hillary</FirstName><LastName>Clinton</LastName ></Employee><Employee id="6"><FirstName>Barack</FirstName><L astName>Obama</LastName></Employee><Employee id="7"><FirstNa me>John</FirstName><LastName>McCain</LastName></Employee><Em ployee id="8"><FirstName>Rudy</FirstName><LastName>Guiliani< /LastName></Employee></Employees></Department></Departments> Execution Plan ---------------------------------------------------------Plan hash value: 3184291738

Retest query, index used, with significant improvement in performance

--------------------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | | 2 (0)| 00:00:01 | | 1 | SORT AGGREGATE | | 1 | 21 | | | | 2 | TBL ACCESS BY INDEX ROWID| EMPLOYEE | 1 | 21 | 4 (0)| 00:00:01 | |* 3 | INDEX RANGE SCAN | EMP_IDX1 | 1 | | 3 (0)| 00:00:01 | | 4 | SORT AGGREGATE | | 1 | 15 | | | | 5 | TABLE ACCESS FULL | DEPARTMENT | 3 | 45 | 3 (0)| 00:00:01 | | 6 | FAST DUAL | | 1 | | 2 (0)| 00:00:01 | --------------------------------------------------------------------------------------Predicate Information (identified by operation id): --------------------------------------------------3 - access("DEPT_NO"=3) filter("DEPT_NO"=:B1)

Note that the EMPLOYEE index is used, the results show all departments, but only employees in department 3! This is similar to an Outer Join on the DEPARTMENT table. And the costs are significantly reduced since a full table scan of the EMPLOYEE table is not required. Our tuning of this XQuery expression using ora:view is very similar to our tuning of standard, nonXML SQL. XQuery will automatically utilize indexes as warranted.

www.skillbuilders.com

V2.1

57

Optimizing structured XMLType data


Indexes can be used with both structured and unstructured data Structured data access is generally more efficient than unstructured access. Optimize both with function-based indexes when using extractValue and similar functions

@tuning4 @tuning4

create create index index invoicexml_tbl_idx1 invoicexml_tbl_idx1 on on invoicexml_tbl(extractValue(object_value, invoicexml_tbl(extractValue(object_value, '/Invoice/MailAddressTo/Person')); '/Invoice/MailAddressTo/Person')); select select extract(object_value, extract(object_value, '/Invoice/MailAddressTo')from '/Invoice/MailAddressTo')from invoicexml_tbl invoicexml_tbl where where extractValue(object_value, extractValue(object_value, '/Invoice/MailAddressTo/Person')= '/Invoice/MailAddressTo/Person')= 'Joe 'Joe Smith'; Smith'; Execution Execution Plan Plan ------------------------------------------------------------------------------------------------------------------Plan hash value: Plan hash value: 4044500576 4044500576 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|| Id | Operation || Name |Rows| || Id | Operation Name |Rows| Bytes|Cost Bytes|Cost (%CPU)| (%CPU)| Time Time ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|| 00 || SELECT || || 11 || 87 22 (0)| SELECT STATEMENT STATEMENT 87 || (0)| 00:00:01 00:00:01 || || 11 || TBL || 11 || 87 22 (0)| TBL ACCESS ACCESS BY BY IDX IDX ROWID| ROWID| INVOICEXML_TBL INVOICEXML_TBL 87 || (0)| 00:00:01 00:00:01 || |* || INVOICEXML_TBL_IDX1 || 11 (0)| |* 22 || INDEX INDEX RANGE RANGE SCAN SCAN INVOICEXML_TBL_IDX1 || 11 || (0)| 00:00:01 00:00:01 || ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Predicate Information (identified by operation id): Predicate Information (identified by operation id): ----------------------------------------------------------------------------------------------------22 -- access(EXTRACTVALUE(SYS_MAKEXML("XMLDATA"),'/Invoice/MailAddressTo/Person')='Joe access(EXTRACTVALUE(SYS_MAKEXML("XMLDATA"),'/Invoice/MailAddressTo/Person')='Joe Smith') Smith') www.skillbuilders.com

-- First run query with no index. desc invoicexml_tbl select * from invoicexml_tbl; drop index invoicexml_tbl_idx1; exec dbms_stats.gather_table_stats(user, 'invoicexml_tbl') set autotrace on select extract(object_value, '/Invoice/MailAddressTo')from invoicexml_tblwhere extractValue(object_value, '/Invoice/MailAddressTo/Person')= 'Joe Smith'; set autotrace off EXTRACT(OBJECT_VALUE,'/INVOICE/MAILADDRESSTO') -----------------------------------------------------------<MailAddressTo id="PA"><Person>Joe Smith</Person><Street>10Apple Tree Lane</Street><City>New York</City><State>NY</State><Zipcode>12345</Zipcode></MailAddressTo> Execution Plan ---------------------------------------------------------Plan hash value: 2364304528 --------------------------------------------------------------------------| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time | --------------------------------------------------------------------------| 0 |SELECT STMT | | 1 | 87 | 4 (0)| 00:00:01 | |* 1 |TBL ACC FULL|INVOICEXML_TBL| 1 | 87 | 4 (0)| 00:00:01 | --------------------------------------------------------------------------Predicate Information (identified by operation id): --------------------------------------------------1 - filter(EXTRACTVALUE(SYS_MAKEXML("INVOICEXML_TBL"."XMLDATA"),'/Invoice /MailAddressTo/Person')='Joe Smith') -- Code example continued on next page . . .

V2.1

-- Now create a function-based index and rerun query. create index invoicexml_tbl_idx1 on invoicexml_tbl (extractValue(object_value, '/Invoice/MailAddressTo/Person')); exec dbms_stats.gather_table_stats(user, 'invoicexml_tbl') set autotrace on select extract(object_value, '/Invoice/MailAddressTo') from invoicexml_tbl where extractValue(object_value, '/Invoice/MailAddressTo/Person')= 'Joe Smith'; set autotrace off EXTRACT(OBJECT_VALUE,'/INVOICE/MAILADDRESSTO') -----------------------------------------------------------<MailAddressTo id="PA"><Person>Joe Smith</Person><Street>10 Apple Tree Lane</Street><City>New York</City><State>NY</Stat e><Zipcode>12345</Zipcode></MailAddressTo> Execution Plan ---------------------------------------------------------Plan hash value: 4044500576 ---------------------------------------------------------------------------------------| Id | Operation | Name |Rows| Bytes|Cost (%CPU)| Time | ---------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 87 | 2 (0)| 00:00:01 | | 1 | TBL ACCESS BY IDX ROWID| INVOICEXML_TBL | 1 | 87 | 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | INVOICEXML_TBL_IDX1 | 1 | | 1 (0)| 00:00:01 | ---------------------------------------------------------------------------------------Predicate Information (identified by operation id): --------------------------------------------------2 - access(EXTRACTVALUE(SYS_MAKEXML("XMLDATA"),'/Invoice/MailAddressTo/Person')='Joe Smith')

Indexes can be used effectively with structured and unstructured data. Structured data access is generally more efficient than unstructured access.

www.skillbuilders.com

V2.1

59

Optimizing UNstructured XMLType data


Again, optimize with function-based indexes
@tuning5 @tuning5
create create index index invtest_unstruct_idx1 invtest_unstruct_idx1 on on invtest_unstruct(extractValue(object_value, invtest_unstruct(extractValue(object_value, '/Invoice/MailAddressTo/Person')); '/Invoice/MailAddressTo/Person')); select select extract(object_value, extract(object_value, '/Invoice/MailAddressTo')from '/Invoice/MailAddressTo')from invtest_unstruct invtest_unstruct where where extractValue(object_value, extractValue(object_value, '/Invoice/MailAddressTo/Person')='Joe '/Invoice/MailAddressTo/Person')='Joe Smith'; Smith'; EXTRACT(OBJECT_VALUE,'/INVOICE/MAILADDRESSTO') EXTRACT(OBJECT_VALUE,'/INVOICE/MAILADDRESSTO') ----------------------------------------------------------------------------------------------------------------------<MailAddressTo <MailAddressTo id="PA"><Person>Joe id="PA"><Person>Joe Smith</Person><Street>10Apple Smith</Person><Street>10Apple Tree Tree Lane</Street><City>New Lane</Street><City>New York</City><State>NY</State><Zipcode>12345</Zipcode></MailAddressTo> York</City><State>NY</State><Zipcode>12345</Zipcode></MailAddressTo> Execution Plan Execution Plan ------------------------------------------------------------------------------------------------------------------Plan Plan hash hash value: value: 2915445422 2915445422 --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|| Id || Name |Rows|Bytes| || Id || Operation Operation Name |Rows|Bytes| Cost(%CPU)| Cost(%CPU)| Time Time --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|| 00 || SELECT STATEMENT | | 1 | 2002| 2 (0)| 00:00:01 SELECT STATEMENT | | 1 | 2002| 2 (0)| 00:00:01 || || 11 || TBL || 11 || 2002| 22 (0)| TBL ACCESS ACCESS BY BY IDX IDX ROWID| ROWID| INVTEST_UNSTRUCT INVTEST_UNSTRUCT 2002| (0)| 00:00:01 00:00:01 || |* || INVTEST_UNSTRUCT_IDX1| || 11 (0)| |* 22 || INDEX INDEX RANGE RANGE SCAN SCAN INVTEST_UNSTRUCT_IDX1| 11 || (0)| 00:00:01 00:00:01 || --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Predicate Predicate Information Information (identified (identified by by operation operation id): id): ----------------------------------------------------------------------------------------------------22 -- access(EXTRACTVALUE(SYS_MAKEXML("XMLDATA"),'/Invoice/MailAddressTo/Person')='Joe access(EXTRACTVALUE(SYS_MAKEXML("XMLDATA"),'/Invoice/MailAddressTo/Person')='Joe Smith') Smith') www.skillbuilders.com

-- First run query with no index.


create table invtest_unstruct of XMLtype XMLType store as CLOB; Insert into invtest_unstruct values ( XMLType(bfilename('XMLDIR', 'invoicexml.txt'), nls_charset_id('WE8MSWIN1252') )); exec dbms_stats.gather_table_stats(user, 'INVTEST_UNSTRUCT') set autotrace on select extract(object_value, '/Invoice/MailAddressTo') from invtest_unstruct where extractValue(object_value, '/Invoice/MailAddressTo/Person')='Joe Smith'; set autotrace off -- Code example continued on next page . . .

V2.1

EXTRACT(OBJECT_VALUE,'/INVOICE/MAILADDRESSTO') -----------------------------------------------------------<MailAddressTo id="PA"><Person>Joe Smith</Person><Street>10 Apple Tree Lane</Street><City>New York</City><State>NY</Stat e><Zipcode>12345</Zipcode></MailAddressTo> Execution Plan ---------------------------------------------------------Plan hash value: 2915445422 ----------------------------------------------------------------------------------------| Id | Operation | Name |Rows|Bytes| Cost(%CPU)| Time | ----------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 2002| 2 (0)| 00:00:01 | | 1 | TBL ACCESS BY IDX ROWID| INVTEST_UNSTRUCT | 1 | 2002| 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | INVTEST_UNSTRUCT_IDX1| 1 | | 1 (0)| 00:00:01 | ----------------------------------------------------------------------------------------Predicate Information (identified by operation id): --------------------------------------------------2 - access(EXTRACTVALUE(SYS_MAKEXML("XMLDATA"),'/Invoice/MailAddressTo/Person')='Joe Smith') -- Now create a function-based index and rerun query. create index invtest_unstruct_idx1 on invtest_unstruct (extractValue(object_value, '/Invoice/MailAddressTo/Person')); exec dbms_stats.gather_table_stats(user, 'INVTEST_UNSTRUCT') -- Now see is index is used. set autotrace on select extract(object_value, '/Invoice/MailAddressTo') from invtest_unstruct where extractValue(object_value, '/Invoice/MailAddressTo/Person')='Joe Smith'; set autotrace off EXTRACT(OBJECT_VALUE,'/INVOICE/MAILADDRESSTO') -----------------------------------------------------------<MailAddressTo id="PA"><Person>Joe Smith</Person><Street>10 Apple Tree Lane</Street><City>New York</City><State>NY</Stat e><Zipcode>12345</Zipcode></MailAddressTo> Execution Plan ---------------------------------------------------------Plan hash value: 2915445422 ----------------------------------------------------------------------------------------| Id | Operation | Name |Rows|Bytes| Cost(%CPU)| Time | ----------------------------------------------------------------------------------------| 0 | SELECT STATEMENT | | 1 | 2002| 2 (0)| 00:00:01 | | 1 | TBL ACCESS BY IDX ROWID| INVTEST_UNSTRUCT | 1 | 2002| 2 (0)| 00:00:01 | |* 2 | INDEX RANGE SCAN | INVTEST_UNSTRUCT_IDX1| 1 | | 1 (0)| 00:00:01 | ----------------------------------------------------------------------------------------Predicate Information (identified by operation id): --------------------------------------------------2 - access(EXTRACTVALUE(SYS_MAKEXML("XMLDATA"),'/Invoice/MailAddressTo/Person')='Joe Smith')

V2.1

61

11g Features
Features for increased flexibility and better performance
In-place XML schema evolution Partitioning support More efficient schema-optimized storage Simplification of text based searches using Oracle Text Enhanced XQuery support Elimination of the 64k limit on size of text node Optimization of non-schema based XML operations (new binary XML format)

www.skillbuilders.com

In-place XML schema evolution allows us to make changes to an XML schema without requiring that existing data to be copied, deleted, and reinserted. Oracle binary XML is a compact representation of an XML document. This significantly reduces the amount of disk space required to store unstructured (schema-less) XML documents. For a full description of the Oracle11g enhancements to XML, see the Oracle white paper titled New Features in Oracle XML DB for Oracle Database 11g Release 1 located at http://www.oracle.com/technology/products/database/oracle11g/pdf/xml-db-11g-whitepaper.pdf

V2.1

Summary
Oracle9i
Introduced XML DB repository, XMLType datatype, and XMLSchema Support for both LOB and structured storage options XPath expressions operate on individual elements of a document

62

10g enhancements
W3C XML XQuery language XMLQuery() and XMLTable() functions Interchangeable use of relational and XML data

SQL queries can operate on XML data XML queries can access relational data

11g Enhancements
In-place XML schema evolution Partitioning support plus more

www.skillbuilders.com

V2.1

63

To Learn More
SkillBuilders

Onsite, Offsite Training, Online Classes (instructor-led!) Introduction to XML Java, J2EE SOA, Web Services

Just a few examples:


Using XML in Oracle Oracle Performance Tuning


Software Security

SQL / Application Tuning RAC 11g Coming Very Soon!

To register
Visit www.skillbuilders.com Or call 1-888-803-5607

www.skillbuilders.com

V2.1

64

SkillBuilders Consulting
Oracle Performance Tuning Oracle Administration Short-term assignments (4 hour minimum)
Have

a performance problem?

Onsite or Remote Call 1-888-803-5607

Copyright 2007-2008 Kyle Hailey. Distributed by www skillbuilders com

V2.1

65

Oracle Software VAR


Call us for your Oracle software requirements
Database

and Options Business Intelligence Content Management

Expert and Honest advice Call 1-888-803-5607

Copyright 2007-2008 Kyle Hailey. Distributed by www skillbuilders com

V2.1

66

Thanks for Listening


Geoff.Wiland@skillbuilders.com 1-888-803-5607

V2.1

Вам также может понравиться