Tải bản đầy đủ - 0 (trang)
3-2. Loading XML Data into Rows and Columns

3-2. Loading XML Data into Rows and Columns

Tải bản đầy đủ - 0trang

s



EXECUTE master.dbo.sp_xml_preparedocument @DocID OUTPUT, @DocXML;

SELECT

INTO

FROM



ID, ClientName, Country

XmlTable

OPENXML(@DocID, 'CarSales/Client', 2)

WITH (

ID VARCHAR(50)

,ClientName VARCHAR(50)

,Country VARCHAR(10)

);



EXECUTE master.dbo.sp_xml_removedocument @DocID;

3.



Query the XmlTable table and you should see something like the following result:

ID

3

4

5

6

7



ClientName

John Smith

Bauhaus Motors

Honest Fred

Fast Eddie

Slow Sid



Country

1

2

3

2

3



How It Works

Ever since SQL Server 2000 was released, OPENXML has been helping developers and DBAs load (or “shred” as

it is known) XML data into relational tables. This technology is still supported in the current version of SQL

Server, and is easy and efficient to use. The result is totally different from that obtained in Recipe 3-1, as this time

the source file is broken down into its constituent data fragments and each element or attribute loaded into a

separate column in one or more tables.

First, the source file is loaded into the @DocXML variable. Then, the sp_xml_preparedocument stored

procedure is run against this variable to prepare the XML and return a handle, in this case, @DocID. The

handle is passed to the OPENXML command, which reads the XML data and shreds it into rows and columns

in the destination table. Then, in the OPENXML statement, the row pattern ('CarSales/Client') identifies

which nodes to process.

The WITH clause is the ColPattern, which allows you to traverse the hierarchy of the XML and select any

element you choose. For attributes, you can merely use a ColPattern like Invoice/@InvoiceNumber. You

can traverse the XML up beyond the initial node specified using the RowPattern, by using (for example)

../Invoice—assuming that the row pattern was 'CarSales/Client'/Invoice. If a ColPattern is not specified,

the default mapping (attribute-centric or element-centric mapping, as specified) will take place.

Be warned, however, that although simple, OPENXML is very memory-intensive. Also OPENXML uses XPath (not

XQuery). Taken together, this signifies that OPENXML is best used in the following situations:





When you do not wish to import the XML data into SQL Server before using it in a T-SQL

query.







When you have a properly formed XML document.







When the XML document is less that 2 gigabytes in size.



To extend this example slightly, let us assume that you have an (admittedly fairly simple) attribute-centric

XML file, something like this (C:\SQL2012DIRecipes\CH03\ClientLiteAttributeCentric.Xml):









138

www.it-ebooks.info



Chapter 3 ■ XML Data Sources











To load this file, simply change the OPENXML flags parameter to 1, as in the following example

(C:\SQL2012DIRecipes\CH03\ShredClientLiteAttributeCentric.Sql):

DECLARE @DocID INT;

DECLARE @DocXML VARCHAR(MAX);

SELECT @DocXML  = CAST(XMLSource AS VARCHAR(MAX))

FROM OPENROWSET(BULK 'C:\SQL2012DIRecipes\CH03\ClientLite.xml', SINGLE_BLOB)



AS X (XMLSource);



EXECUTE master.dbo.sp_xml_preparedocument @DocID OUTPUT, @DocXML;

SELECT

ID, ClientName, Country

FROM

OPENXML(@DocID, 'CarSales/Client', 1)

WITH (

ID VARCHAR(50)

,ClientName VARCHAR(50)

,Country VARCHAR(10)

);

EXECUTE master.dbo.sp_xml_removedocument @DocID;

Obviously, it is impossible to predict the multiple permutations of an XML data source. However, it is

probably fair to say that not all source files will be as simple as the one we used here. Fortunately, OPENXML can

handle more complex data sources. The art and science of OPENXML is in the two elements that make up an

OPENXML command. These are:





The row pattern, which identifies the nodes to process ('/CarSales/Client' in the

examples given in this recipe).







The schema declaration, which is the WITH clause that specifies the column output.



As a more complex example, here is an XML fragment with nested elements

(C:\SQL2012DIRecipes\CH03\NestedClients.Xml):





 3

 John Smith

 1

 Uttoxeter



 3A9271EA-FC76-4281-A1ED-714060ADBA30

 500.00







 4

 Bauhaus Motors

 2

 Oxford



139

www.it-ebooks.info



Chapter 3 ■ XML Data Sources





 C9018CC1-AE67-483B-B1B7-CF404C296F0B

 0.00







And here is the code to read it using OPENXML (you will need, of course, to wrap it in the preceding code to

load, prepare, and remove the XML document from memory—omitted here to save space)

(C:\SQL2012DIRecipes\CH03\ShredNestedClients.Sql):

SELECT ID, ClientName, Country, TotalDiscount

FROM

OPENXML(@DocID, 'CarSales/Client', 2)

WITH (

ID VARCHAR(50) 'ID'

,ClientName VARCHAR(50) 'ClientName'

,Country VARCHAR(10)'Country'

,TotalDiscount NUMERIC(18,2) 'Invoice/TotalDiscount'

)

One final thing to take away is that whether you are using attribute- or element-centric XML, you are still using

T-SQL. This means that you can use WHERE to filter output and ORDER BY to sort it. The snippet given at the top of

this recipe could be extended like this (C:\SQL2012DIRecipes\CH03\ShredNestedClientsFilterAndSort.Sql):

DECLARE @DocID INT;

DECLARE @DocXML VARCHAR(MAX);

SELECT @DocXML = CAST(XMLSource AS VARCHAR(MAX))

FROM OPENROWSET(BULK 'C:\SQL2012DIRecipes\CH03\ClientLite.xml', SINGLE_BLOB)

(XMLSource);



AS X 



EXECUTE master.dbo.sp_xml_preparedocument @DocID OUTPUT, @DocXML;

SELECT

INTO

FROM



WHERE

ORDER BY



ID, ClientName, Country

XmlTable

OPENXML(@DocID, 'CarSales/Client', 2)

WITH (

ID VARCHAR(50)

,ClientName VARCHAR(50)

,Country VARCHAR(10)

)

Country = 3

ID



EXECUTE master.dbo.sp_xml_removedocument @DocID;



Hints, Tips, and Traps





The XML document must be well-formed—and specifically only have one top-level

(root) element.







Despite being an older technology, OPENXML is reputed to be the fastest solution when it

comes to loading XML data into a relational structure from T-SQL.



140

www.it-ebooks.info



Chapter 3 ■ XML Data Sources



3-3. Shredding an XML File into an SQL Server Table

Problem

You want to load XML data into SQL Server tables and columns without the overhead of

sp_xml_preparedocument.



Solution

Use OPENROWSET (BULK) and SQL Server’s XQuery support to shred and load the source file.

1.



Create a destination table using the following code

(C:\SQL2012DIRecipes\CH03\tblXMLImport_Clients.Sql):

CREATE TABLE dbo.XmlImport_Clients

(

ID int NULL,

ClientName varchar(50) NULL,

Town varchar(50) NULL,

County varchar(50) NULL,

Country int NULL

) ;

GO



2.



Locate an XML source file—I will use C:\SQL2012DIRecipes\CH03\ClientLite.Xml,

as used in Recipe 3-2.



3.



Load the file using the following code snippet

(C:\SQL2012DIRecipes\CH03\ShredXMLUsingOPENROWSETBulk.Sql):

DECLARE @XMLSource XML;

SELECT

FROM



@XMLSource = CAST(XMLSource AS XML) ;

OPENROWSET(BULK 'C:\SQL2012DIRecipes\CH03\ClientLite.xml', SINGLE_BLOB) 

AS X (XMLSource);



INSERT INTO XmlImport_Clients (ID, ClientName, Town, County, Country)

SELECT

SRC.Client.value('ID[1]', 'INT') AS ID

,SRC.Client.value('ClientName[1]', 'VARCHAR(50)') AS ClientName

,SRC.Client.value('Town[1]', 'VARCHAR(50)') AS Town

,SRC.Client.value('County[1]', 'VARCHAR(50)') AS County

,SRC.Client.value('Country[1]', 'INT') AS Country

FROM @XMLSource.nodes('CarSales/Client') AS SRC (Client);



How It Works

OPENXML is a venerable approach that still works well, but since SQL Server 2005, there have been other

solutions to getting XML data into SQL Server using SQL Server’s support for XML and XQuery. Though a little

disconcerting at first (perhaps because it is less “T-SQL” and more XQuery), using an XML data type can be both

an efficient and a powerful way to load XML source data into a database structure. Unlike OPENXML, loading XML



141

www.it-ebooks.info



Chapter 3 ■ XML Data Sources



data using the .nodes() method will not require sp_xml_preparedocument to instantiate an XML object. This

technique is best used in the following situations:





When you want to load all or part of the contents of the source document into

table columns.







When setting up an SSIS package to shred the data is overkill.



In this recipe, we loaded the source file into a variable, from which it is shredded into a table using the

.nodes() method of an XML data type. The .value() method extracted values from the XML instance stored as

an XML type.

The “staging variable” approach used here requires lots of memory for large files. So, if you find that using

a staging variable seems somewhat old-fashioned, then there is a solution to avoid the staging variable—with a

judicious application of CROSS APPLY. So, using the same XML source file as earlier in this recipe, the code for this

approach is as follows

(C:\SQL2012DIRecipes\CH03\ShredXMLUsingOPENROWSETBulkAndCrossApply.Sql):

INSERT INTO XmlImport_Clients (ID, ClientName, Town, County, Country)

SELECT

SRC.Client.value('ID[1]', 'INT') AS ID

,SRC.Client.value('ClientName[1]', 'VARCHAR(50)') AS ClientName

,SRC.Client.value('Town[1]', 'VARCHAR(50)') AS Town

,SRC.Client.value('County[1]', 'VARCHAR(50)') AS County

,SRC.Client.value('Country[1]', 'INT') AS Country

FROM

(

SELECT

FROM



CAST(XMLSource AS XML)

OPENROWSET(BULK 'C:\SQL2012DIRecipes\CH03\Clients_Simple.xml', SINGLE_BLOB)

AS X (XMLSource)

) AS X (XMLSource)

CROSS APPLY XMLSource.nodes('CarSales/Client') AS SRC (Client);







This second CROSS APPLY approach is slightly more complex, but shreds the data directly into the

destination table.

Once again, as we are in the world of T-SQL, the code we have used so far can be extended to filter and sort

the data that you are loading. So, if we take the preceding example and decide only to load the records where the

country is “3”—and also to order by the Town element, the following is the code to use

(C:\SQL2012DIRecipes\CH03\ShredXMLUsingOPENROWSETBulkAndCrossApplyWithFilter.Sql):

INSERT INTO XmlImport_Clients (ID, ClientName, Town, County, Country)

SELECT

SRC.Client.value('ID[1]', 'INT') AS ID

,SRC.Client.value('ClientName[1]', 'VARCHAR(50)') AS ClientName

,SRC.Client.value('Town[1]', 'VARCHAR(50)') AS Town

,SRC.Client.value('County[1]', 'VARCHAR(50)') AS County

,SRC.Client.value('Country[1]', 'INT') AS Country

FROM

(

SELECT CAST(XMLSource AS XML)

FROM OPENROWSET(BULK 'C:\SQL2012DIRecipes\CH03\Clients_Simple.xml', SINGLE_BLOB) 

AS X (XMLSource)

) AS X (XMLSource)



142

www.it-ebooks.info



Tài liệu bạn tìm kiếm đã sẵn sàng tải về

3-2. Loading XML Data into Rows and Columns

Tải bản đầy đủ ngay(0 tr)

×