Để chọn những loại nhánh khác hơn là nhánh phần tử, ta dùng Node-Type
Test. Mục đích của việc dùng Node-Type Test là để chỉ định sự lựa chọn khác thường. Thí dụ như, descendant::text() cho ta mọi text nhánh là con cháu của
- 26 -
nhánh ngữ cảnh, dù rằng loại nhánh chính của con cháu nhánh ngữ cảnh là phần tử. Có 4 loại Node-Type Tests như liệt kê dưới đây:
Node-Type Trả về Thí dụ comment() Mọi ghi chú của nhánh. following::comment() chọn mọi ghi chú của nhánh hiện ra sau nhánh ngữ cảnh. node() Mọi nhánh preceding::node() chọn mọi nhánh hiện ra trước nhánh ngữ cảnh. processing- instruction() Mọi chỉ thị xử lý của nhánh self::processing instruction() chọn mọi chỉ thị xử lý nhánh trong nhánh ngữ cảnh. text() Mọi text của nhánh child::text() chọn mọi text nhánh là con của nhánh ngữ cảnh. Một số ví dụ khác Cú pháp Ý nghĩa
./author Mọi phần tử author trong ngữ cảnh trước. cú pháp này tương đương với author.
/bookstore Phần tử gốc tên là bookstore.
//author Mọi phần tử tên là author trong tài liệu.
book[/bookstore/@specialty = @style]
Mọi phần tử book có thuộc tính style với giá trị bằng giá trị của thuộc tính specialty của phần tử gốc bookstore của tài liệu.
- 27 -
author/firstname Mọi phần tử firstname con của các phần tử
author.
bookstore//title Mọi phần tử title hoặc con cháu của nó trong phần tử bookstore.
bookstore/*/title Mọi phần tử title là cháu của phần tử
bookstore. bookstore//book/excerpt//emp
h
Mọi phần tử emph bất cứ nơi nào dưới excerpt là con của những phần tử book , bất cứ nơi nào dưới phần tử bookstore.
.//title Mọi phần tử title một hay nhiều bậc thấp hơn trong nhánh ngữ cảnh.
author/* Mọi phần tử là con của các phần tử con author.
book/*/lastname Mọi phần tử lastname là cháu của các phân từ con của phần tử book.
*/* Mọi phần tử cháu của nhánh ngữ cảnh.
*[@specialty] Mọi phần tử con có thuộc tính specialty.
@style Thuộc tính style của nhánh ngữ cảnh.
price/@exchange Thuộc tính exchange của những phần tử price trong ngữ cảnh, tức là những phần tử price của nhánh ngữ cảnh.
price/@exchange/total Trả về một tập rỗng, vì thuộc tính không có phần tử con. Cú pháp này được chấp nhận trong
- 28 -
văn phạm của XML Path Language, nhưng không thật sự hợp lệ.
book[@style] Mọi phần tử book có thuộc tính style trong nhánh ngữ cảnh. Lưu ý phần nằm trong ngoặc vuông là điều kiện của phần tử book
book/@style Thuộc tính style của mọi phần tử book trong nhán ngữ cảnh. Ở đây không có điều kiện như hàng trên. Ta nói đến thuộc tính hay phần tử nằm bên phải nhất.
@* Mọi thuộc tính của nhánh ngữ cảnh.
author[1] Phần tử author thứ nhất trong nhánh ngữ cảnh.
author[firstname][3] Phần tử author thứ ba có một phần tử con
firstname.
my:book Phần tử book từ không gian tên my.
- 29 -
Chương 3. SQL SERVER VÀ DỮ LIỆU XML 3.1. SQL Server và kiểu dữ liệu XML
Cùng với sự phát triển của XML, các hệ quản trị cơ sở dữ liệu bắt đầu có sự hỗ trợ cho ngôn ngữ này. Điển hình là việc hệ quản trị cơ sở dữ liệu SQL Server đã coi XML như là một kiểu dữ liệu, và ngày càng cải tiến khả năng hỗ trợ cho loại ngôn ngữ này.
Ở SQL Server 2000, ta có thể nhận được dữ liệu quan hệở dạng XML với cú pháp: FOR XML, hoặc lưu trữ XML như dữ liệu quan hệ trong SQL Server sử dụng mệnh đề OPEN XML. Cho tới SQL Server 2005 có thêm một kiểu dữ liệu mới là XML cho phép ta viết mã nhận dữ liệu XML như là XML, tránh việc biến đổi từ XML thành dữ liệu quan hệ khi dùng OPEN XML. Cũng từ phiên bản SQL Server 2005, ta cũng có thể dùng tài liệu giản đồ biểu diễn trong ngôn ngữ W3C XML Schema Definition (đôi khi gọi là giản đồ XSD) để chỉ ra cấu trúc hợp lệ trong XML. Từ đó mở ra nhiều hướng tiếp cận cho dữ liệu XML trong hệ quản trị cơ sở dữ liệu này.
3.2. Khai phá dữ liệu XML trong SQL Server
3.2.1. Cách sinh ra một tài liệu XML trong SQL Server
Trong SQL Server, để tạo ra một tài liệu XML ta sử dụng cú pháp sau:
Để tìm hiểu kỹ hơn, ta từng bước đi vào từng cú pháp trong cú pháp lệnh SQL trên. Ta thực hành trên CSDL Northwind với các bảng dữ liệu Customer, Products, Order details.
Trước tiên, ta tìm hiểu cú pháp lệnh: FOR XML {RAW[('ElementName')]} FOR XML {{RAW[('ElementName')]|AUTO}|EXPLICIT|PATH[('ElementName')]} [,BINARY BASE64] [,TYPE] [,ROOT[('RootName')]
- 30 -
Cú pháp này cho kết quả là một tài liệu XML phân mảnh (không có phần tử gốc) và các phần tử XML này sử dụng chung một định danh nằm trong thẻ <row /> trong trường hợp sử dụng định danh khác cho thẻ thì ta sẽ truyền định danh này vào với dạng ‘ElementName’. Ví dụ:
Hoặc sử dụng định danh:
Trong trường hợp dữ liệu được truy vấn từ nhiều bảng dữ liệu: Ví dụ 1:
SELECT CustomerID, ContactName, Country FROM Customers
WHERE Country = 'Spain' FOR XML RAW
Kết quả như sau:
<row CustomerID="BOLID" ContactName="Martín Sommer" Country="Spain" /> <row CustomerID="FISSA" ContactName="Diego Roel" Country="Spain" /> <row CustomerID="GALED" ContactName="Eduardo Saavedra" Country="Spain" />
<row CustomerID="GODOS" ContactName="José Pedro Freyre" Country="Spain" />
<row CustomerID="ROMEY" ContactName="Alejandra Camino" Country="Spain" />
Ví dụ 2:
SELECT CustomerID, ContactName, Country FROM Customers
WHERE Country = 'Spain' FOR XML RAW('CustomerInfo')
Kết quả như sau:
<CustomerInfo CustomerID="BOLID" ContactName="Martín Sommer" Country="Spain" />
<CustomerInfo CustomerID="FISSA" ContactName="Diego Roel" Country="Spain" />
<CustomerInfo CustomerID="GALED" ContactName="Eduardo Saavedra" Country="Spain" />
<CustomerInfo CustomerID="GODOS" ContactName="José Pedro Freyre" Country="Spain" />
<CustomerInfo CustomerID="ROMEY" ContactName="Alejandra Camino" Country="Spain" />
- 31 -
Tiếp theo, ta tìm hiểu cú pháp:
FOR XML {AUTO}
Cú pháp này cho kết quả là một tài liệu XML phân mảnh và các phần tử XML này sử dụng chung một định danh nằm trong thẻ <Customers /> - là tên của bảng được truy vấn. Ví dụ:
Trong trường hợp dữ liệu được truy vấn từ nhiều bảng kết quả trả về là tài liệu xml phân mảnh trong đó mỗi phần tử XML có tên là bảng đầu tiên được truy vấn đến, có thuộc tính là các trường dữ liệu tồn tại trong bảng đó và các phần tử con là các bảng tiếp theo được truy vấn, các phần tử con này cũng chứa thuộc tính là các trường dữ liệu trong bảng tiếp theo, ví dụ:
Ví dụ 3:
SELECT P.ProductID, ProductName, UnitPrice=ROUND(P.UnitPrice, 2),
Quantity,
Discount=CONVERT(int, Discount * 100),
ExtendedPrice=ROUND(CONVERT(money, Quantity *(1 - Discount) *
P.UnitPrice), 2)
FROM Products P, [Order Details] Od
WHERE Od.ProductID = P.ProductID and Od.OrderID = 10248 FOR XML RAW
Kết quả như sau:
<row ProductID="11" ProductName="Queso Cabrales" UnitPrice="21.0000" Quantity="12" Discount="0" ExtendedPrice="252.0000" />
<row ProductID="42" ProductName="Singaporean Hokkien Fried Mee" UnitPrice="14.0000" Quantity="10" Discount="0"
ExtendedPrice="140.0000" />
<row ProductID="72" ProductName="Mozzarella di Giovanni"
UnitPrice="34.8000" Quantity="5" Discount="0" ExtendedPrice="174.0000" />
Ví dụ 4:
SELECT CustomerID, ContactName, Country FROM Customers
WHERE Country = 'Spain' FOR XML AUTO
Kết quả như sau:
<Customers CustomerID="BOLID" ContactName="Martín Sommer" Country="Spain" />
<Customers CustomerID="FISSA" ContactName="Diego Roel" Country="Spain" />
<Customers CustomerID="GALED" ContactName="Eduardo Saavedra" Country="Spain" />
<Customers CustomerID="GODOS" ContactName="José Pedro Freyre" Country="Spain" />
<Customers CustomerID="ROMEY" ContactName="Alejandra Camino" Country="Spain" />
- 32 -
Tiếp theo, ta tìm hiểu cú pháp:
FOR XML EXPLICIT
Cú pháp này ít được dùng hơn do yêu cầu chặt chẽ về mặt cú pháp sử dụng, song đây là cú pháp có thể tạo ra được nhiều kiểu cấu trúc XML khác nhau tùy theo yêu cầu của người sử dụng. Đối với cú pháp này cần có các điều kiện sau:
- Cột đầu tiên phải ở dạng số nguyên và duy nhất đối với từng dòng dữ liệu ( thường là kiểu ID), tên của cột sẽ trở thành thẻ.
- Cột thứ hai cung cấp một số thẻ của phần tử cha, và tên cột này sẽ trở thành cha, bằng cách này các thẻ sẽ cung cấp các thông tin cho ta.
- Các cột còn lại sẽ mang các giá trị cùng với các thông tin trong tên cột và dùng để tạo ra các dữ liệu XML, và ta cũng có thể định dạng lại các dữ liệu này để có được các tài liệu XML khác nhau thông qua các chỉ thị.
Chẳng hạn, đểđưa ra được kết quả như trong ví dụ 5 ta phải làm như sau: Ví dụ 5:
SELECT P.ProductID, ProductName, UnitPrice=ROUND(P.UnitPrice, 2),
Quantity,
Discount=CONVERT(int, Discount * 100),
ExtendedPrice=ROUND(CONVERT(money, Quantity *(1 - Discount) *
P.UnitPrice), 2)
FROM Products P, [Order Details] Od
WHERE Od.ProductID = P.ProductID and Od.OrderID = 10248 FOR XML AUTO
Kết quả như sau:
<P ProductID="11" ProductName="Queso Cabrales" UnitPrice="21.0000"> <Od Quantity="12" Discount="0" ExtendedPrice="252.0000" />
</P>
<P ProductID="42" ProductName="Singaporean Hokkien Fried Mee" UnitPrice="14.0000">
<Od Quantity="10" Discount="0" ExtendedPrice="140.0000" /> </P>
<P ProductID="72" ProductName="Mozzarella di Giovanni" UnitPrice="34.8000">
<Od Quantity="5" Discount="0" ExtendedPrice="174.0000" /> </P>
- 33 -
Ngoài ra ta có thể hiển thị tài liệu XML theo cách khác thông qua việc sử dụng thêm một số các chỉ thị dịch khác, chẳng hạn chỉ thị Element biến giá trị của phần tử thành giá trị của thuộc tính:
Ví dụ 6:
SELECT 1 AS Tag, NULL AS Parent, P.ProductID AS [P!1!ProductID], P.ProductName AS [P!1!ProductName], P.UnitPrice AS [P!1!UnitPrice],
NULL AS [Od!2!Quantity], Null as [Od!2!Discount], null as [Od!2!ExtendedPrice]
FROM Products P, [Order Details] Od
WHERE Od.ProductID = P.ProductID and Od.OrderID = 10248 UNION ALL
SELECT 2 AS Tag, 1 AS Parent, P.ProductID, P.ProductName, P.UnitPrice,
Quantity, Discount=CONVERT(int, Discount * 100),
ExtendedPrice=ROUND(CONVERT(money, Quantity *(1 - Discount) *
P.UnitPrice), 2)
FROM Products P, [Order Details] Od
WHERE Od.ProductID = P.ProductID and Od.OrderID = 10248 ORDER BY [P!1!ProductID], [Od!2!Quantity]
FOR XML EXPLICIT
Kết quả như sau:
<P ProductID="11" ProductName="Queso Cabrales" UnitPrice="21.0000"> <Od Quantity="12" Discount="0" ExtendedPrice="252.0000" />
</P>
<P ProductID="42" ProductName="Singaporean Hokkien Fried Mee" UnitPrice="14.0000">
<Od Quantity="10" Discount="0" ExtendedPrice="140.0000" /> </P>
<P ProductID="72" ProductName="Mozzarella di Giovanni" UnitPrice="34.8000">
<Od Quantity="5" Discount="0" ExtendedPrice="174.0000" /> </P>
- 34 -
Ví dụ 7:
SELECT 1 AS Tag, NULL AS Parent, P.ProductID AS [P!1!ProductID], P.ProductName AS [P!1!ProductName], P.UnitPrice AS [P!1!UnitPrice],
NULL AS [Od!2!Quantity!Element], Null as
[Od!2!Discount!Element], null as [Od!2!ExtendedPrice!Element] FROM Products P, [Order Details] Od
WHERE Od.ProductID = P.ProductID and Od.OrderID = 10248 UNION ALL
SELECT 2 AS Tag, 1 AS Parent, P.ProductID, P.ProductName, P.UnitPrice,
Quantity, Discount=CONVERT(int, Discount * 100),
ExtendedPrice=ROUND(CONVERT(money, Quantity *(1 - Discount) *
P.UnitPrice), 2)
FROM Products P, [Order Details] Od
WHERE Od.ProductID = P.ProductID and Od.OrderID = 10248 ORDER BY [P!1!ProductID], [Od!2!Quantity!Element] FOR XML EXPLICIT
Kết quả như sau:
<P ProductID="11" ProductName="Queso Cabrales" UnitPrice="21.0000"> <Od> <Quantity>12</Quantity> <Discount>0</Discount> <ExtendedPrice>252.0000</ExtendedPrice> </Od> </P>
<P ProductID="42" ProductName="Singaporean Hokkien Fried Mee" UnitPrice="14.0000"> <Od> <Quantity>10</Quantity> <Discount>0</Discount> <ExtendedPrice>140.0000</ExtendedPrice> </Od> </P>
<P ProductID="72" ProductName="Mozzarella di Giovanni" UnitPrice="34.8000"> <Od> <Quantity>5</Quantity> <Discount>0</Discount> <ExtendedPrice>174.0000</ExtendedPrice> </Od> </P>
Hoặc sử dụng chỉ thị CDATA, chỉ thị này thường được sử dụng khi giá trị của phần tử có các ký tự vi phạm luật của một tài liệu XML, khi đó sử dụng chỉ thị này các công cụ sử dụng đọc tài liệu XML sẽ tự động bỏ qua các giá trị trong thẻ này:
- 35 -
Tiếp theo, ta tìm hiểu cú pháp:
FOR XML {PATH[('ElementName')]}
Khác với cú pháp FOR XML EXPLICIT, cú pháp FOR XML {PATH [(‘ElementName’)]} có thể tạo ra được nhiều kiểu cấu trúc xml khác nhau từ đơn giản đến phức tạp theo yêu cầu của người sử dụng bằng cách sử dụng bộ các quy tắc cho sẵn. Ở mức độ đơn giản, ta dễ dàng tạo được các tài liệu XML, ngoài ra có thể dùng các chỉ thị dịch khác nhau để tạo nên các tài liệu XML khác ở mức độ phức tạp hơn.
Ví dụ 8:
SELECT 1 AS Tag, NULL AS Parent, P.ProductID AS [P!1!ProductID], P.ProductName AS [P!1!ProductName!CDATA], P.UnitPrice AS
[P!1!UnitPrice],
NULL AS [Od!2!Quantity!Element], Null as [Od!2!Discount], null as [Od!2!ExtendedPrice]
FROM Products P, [Order Details] Od
WHERE Od.ProductID = P.ProductID and Od.OrderID = 10248 UNION ALL
SELECT 2 AS Tag, 1 AS Parent, P.ProductID, P.ProductName, P.UnitPrice,
Quantity, Discount=CONVERT(int, Discount * 100),
ExtendedPrice=ROUND(CONVERT(money, Quantity *(1 - Discount) *
P.UnitPrice), 2)
FROM Products P, [Order Details] Od
WHERE Od.ProductID = P.ProductID and Od.OrderID = 10248 ORDER BY [P!1!ProductID], [Od!2!Quantity!Element] FOR XML EXPLICIT
Kết quả như sau:
<P ProductID="11" UnitPrice="21.0000">
<ProductName><![CDATA[Queso Cabrales]]></ProductName> <Od Discount="0" ExtendedPrice="252.0000">
<Quantity>12</Quantity> </Od>
</P>
<P ProductID="42" UnitPrice="14.0000">
<ProductName><![CDATA[Singaporean Hokkien Fried Mee]]></ProductName> <Od Discount="0" ExtendedPrice="140.0000">
<Quantity>10</Quantity> </Od>
</P>
<P ProductID="72" UnitPrice="34.8000">
<ProductName><![CDATA[Mozzarella di Giovanni]]></ProductName> <Od Discount="0" ExtendedPrice="174.0000">
<Quantity>5</Quantity> </Od>
- 36 -
Khi ta thay đổi cú pháp thì cũng sẽ nhận được các giá trị khác nhau, chẳng hạn khi đặt tên cho cột bắt đầu bởi ký tự “@” và trong tên của cột không chứa ký tự “/” thì cột thay vì hiển thị là giá trị sẽ chuyển thành thuộc tính của phần tử trong tài liệu xml, như sau:
Ví dụ 9:
SELECT P.ProductID, ProductName, UnitPrice=ROUND(P.UnitPrice, 2),
Quantity,
Discount=CONVERT(int, Discount * 100),
ExtendedPrice=ROUND(CONVERT(money, Quantity *(1 - Discount) *
P.UnitPrice), 2)
FROM Products P, [Order Details] Od
WHERE Od.ProductID = P.ProductID and Od.OrderID = 10248 FOR XML PATH
Kết quả như sau:
<row>
<ProductID>11</ProductID>
<ProductName>Queso Cabrales</ProductName> <UnitPrice>21.0000</UnitPrice> <Quantity>12</Quantity> <Discount>0</Discount> <ExtendedPrice>252.0000</ExtendedPrice> </row> <row> <ProductID>42</ProductID>
<ProductName>Singaporean Hokkien Fried Mee</ProductName> <UnitPrice>14.0000</UnitPrice> <Quantity>10</Quantity> <Discount>0</Discount> <ExtendedPrice>140.0000</ExtendedPrice> </row> <row> <ProductID>72</ProductID>
<ProductName>Mozzarella di Giovanni</ProductName> <UnitPrice>34.8000</UnitPrice>
<Quantity>5</Quantity> <Discount>0</Discount>
<ExtendedPrice>174.0000</ExtendedPrice> </row>
- 37 -
Trường hợp tên của cột không bắt đầu bởi ký tự “@” và có chứa ký tự “/” khi đó tên của cột được hiểu như một đường dẫn tới giá trị của cột, bao gồm các phần tử cha và phần tử con có tên phân cách nhau bởi chính ký tự “/”. Để hiểu rõ hơn ta xem ví dụ sau:
Ví dụ 10:
SELECT P.ProductID "@ProductID", ProductName "@ProductName",
UnitPrice=ROUND(P.UnitPrice, 2), Quantity,
Discount=CONVERT(int, Discount * 100),
ExtendedPrice=ROUND(CONVERT(money, Quantity *(1 - Discount) *
P.UnitPrice), 2)
FROM Products P, [Order Details] Od
WHERE Od.ProductID = P.ProductID and Od.OrderID = 10248 FOR XML PATH
Kết quả như sau:
<row ProductID="11" ProductName="Queso Cabrales"> <UnitPrice>21.0000</UnitPrice>
<Quantity>12</Quantity> <Discount>0</Discount>
<ExtendedPrice>252.0000</ExtendedPrice> </row>
<row ProductID="42" ProductName="Singaporean Hokkien Fried Mee"> <UnitPrice>14.0000</UnitPrice>
<Quantity>10</Quantity> <Discount>0</Discount>
<ExtendedPrice>140.0000</ExtendedPrice> </row>
<row ProductID="72" ProductName="Mozzarella di Giovanni"> <UnitPrice>34.8000</UnitPrice>
<Quantity>5</Quantity> <Discount>0</Discount>
<ExtendedPrice>174.0000</ExtendedPrice> </row>
- 38 -
Trường hợp tên của cột có chứa ký tự “*” khi đó giá trị của cột được hiển thị trong tài liệu xml như một cột không có tên (nếu cột này không phải ở dạng xml) với nội dung là một nút. Ví dụ như sau:
Ví dụ 11:
SELECT P.ProductID "Product/ProductID", ProductName
"Product/ProductName", UnitPrice=ROUND(P.UnitPrice, 2), Quantity,
Discount=CONVERT(int, Discount * 100),
ExtendedPrice=ROUND(CONVERT(money, Quantity *(1 - Discount) *
P.UnitPrice), 2)
FROM Products P, [Order Details] Od
WHERE Od.ProductID = P.ProductID and Od.OrderID = 10248 FOR XML PATH
Kết quả như sau:
<row>
<Product>
<ProductID>11</ProductID>
<ProductName>Queso Cabrales</ProductName> </Product> <UnitPrice>21.0000</UnitPrice> <Quantity>12</Quantity> <Discount>0</Discount> <ExtendedPrice>252.0000</ExtendedPrice> </row> <row> <Product> <ProductID>42</ProductID>
<ProductName>Singaporean Hokkien Fried Mee</ProductName> </Product> <UnitPrice>14.0000</UnitPrice> <Quantity>10</Quantity> <Discount>0</Discount> <ExtendedPrice>140.0000</ExtendedPrice> </row> <row> <Product> <ProductID>72</ProductID>
<ProductName>Mozzarella di Giovanni</ProductName> </Product> <UnitPrice>34.8000</UnitPrice> <Quantity>5</Quantity> <Discount>0</Discount>