Hãy khởi đầu nhanh chóng với DB2 9 pureXML, Phần 4: Truy vấn dữ liệu DB2 XML bằng XQuery

DB2® phiên bản V9 của IBM cho Linux®, UNIX® và Windows®(R) mô tả sự hỗ trợ mới quan trọng để lưu trữ, quản lý và tìm kiếm dữ liệu XML, gọi tắt là pureXML. Loạt bài này giúp bạn nhanh chóng nắm vững các tính năng XML mới này thông qua một số bài viết đi dần từng bước giải thích cách hoàn thành các nhiệm vụ cơ bản. Trong bài viết này, hãy tìm hiểu cách truy vấn dữ liệu được lưu trữ trong các cột XML bằng cách sử dụng XQuery. [25.03.2010: Được viết lần đầu vào năm 2006, bài viết này luôn được cập nhật để theo kịp với các thay đổi trong phiên bản 9.5 và 9.7 của DB2 -- Người hiệu đính.]

Don Chamberlin, Tác giả chuyên nghiệp, IBM's Almaden Research Center

Don Chamberlin, một thành viên IBM tại Trung tâm nghiên cứu Almaden, là một trong những đại diện của IBM trong Nhóm làm việc về truy vấn XML của W3C. Ông cũng là đồng tác giả của đề xuất ngôn ngữ Quilt, đã tạo nên cơ sở cho thiết kế XQuery. Don được biết đến như là đồng phát minh về ngôn ngữ cơ sở dữ liệu SQL và là tác giả của hai cuốn sách về hệ thống cơ sở dữ liệu DB2. Ông có bằng Cử nhân khoa học của trường cao đẳng Harvey Mudd và bằng Tiến sĩ của Đại học Stanford. Ông cũng là một thành viên ACM và là thành viên của Học viện Kỹ thuật Quốc gia.



Cynthia M. Saracco, Kiến trúc giải pháp cao cấp, IBM

Cynthia M. Saracco là kiến trúc sư giải pháp cao cấp tại Phòng thí nghiệm ở Thung lũng Silicon của IBM, bà chuyên về các công nghệ mới xuất hiện và các chủ đề quản lý cơ sở dữ liệu. Bà có 23 năm kinh nghiệm về công nghiệp phần mềm, đã viết 3 cuốn sách và hơn 60 tài liệu kỹ thuật và có 7 bằng sáng chế.



25 03 2010 (Xuất bản lần đầu tiên vào ngày 10 08 2011)

Bạn đã có thể nghe thấy tin đồn về phiên bản V9 của DB2 -- hệ thống quản lý cơ sở dữ liệu đầu tiên của IBM có hỗ trợ cả hai cấu trúc dữ liệu (dựa vào-SQL) dạng bảng và cấu trúc dữ liệu (dựa vào-XML) phân cấp. Các bài viết trước của loạt bài này đã tóm tắt các tính năng XML mới của DB2, đã mô tả cách tạo các đối tượng cơ sở dữ liệu và điền chúng với dữ liệu XML và giải thích cách làm việc với dữ liệu XML bằng cách sử dụng SQL và SQL/XML. Bài viết này tiếp tục tìm hiểu các khả năng của XML của DB2 bằng cách tập trung vào sự hỗ trợ mới của nó cho XQuery.

DB2 xử lý XQuery như một ngôn ngữ lớp đầu tiên, cho phép những người dùng viết các biểu thức XQuery trực tiếp chứ không yêu cầu những người dùng đó nhúng hoặc bao bọc các XQuery trong các câu lệnh SQL. Hơn nữa, máy truy vấn của DB2 xử lý các XQuery theo cách tự nhiên, nghĩa là nó phân tích cú pháp, đánh giá và tối ưu hóa các XQuery mà không bao giờ chuyển dịch chúng thành SQL sau hậu trường. Tất nhiên, nếu bạn chọn viết các truy vấn song ngữ bao gồm cả các biểu thức XQuery lẫn SQL, thì DB2 cũng sẽ xử lý và tối ưu hóa các truy vấn này.

Như với SQL/XML trong Phần 3 của loạt bài này, bài viết này tìm hiểu một số nhiệm vụ truy vấn chung và xem xét cách bạn có thể sử dụng XQuery để hoàn thành các mục tiêu của mình. Đầu tiên, hãy xem xét nhanh XQuery khác với SQL như thế nào.

Giới thiệu về XQuery

XQuery khác với SQL ở một số khía cạnh chính, phần lớn là do các ngôn ngữ được thiết kế để làm việc với các mô hình dữ liệu khác nhau có các đặc điểm khác nhau. Các tài liệu XML chứa các hệ thống phân cấp và sở hữu một trật tự vốn có. Các cấu trúc dữ liệu dạng bảng được các DBMS dựa trên SQL hỗ trợ là phẳng và dựa vào thiết lập. Như vậy, các hàng không có thứ tự.

Những khác biệt giữa các mô hình dữ liệu này sinh ra một số các khác biệt cơ bản trong các ngôn ngữ truy vấn riêng của chúng, bao gồm:

  • XQuery hỗ trợ các biểu thức đường dẫn để cho phép các lập trình viên chuyển hướng thông qua cấu trúc phân cấp của XML, trong khi SQL thuần (không có các phần mở rộng XML) thì không.
  • XQuery hỗ trợ cả hai dữ liệu được định kiểu và không được định kiểu, trong khi dữ liệu SQL luôn luôn được định nghĩa với một kiểu cụ thể.
  • XQuery thiếu các giá trị bằng không (null) vì các tài liệu XML bỏ qua dữ liệu còn thiếu hoặc chưa rõ. Tất nhiên, SQL sử dụng các giá trị bằng không để biểu diễn các giá trị dữ liệu còn thiếu hoặc chưa rõ.
  • XQuery trả về các chuỗi dữ liệu XML, trong khi SQL trả về các tập kết quả của các kiểu dữ liệu SQL khác nhau.

Đây là một tập hợp con về những khác biệt cơ bản giữa XQuery và SQL. Việc cung cấp một danh sách đầy đủ nằm ngoài phạm vi của bài viết giới thiệu này, nhưng một bài viết của Tạp chí Các hệ thống IBM đã bàn luận về các khác biệt ngôn ngữ cụ thể hơn. Bài viết này tập trung vào một số khía cạnh cơ bản của ngôn ngữ XQuery và cách bạn có thể sử dụng nó để truy vấn dữ liệu XML trong DB2 V9.


Cơ sở dữ liệu mẫu

Các truy vấn trong bài viết này truy cập các bảng mẫu được tạo ra trong Phần 1 của loạt bài này. Để xem lại nhanh, Liệt kê 1 định nghĩa các bảng items (các mặt hàng) và clients (các khách hàng) mẫu.

Liệt kê 1. Các định nghĩa bảng
create table items (
id 		int primary key not null, 
brandname 	varchar(30), 
itemname 	varchar(30), 
sku 		int, 
srp 		decimal(7,2), 
comments 	xml
)

create table clients(
id 		int primary key not null, 
name 		varchar(50), 
status 		varchar(10), 
contactinfo 	xml
)

Dữ liệu XML mẫu có trong cột items.comments được hiển thị trong Liệt kê 2, trong khi mẫu dữ liệu XML có trong cột clients.contactinfo được hiển thị trong Liệt kê 3. Các ví dụ truy vấn tiếp theo sẽ tham chiếu các phần tử cụ thể trong một hoặc cả hai tài liệu XML này.

Liệt kê 2. Tài liệu mẫu XML được lưu trữ trong cột Comments của bảng Items
<Comments>
	<Comment>
		<CommentID>133</CommentID>
		<ProductID>3926</ProductID>
		<CustomerID>8877</CustomerID>
		<Message>Heels on shoes wear out too quickly.</Message>
		<ResponseRequested>No</ResponseRequested>
	</Comment>
	<Comment>
		<CommentID>514</CommentID>
		<ProductID>3926</ProductID>
		<CustomerID>3227</CustomerID>
		<Message>Where can I find a supplier in San Jose?</Message>
		<ResponseRequested>Yes</ResponseRequested>
	</Comment>
</Comments>
Liệt kê 3. Tài liệu mẫu XML được lưu trữ trong cột Contactinfo của bảng Clients
<Client>
	<Address>
		<street>5401 Julio Ave.</street>
		<city>San Jose</city>
		<state>CA</state>
		<zip>95116</zip>
	</Address>
	<phone>
		<work>4084630000</work>
		<home>4081111111</home>
		<cell>4082222222</cell>
	</phone>
	<fax>4087776666</fax>
	<email>love2shop@yahoo.com</email>
</Client>

Môi trường truy vấn

Thiết kế tất cả các truy vấn trong bài viết này được ban hành tương tác với nhau. Bạn có thể làm điều này thông qua bộ xử lý dòng lệnh DB2 hoặc Trình soạn thảo lệnh của DB2 (DB2 Command Editor) của Trung tâm điều khiển DB2 (DB2 Control Center). Các ảnh màn hình và các tập lệnh trong bài viết này tập trung vào cái sau. (IBM Data Studio và IBM Optim Development Studio cũng đi kèm với một bàn làm việc của nhà phát triển dựa trên Eclipse có thể giúp các lập trình viên xây dựng các truy vấn bằng đồ họa. Tuy nhiên, bài viết này không bàn về các vấn đề phát triển ứng dụng hoặc Development Studio).

Để sử dụng DB2 Command Editor, hãy khởi chạy Control Center và chọn Tools > Command Editor. Một cửa sổ tương tự như Hình 1 sẽ xuất hiện.

Hình 1. DB2 Command Editor, có thể được khởi chạy từ DB2 Control Center
Cho thấy một truy vấn trong ô trên cùng và một bản ghi các lệnh được nhập vào ô dưới cùng

Gõ các truy vấn của bạn vào ô bên trên, nhấn vào mũi tên màu xanh lá cây ở góc trên bên trái để chạy chúng và xem kết quả của bạn ở ô bên dưới hoặc trong thẻ Query results (Các kết quả truy vấn) riêng.


Các ví dụ XQuery

Cũng như trong Phần 3 của loạt bài này, bài viết này đi dần từng bước qua một số các kịch bản nghiệp vụ chung và cho thấy cách sử dụng XQuery để đáp ứng các yêu cầu về dữ liệu XML. Nó cũng tìm hiểu nhiều tình huống phức tạp có liên quan đến việc nhúng SQL trong XQuery.

XQuery cung cấp nhiều loại biểu thức khác nhau, có thể được kết hợp theo bất kỳ cách nào mà bạn muốn. Mỗi biểu thức trả về một danh sách các giá trị, có thể được sử dụng làm đầu vào cho các biểu thức khác. Kết quả của biểu thức ngoài cùng là kết quả của truy vấn đó.

Bài viết này tập trung vào hai loại biểu thức XQuery quan trọng: các biểu thức FLWOR và các biểu thức đường dẫn. Một biểu thức FLWOR tựa như một biểu thức SELECT-FROM-WHERE trong SQL: nó được dùng để lặp lại qua một danh sách các mặt hàng và để trả về theo tùy chọn cái gì đó được tính toán từ mỗi mặt hàng. Mặt khác, một biểu thức đường dẫn chuyển hướng qua một hệ thống phân cấp các phần tử XML và trả về các phần tử được tìm thấy ở cuối đường dẫn.

Giống như một biểu thức SELECT-FROM-WHERE trong SQL, một biểu thức XQuery FLWOR có thể chứa một số mệnh đề bắt đầu bằng các từ khoá cụ thể. Các từ khoá sau đây được sử dụng để bắt đầu các mệnh đề trong một biểu thức FLWOR:

  • for: Lặp lại qua một chuỗi đầu vào, kết hợp một biến với lần lượt từng mặt hàng đầu vào.
  • let: Khai báo một biến và gán cho nó một giá trị, có thể là một danh sách chứa nhiều mặt hàng.
  • where: Quy định các tiêu chuẩn để lọc các kết quả truy vấn.
  • order by: Quy định thứ tự sắp xếp của kết quả.
  • return: Định nghĩa kết quả được trả về.

Một biểu thức đường dẫn trong XQuery gồm có một loạt các bước, được phân cách bằng các ký tự gạch chéo. Theo dạng đơn giản nhất, mỗi bước chuyển hướng đi xuống trong một hệ thống phân cấp XML để tìm phần tử con của các phần tử được bước trước đó trả về. Mỗi bước trong một biểu thức đường dẫn cũng có thể chứa một biến vị ngữ lọc các phần tử được bước đó trả về, chỉ giữ lại các phần tử đáp ứng một số điều kiện. Ví dụ, giả sử rằng biến $clients liên kết với một danh sách các tài liệu XML có chứa các phần tử <Client>, biểu thức đường dẫn bốn bước $clients/Client/Address[state = "CA"]/zip sẽ trả về danh sách các mã vùng bưu điện cho các khách hàng có địa chỉ ở California.

Trong nhiều trường hợp, có thể viết một câu truy vấn bằng cách sử dụng hoặc một biểu thức FLWOR hoặc một biểu thức một đường dẫn.

Sử dụng XQuery của DB2 như một ngôn ngữ truy vấn cao cấp

Để thực hiện trực tiếp một XQuery trong DB2 V9 (trái với việc nhúng nó vào một câu lệnh SQL), bạn phải bắt đầu truy vấn bằng từ khóa xquery. Từ khóa này ra lệnh cho DB2 gọi trình phân tích cú pháp XQuery của nó để xử lý yêu cầu của bạn. Lưu ý rằng bạn chỉ cần làm điều này khi bạn đang sử dụng XQuery như ngôn ngữ ngoài cùng (hoặc mức cao nhất). Nếu bạn đã nhúng biểu thức XQuery trong SQL, bạn không cần bắt đầu với từ khóa xquery. Tuy nhiên, bài viết này sử dụng XQuery như là ngôn ngữ chính, vì vậy tất cả các truy vấn đều được bắt đầu bằng xquery.

Khi chạy như một ngôn ngữ mức cao, XQuery cần phải có một nguồn dữ liệu đầu vào. Một cách để một XQuery có thể nhận được dữ liệu đầu vào là gọi một hàm tên là db2-fn:xmlcolumn với một tham số, nhận biết tên bảng và tên cột của một cột XML trong một bảng DB2. Hàm db2-fn:xmlcolumn trả về chuỗi các tài liệu XML, được lưu trữ trong cột cụ thể. Ví dụ, truy vấn sau đây trong Liệt kê 4 trả về một chuỗi các tài liệu XML có chứa thông tin liên hệ của khách hàng.

Liệt kê 4. XQuery đơn giản để trả về dữ liệu liên hệ của khách hàng
xquery db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')

Như bạn có thể nhớ lại trong lược đồ cơ sở dữ liệu của chúng ta (xem phần "Cơ sở dữ liệu mẫu"), các tài liệu XML được lưu trữ trong cột Contactinfo của bảng Clients. Lưu ý rằng ở đây các tên cột và bảng được quy định theo dạng chữ hoa. Điều này là do các tên bảng và cột thường được chuyển thành chữ hoa trước khi được ghi vào danh mục bên trong của DB2. Vì XQuery là trường hợp có phân biệt chữ hoa, chữ thường, nên các tên bảng và tên cột chữ thường sẽ không khớp với các tên chữ hoa trong danh mục DB2.

Lấy ra các phần tử XML cụ thể

Bây giờ, hãy tìm hiểu một nhiệm vụ cơ bản. Giả sử bạn muốn lấy ra các số fax của tất cả các khách hàng đã cung cấp cho bạn thông tin này. Liệt kê 5 chỉ ra một cách để bạn có thể viết truy vấn này.

Liệt kê 5. Biểu thức FLWOR để lấy ra dữ liệu fax của khách hàng
xquery 
for $y in db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/fax
return $y

Dòng đầu tiên chỉ thị cho DB2 gọi trình phân tích cú phápXQuery của nó. Dòng tiếp theo chỉ thị cho DB2 lặp lại qua các phần tử con fax của các phần tử Client đã chứa trong cột CLIENTS.CONTACTINFO. Mỗi phần tử fax lần lượt được liên kết với biến $y. Dòng thứ ba cho biết, với mỗi lần lặp lại, giá trị $y được trả về. Kết quả là một chuỗi các phần tử XML, như trong Liệt kê 6.

Liệt kê 6. Kết quả đầu ra mẫu cho truy vấn trước đó
<fax>4081112222</fax>
<fax>5559998888</fax>

Một nhận xét ngẫu nhiên là, kết quả đầu ra cũng sẽ có một số thông tin không phải là mối quan tâm lớn trong bài viết này: phiên bản và dữ liệu mã hóa XML, chẳng hạn như <?xml version="1.0" encoding="windows-1252" ?>, và thông tin về không gian tên XML, chẳng hạn như <fax xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">. Để giúp bạn dễ dàng làm theo kết quả đầu ra hơn, thông tin đó được bỏ ra khỏi bài viết này. Tuy nhiên, nó có thể lại quan trọng đối với một số ứng dụng XML. Nếu bạn sử dụng bộ xử lý dòng lệnh DB2 để chạy các truy vấn của mình, bạn có thể sử dụng tùy chọn -d để chặn thông tin khai báo XML và chọn tùy chọn -i để in các kết quả theo cách dễ nhìn.

Truy vấn được hiển thị trong Liệt kê 5 có thể được biểu diễn có phần chính xác như một biểu thức đường dẫn ba bước, như trong Liệt kê 7.

Liệt kê 7. Biểu thức đường dẫn để lấy ra dữ liệu fax của khách hàng
xquery 
db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/fax

Bước đầu tiên của biểu thức đường dẫn gọi hàm db2-fn:xmlcolumn để nhận được một danh sách các tài liệu XML trong cột CONTACTINFO của bảng CLIENTS. Bước thứ hai trả về tất cả các phần tử Client trong các tài liệu này và bước thứ ba trả về các phần tử fax được lồng bên trong các phần tử Client này.

Nếu bạn không quan tâm đến việc nhận được các đoạn XML từ truy vấn của bạn nhưng thay vì chỉ muốn một biểu diễn văn bản của các giá trị phần tử XML có đủ điều kiện, bạn có thể gọi hàm text() trong mệnh đề return của bạn, như trong Liệt kê 8.

Liệt kê 8. Hai truy vấn để lấy ra biểu diễn văn bản của dữ liệu fax của khách hàng
xquery 
for $y in db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/fax
return $y/text()

(hay)

xquery
db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/fax/text()

Kết quả của các truy vấn này sẽ tương tự như kết quả trong Liệt kê 9.

Liệt kê 9. Kết quả đầu ra mẫu từ các truy vấn trước đây
4081112222
5559998888

Các kết quả của các truy vấn mẫu tương đối đơn giản vì phần tử fax được dựa trên một kiểu dữ liệu nguyên thủy. Tất nhiên, các phần tử có thể dựa trên các kiểu phức tạp, có nghĩa là chúng có thể chứa các phần tử con (hoặc các hệ thống phân cấp lồng nhau). Phần tử Address (Địa chỉ) của thông tin liên hệ khách hàng của chúng ta là một ví dụ về điều này. Theo lược đồ được định nghĩa trong Phần 2 của loạt bài này, nó có thể chứa một địa chỉ đường phố, số căn hộ, thành phố, tiểu bang và mã vùng bưu điện. Hãy xem Query trong Liệt kê 10 sẽ trả về cái gì.

Liệt kê 10. Biểu thức FLWOR để lấy ra kiểu XML phức tạp
xquery 
for $y in db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/Address
return $y

Nếu bạn đã chắc rằng một chuỗi các đoạn XML có chứa các phần tử Address và tất cả các phần tử con của chúng, thì bạn đã đúng. Liệt kê 11 là một ví dụ:

Liệt kê 11. Kết quả đầu ra mẫu từ truy vấn trước đó
<Address>
  <street>5401 Julio Ave.</street>
  <city>San Jose</city>
  <state>CA</state>
  <zip>95116</zip>
</Address>
. . . 
<Address>
  <street>1204 Meridian Ave.</street>
  <apt>4A</apt>
  <city>San Jose</city>
  <state>CA</state>
  <zip>95124</zip>
</Address>

Lưu ý: Kết quả đầu ra mẫu được định dạng để làm cho bạn dễ đọc hơn. DB2 Command Editor sẽ hiển thị từng bản ghi địa chỉ của khách hàng trên một dòng.

Lọc các giá trị phần tử XML

Bạn có thể cải thiện các ví dụ XQuery trước đây để có tính chọn lọc hơn nữa. Ví dụ, hãy xem xét cách trả về các địa chỉ gửi thư của tất cả các khách hàng đang sống trong vùng có mã vùng bưu điện của Mỹ là 95116.

Như bạn có thể tưởng tượng, mệnh đề where của XQuery cho phép bạn lọc các kết quả dựa trên giá trị của phần tử zip (mã vùng bưu điện) trong các tài liệu XML của bạn. Liệt kê 12 cho thấy cách thêm một mệnh đề where vào biểu thức FLWOR trước đó trong Liệt kê 10 để nhận được chỉ các địa chỉ mà bạn quan tâm.

Liệt kê 12. Biểu thức FLWOR với một mệnh đề "where" mới
xquery 
for $y in db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/Address
where $y/zip="95116"
return $y

Mệnh đề where được bổ sung khá dễ hiểu. Mệnh đề for liên kết biến $y với lần lượt từng địa chỉ. Mệnh đề where chứa một biểu thức đường dẫn nhỏ chuyển hướng từ mỗi địa chỉ đến phần tử zip lồng nhau của nó. Mệnh đề where là đúng (và địa chỉ đó được giữ lại) chỉ khi giá trị của phần tử zip này bằng 95116.

Có thể nhận được kết quả tương tự bằng cách thêm một biến vị ngữ vào biểu thức đường dẫn, như trong Liệt kê 13.

Liệt kê 13. Biểu thức đường dẫn với biến vị ngữ lọc bổ sung
xquery
db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/Address[zip="95116"]

Tất nhiên, bạn có thể lọc các giá trị mã vùng bưu điện và trả về các phần tử không liên quan đến các địa chỉ đường phố. Hơn nữa, bạn cũng có thể lọc nhiều giá trị phần tử XML trong một truy vấn đơn. Truy vấn trong Liệt kê 14 trả về thông tin e-mail cho các khách hàng sống trong vùng có một mã vùng bưu điện cụ thể trong thành phố New York (10011) hoặc bất cứ nơi nào ở thành phố San Jose.

Liệt kê 14. Lọc nhiều giá trị phần tử XML với một biểu thức FLWOR
xquery 
for $y in db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client
where $y/Address/zip="10011" or $y/Address/city="San Jose"
return $y/email

Lưu ý rằng mệnh đề for được thay đổi để nó liên kết với biến $y với các phần tử Client chứ không phải với các phần tử Address. Mệnh đề này cho phép bạn lọc các phần tử Client bằng một phần của cây con (Địa chỉ) và trả về một phần khác của cây con (email). Các biểu thức đường dẫn trong mệnh đề where và mệnh đề return phải được viết liên quan với phần tử có liên kết với biến đó (trong trường hợp này là, $y).

Có thể thể hiện truy vấn tương tự chính xác hơn một chút như một biểu thức đường dẫn, như trong Liệt kê 15.

Liệt kê 15. Lọc nhiều giá trị phần tử XML bằng một biểu thức đường dẫn
xquery 
db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client[Address/zip="10011"  
or Address/city="San Jose"]/email;

Điều không rõ ràng khi xem xét lại cả hai dạng truy vấn này là các kết quả được trả về sẽ khác với hai cách đáng kể so với những gì mà một lập trình viên SQL có thể mong đợi:

  1. Bạn sẽ không nhận dữ liệu XML được trả về cho các khách hàng có đủ điều kiện vẫn chưa cung cấp cho bạn các địa chỉ e-mail của họ. Nói cách khác, nếu bạn có 1000 khách hàng sống ở San Jose hoặc trong vùng có mã vùng bưu điện là 10011 và 700 khách hàng mỗi người cung cấp cho bạn một địa chỉ e-mail, bạn sẽ nhận được một danh sách 700 địa chỉ e-mail được trả về. Điều này là do sự khác biệt cơ bản giữa XQuery và SQL đã nói ở trên: XQuery không sử dụng các giá trị bằng không.
  2. Bạn sẽ không biết các địa chỉ e-mail nào đã được bắt nguồn từ cùng một tài liệu XML. Nói cách khác, nếu bạn có 700 khách hàng đang sống ở San Jose hoặc trong vùng có mã vùng bưu điện là 10011 và mỗi người đã cung cấp cho bạn hai địa chỉ e-mail, thì bạn sẽ nhận được một danh sách có 1400 phần tử email được trả về. Bạn sẽ không nhận được một 700 bản ghi liên tiếp nhau, mà mỗi bản ghi có hai địa chỉ e-mail.

Cả hai tình huống có thể là mong muốn trong một số trường hợp và là không mong muốn trong những trường hợp khác. Ví dụ, nếu bạn cần gửi e-mail thông báo đến mọi tài khoản có đủ điều kiện mà bạn có trong bản ghi, thì lặp lại qua một danh sách các địa chỉ e-mail của khách hàng theo định dạng XML là cách dễ dàng để làm trong một ứng dụng. Tuy nhiên, nếu bạn muốn gửi e-mail chỉ có một thông báo đến mọi khách hàng, bao gồm cả những người đã chỉ cung cấp cho bạn các địa chỉ đường phố của họ, thì các XQuery đã hiển thị trước đây sẽ không đủ.

Có nhiều cách để bạn có thể viết lại truy vấn này sao cho các kết quả trả về biểu thị thông tin còn thiếu trong một số mẫu và cho biết khi nhiều địa chỉ e-mail đã được bắt nguồn từ cùng một bản ghi của khách hàng, đó là, cùng một tài liệu XML (nói thêm về điều này sau). Tuy nhiên, nếu tất cả mọi thứ bạn muốn làm là lấy ra một danh sách có chứa một địa chỉ e-mail cho mỗi khách hàng có đủ điều kiện, thì bạn có thể sửa đổi mệnh đề return của truy vấn trước đó một chút.

Liệt kê 16. Chỉ lấy ra phần tử email đầu tiên cho mỗi khách hàng
xquery 
for $y in db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client
where $y/Address/zip="10011" or $y/Address/city="San Jose"
return $y/email[1]

Truy vấn này khiến DB2 trả về phần tử e-mail đầu tiên mà nó tìm thấy trong mỗi tài liệu XML có đủ điều kiện (bản ghi liên hệ khách hàng). Nếu nó không tìm thấy một địa chỉ e-mail cho một khách hàng có đủ điều kiện, nó sẽ không trả về bất kỳ điều gì cho khách hàng đó.

Chuyển đổi kết quả đầu ra XML

Một khía cạnh mạnh mẽ của XQuery là khả năng biến đổi đầu ra XML từ một dạng XML sang một dạng khác. Ví dụ, bạn có thể sử dụng XQuery để lấy tất cả hoặc một phần tài liệu XML đã lưu của bạn và chuyển đổi kết quả đầu ra thành HTML cho dễ hiển thị trong một trình duyệt Web. Truy vấn trong Liệt kê 17 lấy các địa chỉ của các khách hàng, sắp xếp các kết quả theo mã vùng bưu điện và chuyển đổi kết quả đầu ra thành các phần tử XML, là một phần của một danh sách HTML không theo thứ tự.

Liệt kê 17. Truy vấn dữ liệu XML của DB2 và trả về các kết quả dưới dạng HTML
xquery 
<ul> {
for $y in db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client/Address
order by $y/zip
return <li>{$y}</li> 
} </ul>

Truy vấn có thể được mổ xẻ theo cách sau:

  • Truy vấn bắt đầu đủ đơn giản với từ khóa xquery để cho biết trình phân tích cú pháp DB2 mà XQuery đang sử dụng làm ngôn ngữ chính.
  • Dòng thứ hai làm cho việc đánh dấu HTML với một danh sách không theo thứ tự (<ul>) được bao gồm trong các kết quả. Nó cũng đưa vào một dấu ngoặc nhọn, tập thứ nhất trong hai tập được sử dụng trong truy vấn này. Các dấu ngoặc nhọn ra lệnh cho DB2 đánh giá và xử lý biểu thức kèm theo chứ không xử lý nó như là một chuỗi bằng chữ.
  • Dòng thứ ba lặp lại qua các địa chỉ khách hàng, liên kết biến $y với lần lượt từng phần tử địa chỉ.
  • Dòng thứ tư bao gồm một mệnh đề order by mới, quy định rằng các kết quả phải được trả về theo thứ tự tăng dần (thứ tự mặc định) dựa trên các mã vùng bưu điện của khách hàng (phần tử con zip của mỗi địa chỉ được liên kết với $y).
  • Mệnh đề return cho biết rằng các phần tử Address sẽ được bao quanh bởi các thẻ mục danh sách HTML trước khi trả về.
  • Dòng cuối cùng kết thúc truy vấn và hoàn thành thẻ danh sách không theo thứ tự HTML.

Kết quả đầu ra xuất hiện tương tự như trong Liệt kê 18.

Liệt kê 18. Kết quả đầu ra HTML mẫu của truy vấn trước đó
<ul>
  <li>
     <Address>
         <street>9407 Los Gatos Blvd.</street>
         <city>Los Gatos</city>
         <state>CA</state>
         <zip>95032</zip>
     </Address>
  </li>
  <li>
     <Address>
         <street>4209 El Camino Real</street>
         <city>Mountain View</city>
         <state>CA</state>
        <zip>95033</zip>
     </Address>
  </li>
. . . 
</ul>

Bây giờ hãy xem xét một chủ đề đã nêu lên trước đó: cách viết một XQuery, sẽ chỉ ra các giá trị còn thiếu trong các kết quả trả về cũng như cho biết khi nào một tài liệu XML đơn (chẳng hạn như một bản ghi khách hàng duy nhất) có chứa các phần tử lặp lại (ví dụ như nhiều địa chỉ email). Một cách để làm điều đó liên quan đến việc bao bọc kết quả được trả về trong một phần tử XML mới, như trong truy vấn trong Liệt kê 19:

Liệt kê 19. Chỉ ra các giá trị còn thiếu và lặp lại các phần tử trong một kết quả XQuery
xquery 
for $y in db2-fn:xmlcolumn('CLIENTS.CONTACTINFO')/Client
where $y/Address[zip="10011"] or $y/Address[city="San Jose"]
return <emailList> {$y/email} </emailList>

Chạy truy vấn này làm cho một chuỗi các phần tử emailList được trả về, một phần tử cho mỗi bản ghi khách hàng có đủ điều kiện. Mỗi phần tử emailList chứa dữ liệu e-mail. Nếu DB2 tìm thấy một địa chỉ e-mail duy nhất trong hồ sơ của khách hàng, nó trả về phần tử đó và giá trị của nó. Nếu nó tìm thấy nhiều địa chỉ e-mail, nó sẽ trả về tất cả các phần tử e-mail và các giá trị của chúng. Cuối cùng, nếu nó không tìm thấy bất kỳ địa chỉ e-mail nào, nó sẽ trả về một phần tử emailList rỗng. Do đó, kết quả đầu ra có thể xuất hiện như thể hiện trong Liệt kê 20.

Liệt kê 20. Kết quả đầu ra mẫu của truy vấn trước đó
<emailList>
   <email>love2shop@yahoo.com</email>
</emailList>
<emailList/>
<emailList>
   <email>beatlesfan36@hotmail.com</email>
   <email>lennonfan36@hotmail.com</email>
</emailList>       
. . .

Sử dụng logic điều kiện

Khả năng của XQuery để chuyển đổi đầu ra XML có thể được kết hợp với sự hỗ trợ có sẵn của nó cho logic điều kiện để giảm bớt sự phức tạp của mã ứng dụng. Hãy xem xét một ví dụ đơn giản. Bảng Items (Các mặt hàng) bao gồm một cột XML chứa các ý kiến mà các khách hàng đã đóng góp về các sản phẩm. Đối với các khách hàng đã yêu cầu trả lời lại ý kiến của họ, bạn có thể cần tạo các phần tử Action (Hoạt động) mới có chứa mã định danh sản phẩm, mã định danh khách hàng và thông báo để cho bạn có thể định tuyến thông tin này đến những người thích hợp để xử lý. Tuy nhiên, các ý kiến mà không yêu cầu câu trả lời vẫn quan trọng cho nghiệp vụ và bạn không muốn bỏ qua chúng. Thay vào đó, hãy tạo một phần tử Info (Thông tin) với chỉ mã định danh và thông báo sản phẩm. Đây là cách bạn có thể sử dụng một biểu thức XQuery if-then-else để hoàn thành nhiệm vụ này:

Liệt kê 21. Sử dụng một biểu thức "if-then-else" trong một XQuery
xquery 
for $y in db2-fn:xmlcolumn('ITEMS.COMMENTS')/Comments/Comment 
return ( 
	if ($y/ResponseRequested = 'Yes') 
		then <action>
			{$y/ProductID, 
			 $y/CustomerID, 
			 $y/Message}
		      </action>
		else ( <info>
			{$y/ProductID, 
			 $y/Message}
			</info>
		)
)

Bây giờ hầu hết các khía cạnh của truy vấn này sẽ quen thuộc với bạn, do đó, hãy tập trung vào logic điều kiện. Mệnh đề if xác định xem giá trị của phần tử con ResponseRequested cho một ý kiến cụ thể có bằng Yes không. Nếu đúng, mệnh đề then được đánh giá, làm cho DB2 trả về một phần tử mới (action) có chứa ba phần tử con: ProductID, CustomerID và thông bokáo. Nếu không, mệnh đề else được đánh giá và DB2 trả về một phần tử Info có chứa chỉ dữ liệu mã định danh sản phẩm và thông báo.

Sử dụng mệnh đề let

Bạn đã thấy cách sử dụng tất cả các phần của một biểu thức FLWOR trừ mệnh đề let (cho phép). Mệnh đề này được sử dụng để gán một giá trị (có thể chứa một danh sách của một số mặt hàng) cho một biến có thể được sử dụng trong các mệnh đề khác của biểu thức FLWOR.

Giả sử bạn muốn tạo một danh sách có bao nhiêu ý kiến đã nhận được cho mỗi sản phẩm. Điều này có thể được thực hiện bằng truy vấn trong Liệt kê 22.

Liệt kê 22. Sử dụng mệnh đề "let"
xquery
for $p in distinct-values
     (db2-fn:xmlcolumn('ITEMS.COMMENTS')/Comments/Comment/ProductID)
let $pc := db2-fn:xmlcolumn('ITEMS.COMMENTS')
        /Comments/Comment[ProductID = $p]
return
   <product>
          <id> { $p } </id> 
          <comments> { count($pc) } </comments>
   </product>

Hàm distinct-values trong mệnh đề for trả về một danh sách tất cả các giá trị khác biệt của ProductID có trong các ý kiến trong cột Comments của bảng Items. Mệnh đề for liên kết biến $p với lần lượt một trong các giá trị ProductID này. Với mỗi giá trị $p, mệnh đề let quét lại cột Items và liên kết biến $pc với một danh sách chứa tất cả các ý kiến mà ProductID của chúng khớp với ProductID trong $p. Mệnh đề return dựng một phần tử sản phẩm mới cho mỗi giá trị ProductID khác biệt. Mỗi một trong các phần tử sản phẩm này chứa hai phần tử con: một phần tử id có chứa giá trị ProductID và một phần tử các ý kiến có chứa tổng số có bao nhiêu ý kiến đã nhận được với sản phẩm cụ thể đó.

Kết quả của truy vấn ví dụ này có thể trông tương tự như Liệt kê 23.

Liệt kê 23. Kết quả đầu ra mẫu cho truy vấn trước đó
<product>
     <id>3926</id>
     <comments>28</comments>
</product>
<product>
      <id>4097</id>
      <comments>13</comments>
</product>

XQueries với SQL nhúng

Bây giờ, bạn đã thấy cách viết XQueries lấy ra các đoạn tài liệu XML, tạo các dạng kết quả đầu ra XML mới và trả về kết quả đầu ra khác nhau dựa trên các điều kiện đã quy định trong chính các truy vấn đó. Tóm lại, bạn đã tìm hiểu một vài cách sử dụng XQuery để truy vấn dữ liệu XML được lưu trong DB2.

Để chắc chắn, có nhiều cách để tìm hiểu về XQuery hơn cách mà bài viết ngắn gọn này trình bày. Nhưng có một chủ đề rộng hơn vẫn chưa được nêu ra: cách nhúng SQL trong XQuery. Làm như vậy có thể có ích nếu bạn cần viết các truy vấn lọc dữ liệu dựa trên các giá trị cột XML và không XML.

Bạn có thể nhớ lại trong Phần 3 của loạt bài này về cách nhúng các biểu thức XQuery đơn giản trong một câu lệnh SQL để thực hiện nhiệm vụ này. Ở đây, hãy xem xét cách làm ngược lại: SQL được nhúng trong XQuery để hạn chế các kết quả dựa trên cả các giá trị dữ liệu SQL truyền thống lẫn các giá trị phần tử XML cụ thể.

Để thay thế cho hàm db2-fn:xmlcolumn, trả về tất cả dữ liệu XML trong một cột của một bảng, bạn có thể gọi hàm db2-fn:sqlquery, thực hiện một truy vấn SQL và chỉ trả về dữ liệu đã chọn. Truy vấn SQL đã chuyển sang db2-fn:sqlquery phải trả về dữ liệu XML. Sau đó dữ liệu XML này có thể được XQuery xử lý thêm nữa.

Truy vấn trong Liệt kê 24 lấy thông tin về các ý kiến liên quan đến các sản phẩm có giá bán lẻ đề xuất (SRP) lớn hơn $100 USD, có một yêu cầu khách hàng trả lời. Hãy nhớ rằng dữ liệu giá được lưu trữ trong một cột thập phân SQL, trong khi các ý kiến của khách hàng được lưu trữ như XML. Dữ liệu được trả về, bao gồm mã định danh sản phẩm, mã định danh khách hàng và thông báo khách hàng, được bao gồm trong một phần tử hành động XML đơn cho mỗi ý kiến có đủ điều kiện được lưu trong cơ sở dữ liệu.

Liệt kê 24. Nhúng SQL trong một XQuery
xquery 
for $y in 
db2-fn:sqlquery('select comments from items where srp > 100')/Comments/Comment 
where $y/ResponseRequested="Yes"
return (
   <action>
          {$y/ProductID, 
           $y/CustomerID, 
           $y/Message}
  </action>
)

Bây giờ hầu hết truy vấn này trông quen thuộc với bạn, do đó hãy xem xét hàm mới: db2-fn:sqlquery. DB2 xử lý câu lệnh SQL SELECT được cung cấp cho hàm này để xác định các hàng nào chứa thông tin về các mặt hàng có giá cao hơn $ 100 USD. Các tài liệu được lưu trữ trong các hàng này dùng làm các đầu vào cho một biểu thức đường dẫn, trả về tất cả các phần tử Comment lồng nhau. Các phần tiếp theo của truy vấn này sử dụng mệnh đề Query where để lọc thêm dữ liệu trả về và để chuyển đổi các phần về các ý kiến đã chọn thành các đoạn XML mới.

Với suy nghĩ này, hãy xem cách bạn có thể giải quyết vấn đề hơi khác một chút. Hãy tưởng tượng rằng bạn cần một danh sách tất cả các địa chỉ e-mail của các khách hàng Vàng đang sống ở San Jose. Hơn nữa, nếu bạn có nhiều địa chỉ e-mail cho một khách hàng duy nhất, bạn muốn tất cả chúng được bao gồm trong kết quả đầu ra như một phần của một bản ghi khách hàng duy nhất. Cuối cùng, nếu một khách hàng Vàng có đủ điều kiện lại không cung cấp cho bạn một địa chỉ e-mail, thì bạn cần lấy địa chỉ gửi thư của người đó. Liệt kê 25 cho thấy một cách để viết như một truy vấn như vậy:

Liệt kê 25. Nhúng SQL trong một XQuery có logic điều kiện
xquery 
for $y in 
db2-fn:sqlquery('select contactinfo from clients where status=''Gold'' ')/Client
where $y/Address/city="San Jose"
return (
     if ($y/email) then <emailList>{$y/email}</emailList>
     else $y/Address   
)

Hai khía cạnh của truy vấn này đáng được giải thích. Trước tiên, câu lệnh SELECT được nhúng trong dòng thứ hai chứa một biến vị ngữ truy vấn dựa trên cột Status (Trạng thái), so sánh giá trị của cột VARCHAR này với chuỗi Gold. Trong SQL, người ta bao quanh chuỗi như vậy bằng các dấu ngoặc đơn. Lưu ý rằng mặc dù các ví dụ có thể sử dụng các dấu ngoặc kép, nó thực sự sử dụng hai dấu ngoặc đơn trước và sau giá trị so sánh (''Gold''). Các dấu ngoặc đơn thêm vào là các ký tự thoát. Nếu bạn sử dụng các dấu ngoặc kép quanh biến vị ngữ truy vấn dựa trên chuỗi ký tự của bạn, thay cho cặp dấu ngoặc đơn, bạn sẽ nhận được một lỗi cú pháp.

Ngoài ra, mệnh đề return trong truy vấn này chứa logic điều kiện để xác định xem một phần tử e-mail đã có mặt trong bản ghi của một khách hàng cụ thể chưa. Nếu có, truy vấn này trả về một phần tử emailList mới có chứa tất cả địa chỉ e-mail của khách hàng (có nghĩa là, tất cả các phần tử e-mail cho khách hàng đó). Nếu không, nó trả về địa chỉ gửi thư của khách hàng (có nghĩa là, các phần tử Address cho khách hàng đó).

Lập chỉ mục

Thật đáng lưu ý rằng bạn có thể tạo các chỉ mục XML chuyên dụng để tăng tốc độ truy cập dữ liệu được lưu trữ trong các cột XML. Vì đây là một bài viết giới thiệu và các dữ liệu mẫu là nhỏ, nên chủ đề này không được trình bày ở đây. Tuy nhiên, trong môi trường sản xuất, việc định nghĩa các chỉ mục thích hợp có thể rất quan trọng để đạt được hiệu năng tối ưu. Xem phần Tài nguyên để có thông tin thêm về công nghệ lập chỉ mục của DB2.


Tóm tắt

XQuery khác với SQL về nhiều mặt quan trọng, một số mặt trong đó được nhấn mạnh trong bài viết này. Việc tìm hiểu thêm về ngôn ngữ này sẽ giúp bạn xác định khi nào nó có thể có lợi nhất cho công việc của bạn, cũng như giúp bạn hiểu khi nào nó có thể có ích để kết hợp XQuery với SQL. Các bài viết khác trong loạt bài này mô tả cách phát triển các ứng dụng Java nhằm khai thác các khả năng DB2 XML. Tuy vậy, bài viết này bao gồm một ví dụ Java đơn giản, mô tả một ứng dụng Java có thể đã nhúng một XQuery như thế nào.

Lời cảm ơn

Cảm ơn George Lapis, Matthias Nicola và Gary Robinson vì đã xem lại bài viết này.

Tài nguyên

Học tập

Lấy sản phẩm và công nghệ

  • Xây dựng dự án phát triển tiếp theo của bạn bằng phần mềm dùng thử của IBM, có sẵn để tải trực tiếp từ developerWorks.
  • Bây giờ bạn có thể sử dụng DB2 miễn phí. Hãy tải về DB2 Express-C, một phiên bản miễn phí của Ấn bản DB2 Express cho cộng đồng, cung cấp các tính năng dữ liệu cốt lõi giống như Ấn bản DB2 Express và cung cấp một cơ sở vững chắc để xây dựng và triển khai các ứng dụng.

Thảo luận

Bình luận

developerWorks: Đăng nhập

Các trường được đánh dấu hoa thị là bắt buộc (*).


Bạn cần một ID của IBM?
Bạn quên định danh?


Bạn quên mật khẩu?
Đổi mật khẩu

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Ở lần bạn đăng nhập đầu tiên vào trang developerWorks, một hồ sơ cá nhân của bạn được tạo ra. Thông tin trong bản hồ sơ này (tên bạn, nước/vùng lãnh thổ, và tên cơ quan) sẽ được trưng ra cho mọi người và sẽ đi cùng các nội dung mà bạn đăng, trừ khi bạn chọn việc ẩn tên cơ quan của bạn. Bạn có thể cập nhật tài khoản trên trang IBM bất cứ khi nào.

Thông tin gửi đi được đảm bảo an toàn.

Chọn tên hiển thị của bạn



Lần đầu tiên bạn đăng nhập vào trang developerWorks, một bản trích ngang được tạo ra cho bạn, bạn cần phải chọn một tên để hiển thị. Tên hiển thị của bạn sẽ đi kèm theo các nội dung mà bạn đăng tải trên developerWorks.

Tên hiển thị cần có từ 3 đến 30 ký tự. Tên xuất hiện của bạn phải là duy nhất trên trang Cộng đồng developerWorks và vì lí do an ninh nó không phải là địa chỉ email của bạn.

Các trường được đánh dấu hoa thị là bắt buộc (*).

(Tên hiển thị cần có từ 3 đến 30 ký tự)

Bằng việc nhấn Gửi, bạn đã đồng ý với các điều khoản sử dụng developerWorks Điều khoản sử dụng.

 


Thông tin gửi đi được đảm bảo an toàn.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=70
Zone=Information Management
ArticleID=751756
ArticleTitle=Hãy khởi đầu nhanh chóng với DB2 9 pureXML, Phần 4: Truy vấn dữ liệu DB2 XML bằng XQuery
publish-date=03252010