sql OPENXML()在SQL Server中粉碎XML的方法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了sql OPENXML()在SQL Server中粉碎XML的方法相关的知识,希望对你有一定的参考价值。

/*

The OPENXML function allows you to shred XML and return it as scalar (relational) data. 

It allows you to start with XML like this:

<?xml version="1.0" encoding="ISO-8859-1"?>
<order id="123456" date="2015-01-01">
  <salesperson id="123">
    <name>Naomi Sharp</name>
  </salesperson>
  <customer id="921">
    <name>Dan Drayton</name>
  </customer>
  <items>
    <item id="268" quantity="2"/>
    <item id="561" quantity="1"/>
    <item id="127" quantity="2"/>
  </items>
</order>

and query the XML to end up with scalar (relational) data like this:

orderid	   id	   quantity
123456	   268	   2
123456	   561	   1
123456	   127	   2

The format is OPENXML(handle created by sp_xml_preparedocument, the conext node, and the flag), such as OPENXML(@docHandle, 'order', 1).

Details of the OPENXML arguments:
- the handle is a variable that you create as an output element using sp_xml_prepare document
- the context node is the node (located by the XPath) that the function will start from when querying
- the flag is a number that tells it what you're querying for (apparently you can query outside of the scope flag if you specific a different XPath). The values:

0: attribute-centric (I want to query attributes)
1: attribute-centric (I want to query attributes)
2: element-centric (I want to query elements)
3 (1+2): attributes and elements (I want to query attributes and elements)

There are four steps to using OPENXML to shred XML in SQL Server:

1. start by declaring a VARCHAR variable (not an XML variable) and place the XML code into the variable as a VARCHAR string
2. use sp_xml_preparedocument to create a handle for the in-memory node-tree that the XML will be loaded into. The handle will be called @docHandle
3. use OPENXML() to tell it what you want to query into scalar data 
4. use sp_xml_removedocument to release the node tree from memory

*/

-- An example of OPENXML in use
SELECT * FROM OPENXML(@docHandle, 'order', 1)
WITH  (id INT,
      date DATE,
      customer varchar(25) 'customer/name');
	
-- DETAIL of the OPENXML example

-- SELECT * FROM OPENXML, with the arguments @docHandle (the handle of the node in the form of a variable), 'order' (start querying from the order element), and flag = 1 (I want to query attributes)
SELECT * FROM OPENXML(@docHandle, 'order', 1)
-- WITH tells it what columns you want to return
WITH	(
  -- id will pull the id attribute from the order element, and return it as an integer
  id INT,
  -- date will pull the date attribute from the order element, and return it as a date
  date DATE,
  -- customer will pull the name element as located in the XPath 'order/customer/name' - note two things: the XPath is only needed if you're going outside of the context node (order is the context node, but from there you're going down one element level to customer, and then down another element level to name), and you're pulling an element even though you set the flag to 1 (I want attributes) - you're apparently allowed to go outside of the scope of the flag if you explicitly spell it out in the XPath 	
  customer varchar(25) 'customer/name'
);

-----------------------------------------------------------------
-- EXAMPLE OF ENTIRE PROCESS (all processed in a single batch) --
-----------------------------------------------------------------

-- 1. start by declaring a VARCHAR variable (not an XML variable) and place the following XML into the variable.
DECLARE @x VARCHAR(max);
SET @x = '<?xml version="1.0" encoding="ISO-8859-1"?>
<order id="123456" date="2015-01-01">
  <salesperson id="123">
    <name>Naomi Sharp</name>
  </salesperson>
  <customer id="921">
    <name>Dan Drayton</name>
  </customer>
  <items>
    <item id="268" quantity="2"/>
    <item id="561" quantity="1"/>
    <item id="127" quantity="2"/>
  </items>
</order>';

-- 2. use sp_xml_preparedocument to create a handle for the in-memory node-tree that the XML will be loaded into. The handle will be called @docHandle
DECLARE @docHandle INT;
EXEC sp_xml_preparedocument @docHandle OUTPUT, @x;

-- 3A. Query example A - query @docHandle using OPENXML function, starting at 'order' node, looking for just attributes of 'order' node. The three columns we return will be the id attribute, the date attribute, and the customer element (using the XPath 'customer/name' you tell it to grab the element)
SELECT * FROM OPENXML(@docHandle, 'order', 1)
WITH  (id INT,
      date DATE,
      customer varchar(25) 'customer/name');

/* Result from query example A:
id      date       customer
1234562 015-01-01  Dan Drayton
*/

-- 3B. Query example B - query @docHandle using OPENXML function, starting at 'order/items/item' node, looking for just attributes of 'order' node. The orderid column actually goes two level back up from the context node 'order/items/item', as indicated by the '../..'. it then pulls the id attribute and quantity attribute from the context node 'order/items/item'.
SELECT * FROM OPENXML(@docHandle, 'order/items/item', 1)
WITH  (orderid INT '../../@id',
      id INT,
      quantity INT);

/*Result from query example B:
orderid	id	quantity
123456	268	2
123456	561	1
123456	127	2
*/

-- 3C. Query example C - query @docHandle using OPENXML function, starting at 'order/salesperson' node, looking for both attributes and elements as indicated by the 3. The columns pulled will be the id attribute as well as the name element
SELECT * FROM OPENXML(@docHandle, 'order/salesperson', 3)
WITH  (id INT,
      name VARCHAR(25));

/*Result from query example C:
orderid	id	quantity
123456	268	2
123456	561	1
123456	127	2
*/

-- 4. use sp_xml_removedocument to release the node tree from memory
EXEC sp_xml_removedocument @docHandle;

--------------------------------------
-- Using OPENXML() with a namespace --
--------------------------------------

-- if your XML document uses a namespace, you can include that with OPENXML()

-- you can specify a namespace and prefix in sp_xml_preparedocument
EXEC sp_xml_preparedocument @docHandle OUTPUT, @xml,
	'root xmlns:asw="http://aw/order"/>';

-- you can also prefix a namespace in row and column patterns
SELECT * FROM OPENXML(@docHandle, 'awo:order', 1)
WITH	(id INT,
	date DATE,
	customer VARCHAR(25) 'awo:customer/awo:name');


-- entire procedure using a namespace
DECLARE @x VARCHAR(max);
SET @x = '<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- root order attribute has namespace aw/order -->
<order xmlns="http://aw/order" id="123456" date="2015-01-01">
  <salesperson id="123">
    <name>Naomi Sharp</name>
  </salesperson>
  <customer id="921">
    <name>Dan Drayton</name>
  </customer>
  <items>
    <item id="268" quantity="2"/>
    <item id="561" quantity="1"/>
    <item id="127" quantity="2"/>
  </items>
</order>';

--how to specify the namespace in sp_xml_preparedocument
DECLARE @docHandle INT;
-- root is an arbitraty element name, and putting awo in front of the namespace gives it an alias, a way to reduce having to spell out the entire namespace each time 
EXEC sp_xml_preparedocument @docHandle OUTPUT, @x, '<root xmlns:awo="http://aw/order"/>'

-- use OPENXML with the namespace. In this case, 'awo' tells it to use the order element from the awo aliased namespace used in sp_xml_preparedocument, which in this case was "http://aw/order"
SELECT * FROM OPENXML(@docHandle, '
awo:order', 1)
WITH	(id INT,
	date DATE,
	-- specifies specific path, in this case, go to 'awo' customer element, then down to 'awo' name element
	customer VARCHAR(25) 'awo:customer/awo:name');

-- remove the node from memory
EXEC sp_xml_removedocument @docHandle;

-------------------------------------------------------------
-- CREATING AN EDGE TABLE SHOWING METADATA ABOUT XML NODES --
-------------------------------------------------------------

-- by using OPENXML without a flag (the number as the last argument), you can bring back a so-called edge table, which is tabular representation of the node tree and the metadata represented within the data - this shows the structure of the XML data, as well as the data contained within it.
SELECT * FROM OPENXML(@docHandle, 'order');

-- full example of getting an edge table by omitting the flag argument and the WITH clause
DECLARE @x VARCHAR(max);
SET @x = '<?xml version="1.0" encoding="ISO-8859-1"?>
<order id="123456" date="2015-01-01">
  <salesperson id="123">
    <name>Naomi Sharp</name>
  </salesperson>
  <customer id="921">
    <name>Dan Drayton</name>
  </customer>
  <items>
    <item id="268" quantity="2"/>
    <item id="561" quantity="1"/>
    <item id="127" quantity="2"/>
  </items>
</order>';

DECLARE @docHandle INT;
EXEC sp_xml_preparedocument @docHandle OUTPUT, @x;

-- note how there's no flag number or a WITH clause
SELECT * FROM OPENXML(@docHandle, 'order')

/* Resulting edge table of metadata:
id	parentid	nodetype	localname	prefix	namespaceuri	datatype	prev	text
0	NULL	1	order	NULL	NULL	NULL	1	NULL
5	0	2	id	NULL	NULL	NULL	NULL	NULL
23	5	3	#text	NULL	NULL	NULL	NULL	123456
6	0	2	date	NULL	NULL	NULL	NULL	NULL
24	6	3	#text	NULL	NULL	NULL	NULL	2015-01-01
7	0	1	salesperson	NULL	NULL	NULL	NULL	NULL
8	7	2	id	NULL	NULL	NULL	NULL	NULL
25	8	3	#text	NULL	NULL	NULL	NULL	123
9	7	1	name	NULL	NULL	NULL	NULL	NULL
26	9	3	#text	NULL	NULL	NULL	NULL	Naomi Sharp
10	0	1	customer	NULL	NULL	NULL	7	NULL
11	10	2	id	NULL	NULL	NULL	NULL	NULL
27	11	3	#text	NULL	NULL	NULL	NULL	921
12	10	1	name	NULL	NULL	NULL	NULL	NULL
28	12	3	#text	NULL	NULL	NULL	NULL	Dan Drayton
13	0	1	items	NULL	NULL	NULL	10	NULL
14	13	1	item	NULL	NULL	NULL	NULL	NULL
15	14	2	id	NULL	NULL	NULL	NULL	NULL
29	15	3	#text	NULL	NULL	NULL	NULL	268
16	14	2	quantity	NULL	NULL	NULL	NULL	NULL
30	16	3	#text	NULL	NULL	NULL	NULL	2
17	13	1	item	NULL	NULL	NULL	14	NULL
18	17	2	id	NULL	NULL	NULL	NULL	NULL
31	18	3	#text	NULL	NULL	NULL	NULL	561
19	17	2	quantity	NULL	NULL	NULL	NULL	NULL
32	19	3	#text	NULL	NULL	NULL	NULL	1
20	13	1	item	NULL	NULL	NULL	17	NULL
21	20	2	id	NULL	NULL	NULL	NULL	NULL
33	21	3	#text	NULL	NULL	NULL	NULL	127
22	20	2	quantity	NULL	NULL	NULL	NULL	NULL
34	22	3	#text	NULL	NULL	NULL	NULL	2

*/

以上是关于sql OPENXML()在SQL Server中粉碎XML的方法的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server XML转Table

使用 OpenXml 插入 SQL

将 XML 数据上传到 SQL Server

ORM - 特定于用于 .NET 的 SQL Server 2008+

使用 SQL OPENXML 查找特定子元素

仅在openxml中获取指定节点的子节点