--------------------------------------------------------------------------------------------------------
-- XML - EXtensible Markup Language
--------------------------------------------------------------------------------------------------------
-- * XML stands for EXtensible Markup Language
-- * XML was designed to carry data, not to display data
-- * XML tags are not predefined. You must define your own tags
-- * XML is designed to be self-descriptive
-- * XML Documents Form a Tree Structure
-- * All XML Elements Must Have a Closing Tag
-- * XML Tags are Case Sensitive
-- * XML Attribute Values Must be Quoted
-- * An XML element is everything from (including) the element's start tag to
-- (including) the element's end tag.
-- * An element can contain:
-- other elements
-- text
-- attributes
-- * XML Attributes Must be Quoted
--------------------------------------------------------------------------------------------------------
'
Harry Potter
J K. Rowling
2005
29.99
Learning XML
Erik T. Ray
2003
39.95
'
--------------------------------------------------------------------------------------------------------
-- 1. Record => XML Methods
-- 1. FOR XML RAW
-- 2. FOR XML AUTO
-- 3. FOR XML EXPLICIT
-- 4. FOR XML PATH
-- 2. Creating & Populating XML columns
-- 3. OPENXML - Shredding a XML to a DB RowSet
-- 4. XML Indexes
-- 1. Intro - XML Methods
-- 2. Primary XML Index
-- 3. Secondary XML Index
--------------------------------------------------------------------------------------------------------
-- Record => XML
--------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------
-- FOR XML RAW [('name of element')] , [ROOT ('name of root')] , ELEMENTS
--------------------------------------------------------------------------------------------------------
USE Nortwind
GO
-- Select a rowset from the employees table:
SELECT lastname, title, birthdate
FROM employees
-- Recieve flat XML instead of a rowset:
-- Default is:
-- 1. All columns are Attributes of the row elements
-- 2. Element names are "row" (
)
-- 3. No Root element
SELECT lastname, title, birthdate
FROM employees
FOR XML RAW
-- Change the name of row element: (Instead of
)
SELECT lastname, title, birthdate
FROM employees
FOR XML RAW ('Employee')
-- Add a ROOT element:
SELECT lastname, title, birthdate
FROM employees
FOR XML RAW ('Employee'), ROOT('Employees')
-- Receive each row data as a SubElemnt:
SELECT lastname, title, birthdate
FROM employees
FOR XML RAW ('Employee'), ROOT('Employees'), ELEMENTS
--------------------------------------------------------------------------------------------------------
-- FOR XML AUTO
--------------------------------------------------------------------------------------------------------
SELECT cat.categoryname, pro.productID, pro.productName , pro.unitPrice
FROM products pro, categories cat
WHERE pro.categoryID = cat.categoryID
ORDER BY cat.categoryID, pro.productID
FOR XML AUTO
SELECT lastname, title, birthdate
FROM employees
FOR XML AUTO
SELECT productID, productName , unitPrice, categoryID
FROM products
FOR XML AUTO
SELECT categoryID , productID, productName , unitPrice
FROM products
FOR XML AUTO
SELECT categoryID, productID, productName , unitPrice
FROM products
ORDER BY categoryID, productID
FOR XML AUTO
SELECT sup.CompanyName , pro.productName , pro.unitPrice
FROM products pro, suppliers sup
WHERE pro.supplierID = pro.SupplierID
ORDER BY sup.CompanyName , pro.productName
FOR XML AUTO
-- All customers and their orders:
-- Use Order by to ensure order of hierarchy
SELECT c.customerid, c.companyname, c. country, o.orderid, o.orderdate
FROM customers c JOIN orders o
ON c.customerid = o.customerid
ORDER BY c.customerid, o.orderid
FOR XML AUTO
-- Also with ELEMENTS - Display column data as subelements for each row:
SELECT customer.customerid, customer.companyname, customer. country, order_detailes.orderid, order_detailes.orderdate
FROM customers customer JOIN orders order_detailes
ON customer.customerid = order_detailes.customerid
ORDER BY customer.customerid, order_detailes.orderid
FOR XML AUTO ,ELEMENTS
-- Also possible to use ROOT
SELECT customer.customerid, customer.companyname, customer. country, order_detailes.orderid, order_detailes.orderdate
FROM customers customer JOIN orders order_detailes
ON customer.customerid = order_detailes.customerid
ORDER BY customer.customerid, order_detailes.orderid
FOR XML AUTO ,ELEMENTS, ROOT('Customer_Orders')
--------------------------------------------------------------------------------
-- FOR XML EXPLICIT
--------------------------------------------------------------------------------
-- If I want to manually and explicitly determine the structure of the XML,
-- use a Universal Table to specify the structure.
-- * Each feild in the Universal table represents an element in the XML
-- * The alias for each selected column will determine its location in the XML
-- * The alias specifies the row Element name (Must be the same in all columns),
-- whether the value will be an attribute or a subelement,
-- and the name for the attribute or Subelement.
-- * For now, the first two columns (Tag and Parent) are always 1 and NULL.
-- We will change them only to create a more complex nested XML.
SELECT 1 AS Tag,
NULL AS Parent,
SupplierID AS [Products!1!SupplierID],
ProductName AS [Products!1!ProductName],
Unitprice AS [Products!1!Unitpricet]
FROM products
FOR XML EXPLICIT, ROOT('Products')
-- Using !Element
SELECT 1 AS Tag,
NULL AS Parent,
SupplierID AS [Products!1!SupplierID],
ProductName AS [Products!1!ProductName!Element],
Unitprice AS [Products!1!Unitprice!Element]
FROM products
FOR XML EXPLICIT
SELECT 1 AS Tag,
NULL AS Parent,
SupplierID AS [Products!1!SupplierID],
CategoryID AS [Products!1!CategoryID],
ProductName AS [Products!1!ProductName!Element],
Unitprice AS [Products!1!Unitprice!Element]
FROM products
FOR XML EXPLICIT
-- ELEMENTS is not allowed
SELECT 1 AS Tag,
NULL AS Parent,
SupplierID AS [Products!1!SupplierID],
CategoryID AS [Products!1!CategoryID],
ProductName AS [Products!1!ProductName!Element],
Unitprice AS [Products!1!Unitprice!Element]
FROM products
FOR XML EXPLICIT, ELEMENTS
-- Possible to use ROOT
SELECT 1 AS Tag,
NULL AS Parent,
SupplierID AS [Products!1!SupplierID],
CategoryID AS [Products!1!CategoryID],
ProductName AS [Products!1!ProductName!Element],
Unitprice AS [Products!1!Unitprice!Element]
FROM products
FOR XML EXPLICIT, ROOT('Products')
-- Nesting XML
SELECT 1 AS Tag,
NULL AS Parent,
CategoryName AS [category!1!CategoryName],
NULL AS [product!2!productName],
NULL AS [product!2!unitPrice]
FROM categories
UNION ALL
SELECT 2 ,
1 ,
CategoryName AS [category!1!CategoryName],
productName AS [product!2!productName],
unitPrice AS [product!2!unitPrice]
FROM products pro JOIN categories cat
ON pro.CategoryID = cat.CategoryID
ORDER BY [category!1!CategoryName] , [product!2!productName]
FOR XML EXPLICIT
--------------------------------------------------------------------------------
-- FOR XML PATH
--------------------------------------------------------------------------------
-- * Another way to explicitly map values in columns to XML positions
-- * Again, it is done using the column's alias.
SELECT SupplierID AS [@SupplierID],
ProductName AS [ProductName],
Unitprice AS [Unitprice]
FROM products
FOR XML PATH
SELECT SupplierID AS [@SupplierID],
ProductName AS [ProductName],
Unitprice AS [Unitprice]
FROM products
FOR XML PATH ('Products')
SELECT SupplierID AS [@SupplierID],
CategoryID AS [@categoryID],
ProductName AS [ProductName],
Unitprice AS [Unitprice]
FROM products
FOR XML PATH ('Products')
-- Building levels in the hierarchy is quite simple:
SELECT SupplierID AS [@SupplierID],
CategoryID AS [@categoryID],
ProductName AS [Basic_Details/ProductName],
Unitprice AS [Basic_Details/Unitprice]
FROM products
FOR XML PATH ('Products')
SELECT SupplierID AS [@SupplierID],
CategoryID AS [@categoryID],
ProductName AS [Basic_Details/ProductName],
Unitprice AS [Basic_Details/Unitprice],
QuantityPerUnit AS [Quantity_Details/ProductName],
UnitsInStock AS [Quantity_Details/Unitprice] ,
UnitsOnOrder AS [Quantity_Details/UnitsOnOrder]
FROM products
FOR XML PATH ('Products')
-- ELEMENTS allowed but does not have any effect
SELECT SupplierID AS [@SupplierID],
CategoryID AS [@categoryID],
ProductName AS [Basic_Details/ProductName],
Unitprice AS [Basic_Details/Unitprice],
QuantityPerUnit AS [Quantity_Details/ProductName],
UnitsInStock AS [Quantity_Details/Unitprice] ,
UnitsOnOrder AS [Quantity_Details/UnitsOnOrder]
FROM products
FOR XML PATH ('Products') , ELEMENTS
-- ROOT allowed
SELECT SupplierID AS [@SupplierID],
CategoryID AS [@categoryID],
ProductName AS [Basic_Details/ProductName],
Unitprice AS [Basic_Details/Unitprice],
QuantityPerUnit AS [Quantity_Details/ProductName],
UnitsInStock AS [Quantity_Details/Unitprice] ,
UnitsOnOrder AS [Quantity_Details/UnitsOnOrder]
FROM products
FOR XML PATH ('Products') , ROOT ('ProductsInfo')
--------------------------------------------------------------------------------------------------------
-- Creating & Populating XML columns
--------------------------------------------------------------------------------------------------------
DROP TABLE TestXML
GO
CREATE TABLE TestXML
(KioskID VARCHAR(10) PRIMARY KEY,
XMLValue xml)
--Insert Data Into Table
DECLARE @myXML1 xml,
@myXML2 xml
SET @myXML1 = '
'
SET @myXML2 = '
'
INSERT INTO TestXML VALUES ('Kiosk1', @myXML1)
INSERT INTO TestXML VALUES ('Kiosk2', @myXML2)
-- See the value
SELECT * FROM TestXML
-------------------------------------------------------------------------------------------
-- OPENXML - Shredding a XML to a DB RowSet - Demo
-------------------------------------------------------------------------------------------
-- ROOT
-- Customer - CustomerID, ContactName
-- Order - OrderID, CustomerID, EmployeeID, OrderDate
-- OrderDetail - ProductID, Quantity
-- Order
-- Customer
-- ROOT
DECLARE @idoc int
DECLARE @doc varchar(1000)
SET @doc ='
'
-- Create an internal representation of the XML document.
EXEC sp_xml_preparedocument @idoc OUTPUT, @doc
-- Execute a SELECT stmt using OPENXML rowset provider.
-- 1. Deepest Level
SELECT *
FROM OPENXML ( @idoc, '/ROOT/Customer/Order/OrderDetail',3)
WITH ( ProdID int '@ProductID',
Qty int '@Quantity')
-- 2. Deepest Level + 1 Level up
SELECT *
FROM OPENXML ( @idoc, '/ROOT/Customer/Order/OrderDetail',2)
WITH ( OrderID int '../@OrderID',
CustomerID varchar(10) '../@CustomerID',
OrderDate datetime '../@OrderDate',
ProdID int '@ProductID',
Qty int '@Quantity')
-- 2. Deepest Level + 2 Levels up
SELECT *
FROM OPENXML ( @idoc, '/ROOT/Customer/Order/OrderDetail',3)
WITH ( CustomerID varchar(25) '../../@CustomerID',
ContactName varchar(25) '../../@ContactName',
OrderID int '../@OrderID',
OrderDate datetime '../@OrderDate',
ProdID int '@ProductID',
Qty int '@Quantity')
----------------------------------------------------------------------------------------------------------------------------------
-- XML Indexes
----------------------------------------------------------------------------------------------------------------------------------
-- Products_Categories
-- Category - CategoryID, CategoryName
-- Product - ProductID, ProductName
-- Category
-- Products_Categories
-----------------------------------------------------------------------
-- Intro - XQUERY METHODS
-----------------------------------------------------------------------
-----------------------------------------------------------------------
-- Query
-----------------------------------------------------------------------
SELECT * FROM TestXML
-- Get all products:
SELECT KioskID, XMLValue.query('(/Products_Categories/Category/Product)')
FROM TestXML
-- Get all categories
SELECT KioskID, XMLValue.query('(/Products_Categories/Category)')
FROM TestXML
-- Specify a condition inside [ ]
-- Get the 4th product:
SELECT KioskID, XMLValue.query('(/Products_Categories/Category/Product)[4]')
FROM TestXML
-- Get the Second category with Query:
SELECT KioskID, XMLValue.query('(/Products_Categories/Category[2])')
FROM TestXML
-- Get product 214:
SELECT KioskID, XMLValue.query('(/Products_Categories/Category/Product[@ProductID=214])')
FROM TestXML
-- Get product where id>100:
SELECT KioskID, XMLValue.query('(/Products_Categories/Category/Product[@ProductID>100])')
FROM TestXML
-- Get the Toys category:
SELECT KioskID, XMLValue.query('(/Products_Categories/Category[@CategoryName="Toys"])')
FROM TestXML
-----------------------------------------------------------------------
-- Exist
-----------------------------------------------------------------------
-- * Returns 0 (False) or 1 (True)
--Existing
SELECT KioskID, XMLValue.exist('(/Products_Categories/Category[@CategoryName="Toys"])')
FROM TestXML
--Non-existing
SELECT KioskID, XMLValue.exist('(/Products_Categories/Category[@CategoryName="Fruits"])')
FROM TestXML
-----------------------------------------------------------------------
-- Value
-----------------------------------------------------------------------
SELECT KioskID, XMLValue.value('(/Products_Categories/Category/Product/@ProductID)[1]' , 'int' )
FROM TestXML
SELECT KioskID, XMLValue.value('(/Products_Categories/Category/Product/@ProductName)[1]' , 'varchar(25)' )
FROM TestXML
SELECT KioskID, XMLValue.value('(/Products_Categories/Category/Product/@ProductName)[3]' , 'varchar(25)' )
FROM TestXML
----------------------------------------------------------------------------------------------------------------------------------
-- Primary XML Index
----------------------------------------------------------------------------------------------------------------------------------
--Essentially, the primary XML index is a clustered index on an internal table,
--sometimes referred to as the "node" table (in query execution plans it will be referred
--to as "xml_index_nodes_N" where N is a random integer). This clustered index
--stores data according to XML document order.
SELECT * FROM TestXML
-- Only XML Index can be created on XML column 'XMLValue'.
CREATE INDEX p_xml_ix ON TestXML(xmlValue)
-- Must create primary index first
CREATE XML INDEX p_xml_ix ON TestXML(xmlValue)
-- Create XML Index
CREATE PRIMARY XML INDEX p_xml_ix ON TestXML(xmlValue)
-- Get product 214:
SELECT KioskID, XMLValue.query('(/Products_Categories/Category/Product[@ProductID=214])')
FROM TestXML
----------------------------------------------------------------------------------------------------------------------------------
-- Secondary XML Index
----------------------------------------------------------------------------------------------------------------------------------
-- The PATH secondary XML index created is based on the primary XML index
-- and improves any path expression queries made on this column (query / exist)
CREATE XML INDEX secondary_path_ix
ON TestXML(xmlValue)
USING XML INDEX p_xml_ix
FOR PATH
-- When querying for specific values in an XML instance, you should use the VALUE index
CREATE XML INDEX secondary_value_ix
ON TestXML(xmlValue)
USING XML INDEX p_xml_ix
FOR VALUE
-- When querying for multiple values in an XML instance, you should use the VALUE index
CREATE XML INDEX secondary_property_ix
ON TestXML(xmlValue)
USING XML INDEX p_xml_ix
FOR PROPERTY