今天我要為大家介紹的是XPath,XPath是導航和查詢XML文檔的語言。我們從一個函數開始。
UpdateXML()函數
我們已經花了很多時間介紹ExtractValue()函數,但還沒有介紹MySQL的其它XML函數,如UpdateXML(),因為我們先前主要將內容放在將XML文檔中的數據導入到MySQL數據庫中了,UpdateXML()是一個使用不同的XML標記匹配和替換XML塊的函數。
ExtractValue()有兩個字符串參數,一個XML標記,一個XPath表達式。
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->ExtractValue(XML_frag, xpath_expr)
它返回第一個匹配XPath表達式的文本節點。假設你想將“”變為“”,並將結果保存到一個變量中,下面是使用UpdateXML()函數實現這個目標的做法:
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->MySQL> SELECT @new_xml_node:=UpdateXML('',
-> '//city',
-> '')
-> AS XML_node;
+-----------------------------------------+
| XML_node |
+-----------------------------------------+
| |
+-----------------------------------------+
1 row in set (0.03 sec)
MySQL> SELECT @new_XML_node;
+-----------------------------------------+
| @new_XML_node |
+-----------------------------------------+
| |
+-----------------------------------------+
1 row in set (0.00 sec)
如果沒有發現匹配表達式的文本節點,就返回原始XML字符串。
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->MySQL> SELECT @new_xml_node:=UpdateXML('',
-> '//dummy',
-> '')
-> AS XML_node;
+---------------------------+
| XML_node |
+---------------------------+
| |
+---------------------------+
1 row in set (0.03 sec)
如果發現有多個都匹配,會按順序返回每個匹配的子文本節點的內容。
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->MySQL> SELECT @new_xml_node:=UpdateXML('',
-> '//city,
-> '')
-> AS XML_node;
+--------------------------------------------------------------------+
| XML_node |
+--------------------------------------------------------------------+
| |
+--------------------------------------------------------------------+
1 row in set (0.00 sec)
因為UpdateXML()函數在匹配到空元素和沒有匹配之間沒有區別,要區別它們可以使用XPath count()函數測試ExtractValue()的返回。
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->MySQL> SELECT ExtractValue('',
-> 'count(//city)') AS XML_node;
+----------+
| XML_node |
+----------+
| 3 |
+----------+
1 row in set (0.00 sec)
MySQL> SELECT ExtractValue('',
-> 'count(//county)') AS XML_node;
+----------+
| XML_node |
+----------+
| 0 |
+----------+
1 row in set (0.00 sec)
對ExtractValue() 和 UpdateXML(),使用的XPath定位器必須是有效的,被搜索的XML必須是結構良好的,如果定位器無效,則會產生一個錯誤。
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->MySQL> SELECT @new_xml_node:=UpdateXML('',
-> '//city/"state', '') AS XML_node;
ERROR 1105 (HY000): XPATH syntax error: '"state'
如果被搜索的XML結構不好,則會返回null,並產生一個警告。
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->MySQL> SELECT @new_xml_node:=UpdateXML('', '//city',
-> '') AS XML_node;
+----------+
| XML_node |
+----------+
| NULL |
+----------+
1 row in set, 1 warning (0.01 sec)
可以使用show warnings命令顯示警告。
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->MySQL> show warnings;
+---------+------+---------------------------------------------------------------+
| Level | Code | Message |
+---------+------+---------------------------------------------------------------+
| Warning | 1525 | Incorrect XML value: 'parse error at line 1 pos 21: '' unexpected ('' wanted)' |
+---------+------+---------------------------------------------------------------+
1 row in set (0.01 sec)
作為第三個參數傳遞給UpdateXML()的替換XML則不會檢查結構。
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->MySQL> SELECT @new_xml_node:=UpdateXML('', '//city',
-> '<>') AS XML_node;
+-------------------------------------------+
| XML_node |
+-------------------------------------------+
| <> |
+-------------------------------------------+
1 row in set (0.00 sec)
在XPath表達式中使用變量
從MySQL 5.1.20開始,可以在XPath定位器參數中使用變量,這樣在傳遞參數時就更加靈活了,根據使用的語法不同,可以對變量實施弱檢查或強檢查。
弱檢查變量
使用$@variable_name語法的變量不會檢查類型,也不會檢查之前是否給它分配過值,因此如果變量類型錯誤或未定義,MySQL不會報告任何警告或錯誤。例如,假設你將變量$@county寫成$@conty了,由於拼寫有誤,這個錯誤的變量不會被賦予任何值,MySQL便會給它賦予一個“none”或“null”值。
在下面的例子中,我創建了一個變量@XML_citIEs,在文本節點中包含三個國家的城市,接著我創建了兩個變量存儲XPath信息,第一個SELECT查詢顯示@state_index_first變量和ExtractValue()函數的值,由於@state_index_first變量包含一個值“1”,//state[@state_index_first]表達式檢索第一個城市的結果是“Chicago”。
第二個SELECT查詢檢索@state_index_second變量的值,以及ExtractValue()函數返回的結果,正如預期那樣,它返回“Ocean City”。
最後一個SELECT語句有點問題,因為沒有@state_index_third變量,ExtractValue()函數運行後無值返回,這樣可能導致我們認為確實沒有更多的城市。
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->MySQL> SET @XML_citIEs = 'Chicago
-> Ocean City
-> Baltimore';
Query OK, 0 rows affected (0.21 sec)
MySQL> SET @state_index_first = 1, @state_index_second = 2;
Query OK, 0 rows affected (0.00 sec)
MySQL> SELECT @state_index_first, ExtractValue(@XML_citIEs,
-> '//state[$@state_index_first]') AS 'First City';
+--------------------+------------+
| @state_index_first | First City |
+--------------------+------------+
| 1 | Chicago |
+--------------------+------------+
1 row in set (0.00 sec)
MySQL> SELECT @state_index_second, ExtractValue(@XML_citIEs,
-> '//state[$@state_index_second]') AS 'Second City';
+---------------------+-------------+
| @state_index_second | Second City |
+---------------------+-------------+
| 2 | Ocean City |
+---------------------+-------------+
1 row in set (0.00 sec)
MySQL> SELECT @state_index_third, ExtractValue(@XML_citIEs,
-> '//state[$@state_index_third]') AS 'Third City';
+--------------------+------------+
| @state_index_third | Third City |
+--------------------+------------+
| NULL | |
+--------------------+------------+
1 row in set (0.00 sec)
正是由於上面的原因,強烈建議使用強檢查變量,這並沒什麼奇怪的,因為強檢查變量作為計算機編程語言的一個標准已經有很長一段時間了,如果要使用$variable_name語法,需要在存儲過程中聲明變量,這樣的變量屬於本地變量,是要進行類型和值的檢查的。
下面的存儲過程接受一個XML_data字符串,並將ExtractValue()函數返回的結果添加到一個臨時表中,這樣我們就可以一次查詢所有的城市了。
Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
-->DELIMITER |
CREATE PROCEDURE get_citIEs(IN 'XML_data' varchar(255))
BEGIN
DECLARE i INT DEFAULT 1;
DECLARE city_name VARCHAR(100);
CREATE TEMPORARY TABLE citIEs (
match_number INT UNSIGNED NOT NULL DEFAULT 0,
name VARCHAR(100) NULL );
SET city_name = ExtractValue(XML_data, '//state[$i]');
WHILE city_name != "" DO
INSERT INTO citIEs
SELECT i, city_name;
SET i = i+1;
SET city_name = ExtractValue(XML_data, '//state[$i]');
END WHILE;
SELECT * FROM citIEs;
DROP TABLE citIEs;
END |
DELIMITER ;
CALL get_citIEs(@XML_citIEs);
+--------------------+------------+
| match_number | name |
+--------------------+------------+
| 1 | Chicago |
+--------------------+------------+
| 2 | Ocean City |
+--------------------+------------+
1 row in set (0.01 sec)
小結
雖然在MySQL中使用XML還有些限制,但每個新版本都會帶來更多的與XML相關的功能,也有第三方廠家提供了工具來彌補這些缺陷。我希望未來看到支持新的,冗余更少的數據格式標記,如JSON。