 | 级别: 初级 João Alberto de O. Lima, 程序员/分析员
2002 年 6 月 01 日 DB2 脚本编制用于自动执行任务、测试和使简单场景快速成型。本文描述了如何编写包含了 SQL 程序元素的脚本、如何将脚本的输出转化为一个日志文件以及如何处理参数。
摘要
尽管 XML(可扩展标记语言)元语言问世不久,但对于电子商务世界,它已经被认为是一项关键性技术。每天以这种新格式交换信息的数量和对于集成现有数据的需求都在不断增长。为了满足这种需求,IBM 已创建了 XML Extender for DB2。基本上,XML Extender 提供了两种方法来将 XML 数据集成到 DB2:列方法和集合方法。本文介绍了一种不同于 DB2 XML Extender
所使用的方法。这里呈现的解决方案具有简单、高效和灵活等特点。它简单,因为它只使用三个表来映射
XML 数据。它高效,因为索引适合于最佳访问路径的选择。而且,它灵活,因为通过
SQL 语句,有可能对 XML 数据应用许多操作。在本文的标题中,对表达式“XML 数据”应用
emphasis是为了强调这个建议的解决方案只是应用于
以数据为中心的 XML
文档(而不是
以文档为中心的)。
简介
关系数据库管理系统(RDBMS)处理大量数据的能力是毫无疑问的。传统的应用程序将“结构化数据”存储在表中。可选地,定义索引来优化查询和保证唯一性。SQL 查询的优化利用了一些复杂的功能,例如:查询重写、多连接(join)方法、详细的统计、并行性等。RDBMS 的发展应归功于这样的事实,RDBMS 是基于 E.F.Codd
[Codd1969]
正式定义的模型:关系模型。例如,这种理论模型允许用更有效的等价操作来代替关系代数操作同时不影响结果。RDBMS
优化器以自动方式来完成这种替代。
XML 标准的一个主要特性是:允许以一种一致的和比 SGML(标准通用标记语言)更简单的方式来对数据(通常是指“非结构化的”或“半结构化”的数据)进行操作。基本上,可以认为
XML 文档是“格式良好的”或“有效的”文档。一个“有效的” XML 可以包含一个 DTD(文档类型定义)或者引用一个
DTD,并且该文档符合这个 DTD。“格式良好的” XML 文档遵循 XML 标准的所有规则,但它可以没有 DTD,或者不遵守 DTD。
将 XML 数据映射到关系模型不是一件普通的任务(这也许就是市场上存在多种映射解决方案的原因之一)。
根据 Ronald Bourret
[Bourret2001]:
“XML 文档分为两大类:以数据为中心和以文档为中心。以数据为中心的文档是那些
XML 在其中被用作数据传输的文档。
这些文档包括销售订单、病人记录和科学数据。这些文档的物理结构 — 兄弟元素的次序、数据是存储在属性中还是在
PCDATA 元素中、是否使用实体 — 往往是不重要的。(……)以文档为中心的文档是那些
XML 在其中可以发挥象 SGML 那样作用的文档,譬如在用户手册、静态 Web 页面和市场宣传手册中等。这些文档的特征是:具有不规则的结构和混合的内容,而且,文档的物理结构是重要的。”
本文只讲述如何处理以数据为中心的文档。
XML 的基本概念
按照定义,XML 文档的结构是树状结构。只有一个根节点,所有其它节点都必须以分层形式来组织。在
清单
1中,我们介绍了一个 XML 文档示例。它的根节点是bibliography
元素。在第一行中,该文档声明这是一个 XML 文档,第二行表明将使用哪个 DTD 来验证该文档。
图
1是
清单 1 中 XML 文档的图形表示。DTD
定义了所有元素的次序和它们各自的属性(请参阅
清单 2)。例如:book
元素下包含 authors或author
元素,并且紧随其后的元素必须是元素 title、publisher和可选项地isbn 元素。重要的是注意:DTD
不包含带 MIXED 或 ANY 内容模型的元素。这是使得数据的映射和处理更方便的 XML 以数据为中心文档的特性之一。
<?xml version="1.0"?>
<!DOCTYPE bibliography SYSTEM "biblio.dtd">
<bibliography>
<entry id="Date2000">
<book year="2000" edition="2nd">
<authors>
<author>C. J. Date</author>
<author>Hugh Darwen</author>
</authors>
<title>Foundation for Future Database Systems - The Third Manifesto</title>
<publisher>Addison Wesley</publisher>
<isbn>0-201-70928-7</isbn>
</book>
</entry>
<entry id="Codd1970">
<article year="1970">
<author>E. F. Codd</author>
<title>A Relational Model of Data for Large Shared Data Banks</title>
<journal>Communications of ACM</journal>
<volume>13</volume>
<pages>377--387</pages>
<issn>0001-0782</issn>
</article>
</entry>
</bibliography>
|
清单 1. XML 以数据为中心样本文档
<!ELEMENT bibliography ( entry )* >
<!ELEMENT entry ( article | book ) >
<!ATTLIST entry
id ID #REQUIRED >
<!ELEMENT article (( author | authors ), title, journal, volume?, pages?, issn? ) >
<!ATTLIST article
year CDATA #REQUIRED >
<!ELEMENT book (( author | authors ), title, publisher, isbn? ) >
<!ATTLIST book
year CDATA #REQUIRED
edition CDATA #IMPLIED >
<!ELEMENT authors ( author )* >
<!ELEMENT author ( #PCDATA ) >
<!ELEMENT title ( #PCDATA ) >
<!ELEMENT journal ( #PCDATA ) >
<!ELEMENT year ( #PCDATA ) >
<!ELEMENT volume ( #PCDATA ) >
<!ELEMENT pages ( #PCDATA ) >
<!ELEMENT issn ( #PCDATA ) >
<!ELEMENT publisher ( #PCDATA ) >
<!ELEMENT isbn ( #PCDATA ) >
|
清单 2. XML 以数据为中心样本文档的 DTD
图 1. XML 以数据为中心样本文档的图形表示
表现和表示
本文解决了以下问题:如何在所有数据都是以表格形式
表现的关系数据库中
表示 XML
数据(它与生俱来就是一种层次结构)。强调表现和表示之间的差别,这是很重要的。在这个问题上,Chris Date
[Date2000] 非常强调这一点:
“……我非常讨厌听到这样的说法,‘关系模型是二维的’,或者说关系是‘平面的’(等等),而实际数据是‘多维’的。很清楚,那些认为这些说法构成对关系模型的有效批评的人显然没有理解这种模型,事实上是完全不明白。当然,在纸上以表格形式绘制时,关系看上去确实是平面的。但图上画的与实际的是两码事!如果一个关系有
n 列,那么这个关系中的每行表示 n 维空间中的一点 — 而且这个关系作为一个整体代表了一组这样的点。换句话说,n
列的关系是 n 维的,而不是二维的!”
按照同一想法,我们想要指出,用关系模型来“表示”XML 的层次结构是可能的。使用 Joe Celko
[Celko1995] 所建议的方法之一,下面将要表现的数据模型表示了这种树结构。
建议的数据模型:结构
这个建议的数据模型非常简单,它只由三个关系组成:Document
— 表示每个 XML 文档实例的一个元组;Element — 表示 Document 的每个元素的一个元组;Attribute
— 表示 Element 的每个属性的一个元组。
图 2
显示了这个建议的解决方案的实体-关系(Entity-Relationship)图。
图 2.建议的解决方案的实体-关系图
Document表有以下属性:SEQ_XML— 用来标识 XML 文档实例(主键)的序列号;TEXT_XML
— XML 文档的原始内容;HEAD_XML —
包含 XML 头的声明,有关于编码、版本等方面的信息;DTD_REF_XML —
包含对 DTD 文件的引用;WELL_FORMED — 表明 XML
文档是否是格式良好的;VALID_XML — 表明 XML 文档是否有效。
Element表有以下属性:SEQ_XML— 用来标识 XML 文档实例(主键和外键)的序列号;ORD_INITIAL
— 由树遍历算法生成的数,该数标识了元素在树中的次序(在访问该元素的从属元素
之前);TAG_NAME
— 元素名称;ORD_FINAL— 由树遍历算法生成的数,该数标识了元素在树中的次序(在访问该元素的从属元素
之后);TAG_CONTENT
— 元素内容,如果它存在;QTD_ATTRIBUTE
— 元素所具有属性的数目。
Attribute表有以下属性:SEQ_XML 和
ORD_INITIAL(外键和主键);ATT_SEQ
— 标识属性顺序的序列号(主键);ATT_NAME —
属性名称;ATT_VALUE — 属性值。
表 Element和Attribute
将元素和属性内容作为 VARCHAR
列存储。该内容可以是数字、日期、字符串等,这取决于应用程序。建议的模型的表不应该被视为数据的最终目的地,而应视为“数据阶段”。在对分段文档进行解析、验证和存储之后,根据每个应用程序来检索数据项,然后将这些数据项发送到最终目的地,在那里会对这些数据项做一些常规处理。
要确保从一般 VARCHAR 列的内容到目标列的映射不会导致不一致,可以在解析/验证过程使用 XML Schema
标准中的一些资源。例如,假定在 XML 文档中存储了一个无效的日期(2001 年 2 月
30 日)。如果只有 DTD 验证,则不会检测出这个错误(该无效日期会存储在 VARCHAR 列中,并且只有在到 DATE
类型列的最终映射上,才能发现该错误)。有了 XML Schema,就会在解析/验证时检测出这个错误。
图 3 显示了带有每个元素有序编号的 XML 文档的图形表示。下图显示了每个元素拥有一对数字:左边那个数字代表
ORD_INITIAL,右边那个数字代表 ORD_FINAL。如果使用 DOM(文档对象模型),则对 XML
文档进行解析和验证(可选)之后,可以应用树遍历算法。值得注意的是:数学表达式
((Element.ORD_FINAL - Element.ORD_INITIAL - 1) / 2)
表示该元素的从属元素的数目。
通过使用 XML 数据样本文档,
图 4 显示了我们所考虑的模式(schema)的一个实例。
图 3. 带有序编号的 XML 以数据为中心样本文档的图形表示
图 4. XML 以数据为中心样本文档的表表示
建议的数据模型:操作
这个建议的模型允许完成许多类型的操作。在本文中,我们将看三个操作的组:
重构原始 XML
要重构原始 XML,必须创建一个利用 UNION ALL 操作符的 SELECT 语句。
清单 3 显示了该 SELECT 语句,它由 4 个子选择语句组成,如下所示:
- 第一个 SELECT 语句返回主要内容:元素的初始标记、它的属性和内容;
- 第二个 SELECT 语句返回元素的最后一个标记;
- 第三个 SELECT 语句返回 XML 文档的标题行。
- 而最后一个 SELECT 语句返回 DTD 这一行(如果它存在)。
最后,ORDER BY 根据最初的层次,将初始标记和最后一个标记放在适当的位置,处理结果的排序。
SELECT CASE WHEN COALESCE(att_seq, 1) = 1
THEN '<' || RTRIM(e.tag_name)
ELSE '' END
|| CASE WHEN COALESCE(att_seq, 0) > 0
THEN ' ' || RTRIM(att_name) || '="' || RTRIM(att_value) || '"'
ELSE '' END
|| CASE WHEN qtd_attribute = COALESCE(att_seq, 0)
THEN '>'
ELSE '' END
|| COALESCE(RTRIM(tag_content), '') AS line,
e.ord_initial AS ord,
att_seq AS ord_att
FROM relxml.element e
LEFT OUTER JOIN
relxml.attribute a
ON e.seq_xml = a.seq_xml
AND e.ord_initial = a.ord_initial
WHERE e.seq_xml =
0
UNION ALL
SELECT '</' || RTRIM(tag_name) || '>' AS line,
ord_final AS ord,
0 AS ord_att
FROM relxml.element
WHERE seq_xml =
0
UNION ALL
SELECT RTRIM(head_xml) AS line, -1 AS ord, 0 AS ord_att
FROM relxml.document
WHERE seq_xml =
0
UNION ALL
SELECT RTRIM(dtd_ref_xml) AS line, 0 AS ord, 0 AS ord_att
FROM relxml.document
WHERE seq_xml =
0
ORDER BY ord, ord_att
|
清单 3. 用“seq_xml = 0”重构 XML 文档
返回一个确定的子树也是可能的,在这种情形下,必须指出它的根元素。
清单
4显示了所需要的 SELECT 语句,该语句将从Book 元素(ord_initial =
3,ord_final = 16)返回期望的子树。请注意用
黑体表示的行,这些行起到了子树过滤器的作用。
SELECT CASE WHEN COALESCE(att_seq, 1) = 1
THEN '<' || RTRIM(e.tag_name)
ELSE '' END
|| CASE WHEN COALESCE(att_seq, 0) > 0
THEN ' ' || RTRIM(att_name) || '="' || RTRIM(att_value) || '"'
ELSE '' END
|| CASE WHEN qtd_attribute = COALESCE(att_seq, 0)
THEN '>'
ELSE '' END
|| COALESCE(RTRIM(tag_content), '') AS line,
e.ord_initial AS ord,
att_seq AS ord_att
FROM relxml.element e
LEFT OUTER JOIN
relxml.attribute a
ON e.seq_xml = a.seq_xml
AND e.ord_initial = a.ord_initial
WHERE e.seq_xml = 0
AND e.ord_initial >= 3 and e.ord_final <= 16
UNION ALL
SELECT '</' || rtrim(tag_name) || '>' AS line, ord_final AS ord, 0 AS ord_att
FROM relxml.element
WHERE seq_xml = 0
AND ord_initial >= 3 and ord_final <= 16
ORDER BY ord, ord_att
|
清单 4. 从原始的 XML 文档重构子树。
在元素间导航
给定一个元素,根据该元素与目标元素之间关系的某些特征来导航至目标元素是可能的。在这一节中,用建议的解决方案来探索导航的某些可能性。我们将验证以下导航情况:
下表有两列:左边一列显示了操作、操作参数和实现该操作的 SELECT
语句;右边一列显示了说明导航的图。在图中,箭头将每次操作中的当前节点链接到那些要检索的节点。
a) retrieve root node
Parameter:
1 - Document number (seq_xml)
SELECT tag_name
FROM relxml.element
WHERE seq_xml =
0
AND ord_initial = 1
Result:
tag_name
------------
bibliography
XPath Expression
|
|
|
b) retrieve first child node
Parameters:
1 - Document number (seq_xml)
2 - Initial number (ord_initial)
SELECT tag_name, tag_content
FROM relxml.element
WHERE seq_xml =
0
AND ord_initial =
4 + 1
Result:
tag_name tag_content
-------- -----------
author C. J. Date
|
|
|
c) retrieve next sibling node
Parameters:
1 - Document number (seq_xml)
2 - Initial number (ord_initial)
SELECT e2.tag_name, e2.tag_content
FROM relxml.element e,
relxml.element e2
WHERE e.seq_xml =
0
AND e.ord_initial =
5
AND e.seq_xml = e2.seq_xml
AND e.ord_final + 1 = e2.ord_initial
Result:
tag_name tag_content
-------- -----------
author Hugh Darwen
|
|
|
d) retrieve previous sibling node
Parameters:
1 - Document number (seq_xml)
2 - Initial number (ord_initial)
SELECT e2.tag_name, e2.tag_content
FROM relxml.element e,
relxml.element e2
WHERE e.seq_xml =
0
AND e.ord_initial =
7
AND e.seq_xml = e2.seq_xml
AND e2.ord_final + 1 = e.ord_initial
Result:
tag_name tag_content
-------- -----------
author C. J.Date
|
|
|
e) retrieve context;
Parameters:
1 - Document number (seq_xml)
2 - Initial number (ord_initial)
3 - Final number (ord_final)
SELECT e.tag_name, ord_initial
FROM relxml.element e
WHERE e.seq_xml =
0
AND e.ord_initial <=
7
AND e.ord_final >=
8
ORDER BY ord_initial DESC
Result:
tag_name ord_initial
------------ -----------
authors 4
book 3
entry 2
bibliography 1
|
|
|
f) retrieve parent node
Parameters:
1 - Document number (seq_xml)
2 - Initial number (ord_initial)
3 - Final number (ord_final)
SELECT e.tag_name, e.tag_content
FROM relxml.element e
WHERE e.seq_xml =
0
AND e.ord_initial <
7
AND e.ord_final >
8
AND e.ord_initial =
(SELECT MAX(ord_initial)
FROM relxml.element e2
WHERE e2.seq_xml = e.seq_xml
AND e2.ord_initial <
7
AND e2.ord_final >
8)
Result:
tag_name tag_content
------------ -----------
authors
|
|
|
g) retrieve last sibling node
Parameters:
1 - Document number (seq_xml)
2 - Initial number (ord_initial)
3 - Final number (ord_final)
SELECT e3.tag_name, e3.tag_content
FROM relxml.element e,
relxml.element e3
WHERE e.seq_xml = 0
AND e.seq_xml = e3.seq_xml
AND e.ord_final - 1 = e3.ord_final
AND
21 <> e3.ord_final
AND e.ord_initial <
20
AND e.ord_final >
21
AND e.ord_initial =
(SELECT MAX(ord_initial)
FROM relxml.element e2
WHERE e2.seq_xml = e.seq_xml
AND e2.ord_initial <
20
AND e2.ord_final >
21)
Result:
tag_name tag_content
------------ -----------
issn 0001-0782
|
|
|
h) retrieve first sibling node
Parameters:
1 - Document number (seq_xml)
2 - Initial number (ord_initial)
3 - Final number (ord_final)
SELECT e3.tag_name, e3.tag_content
FROM relxml.element e,
relxml.element e3
WHERE e.seq_xml = 0
AND e.seq_xml = e3.seq_xml
AND e.ord_initial + 1 = e3.ord_initial
AND
30 <> e3.ord_initial
AND e.ord_initial <
30
AND e.ord_final >
31
AND e.ord_initial =
(SELECT MAX(ord_initial)
FROM relxml.element e2
WHERE e2.seq_xml = e.seq_xml
AND e2.ord_initial <
30
AND e2.ord_final >
31)
Result:
tag_name tag_content
------------ -----------
author E. F. Codd
|
|
|
表 1. 元素间的导航
注意:在该表中所显示的操作总是限定一个 XML 文档实例,即,总是提供 seq_xml
属性。优化器会选择 Element 表的主索引中的 Index Scan Matching(Matchcols = 2)访问路径,这将显著减少响应时间。
查询操作(元素和属性)
建议的模型允许通过简单的 SELECT 语句来查询某个元素或属性。下面显示了四种类型的查询:
在下表左边一列显示了操作类型、操作参数和相应的 SELECT 语句。在右边一列中显示了查询结果。
a) retrieve elements based on
an attribute's value.
Parameters:
1 - Document number (seq_xml)
2 - Attribute name (att_name)
3 - Attribute value (att_value)
SELECT e.tag_name,
e.ord_initial, e.ord_final
FROM relxml.element e,
relxml.attribute a
WHERE e.seq_xml = a.seq_xml
AND e.ord_initial = a.ord_initial
AND a.att_name =
'year'
AND a.att_value =
'2000'
AND e.seq_xml =
0
|
|
Result:
Tag_name ord_initial ord_final
-------- ----------- ---------
book 3 16
|
|
b) retrieve elements based on
an element's name.
Parameters:
1 - Document number (seq_xml)
2 - Element name (tag_name)
SELECT tag_content, ord_initial, ord_final
FROM relxml.element
WHERE seq_xml =
0
AND tag_name =
'author'
|
|
Result:
Tag_content ord_initial ord_final
----------- ----------- ---------
J. C. Date 5 6
Hugh Darwen 7 8
E. F. Codd 20 21
|
|
c) retrieve elements based on
an element's content.
Parameters:
1 - Document number (seq_xml)
2 - Element name (tag_name)
3 - Element content (tag_content)
SELECT ord_initial, ord_final
FROM relxml.element
WHERE
tag_name =
'author'
and tag_content =
'E. F. Codd'
and seq_xml =
0
|
|
Result:
ord_initial ord_final
----------- ---------
20 21
|
|
d) retrieve leaf nodes.
Parameters:
1 - Document number (seq_xml)
SELECT ord_initial, ord_final,
tag_name, tag_content
FROM relxml.element
WHERE
seq_xml =
0
and ord_initial + 1 = ord_final
|
|
Result:
o_i o_f tag_name tag_content
--- --- -------- ------------
5 6 author C. J. Date
7 8 author Hugh Darwen
10 11 title Foundation for Future ...
12 13 publisher Addisson Wesley
14 15 isbn 0-201-70928-7
20 21 author E. F. Codd
22 23 title A Relational Model of ...
24 25 journal Communications of ACM
26 27 volume 13
28 29 pages 377--387
30 31 issn 0001-0782
|
|
表 2. 元素和属性上的查询操作
在以上显示的这些示例中,总是提供了 XML 文档(seq_xml
属性)的实例。这意味着查询是在单个文档的元素/属性中执行的。然而,也有可能不通知实例,这时,查询将会应用于所有存储的文档 — 查询的作用域不再是树,而是森林。为了缩短这些查询的响应时间,必须创建一些索引:
- 要优化查询“a”和“b”,必须在Attribute
表中列(att_name, att_value, seq_xml )上创建一个复合索引。
- 要优化查询“c”,必须在 Element 表中列(tag_name, seq_xml)上创建一个复合索引。
限制
下面显示了这个建议的模型的一些限制:
- 这个建议的解决方案不能应用于 MIXED 和 ANY 内容模型。在以文档为中心这一类的 XML
文档中,经常使用到这些内容模型。只要 DTD 不允许 MIXED 或 ANY 内容,这个建议的解决方案也可应用到以文档为中心的 XML 文档。
- 更新 XML 文档的结构(包括或不包括节点)将需要对许多元素重新编号。对于那些具有许多元素和许多更新活动的文档,这种限制使得该解决方案不切实际。
- 为了简化该模型,有意没有考虑 XML 所具有的某些特性,譬如:注释、CDATA 等。
- 限制元素的名称最多可有 100 个字节,其内容最多可有 3000 个字节。可以修改这些限制。对于属性,则限制其名称和值各自最多有
255 个字节,这样可以使属性的名称和值加入到创建的复合索引中。由于可以索引属性的名称和内容,因此必须借助(在
DTD 的定义方面)作为属性(而不是元素)的数据项的建模,如果该数据项成为经常查询的参数的话。
Richard Edwards
[Edwards2000] 和 Sian Hope
建议,对于 XML 文档到关系数据库的映射,在基于 DOM 的一般模型中不存在对 MIXED 和 ANY 内容模型的限制。它们的模式使用 15 张表,比我建议的要更完整和复杂。持久的
DOM 解决方案允许在关系数据库中存储任何类型的 XML 文档。
结束语
已经在 DB2 生产环境下成功地开发和测试了用于 DB2 的 XML 数据映射建议。该建议收集了两种技术的优点:XML
的灵活性和 DB2 的性能。电子商务解决方案有一个重要问题:可伸缩性。因此,将 RDBMS
的性能和全世界电子商务当前使用的语言 XML 结合在一起是很重要的。
参考资料
- 您可以参阅本文在 developerWorks 全球站点上的
英文原文.
-
[Celko1995] Joe Celko,“SQL for Smarties: Advanced SQL Programming”,Morgan Kaufmann,1995。
-
[Codd1969] E. F. Codd,“Derivability, Redundancy, and Consistency of Relations Stored in Large Data Banks”,刊登在
IBM Research Report RJ599,1969,San Jose,CA。
-
[Date2000] C. J. Date,
http://www.firstsql.com/dbdebunk/cjd1a.htm
上的“What do you mean, "Post-Relational"?”,2000 年 6 月。
-
[Bourret2001] Ronald Bourret,
http://www.rpbourret.com/xml/XMLDatabaseProds.htm
上的“XML Database Products”,2001 年 8 月。
-
[Edwards2000] Richard Edwards 和 Sian Hope,“Persistent DOM: An architecture for XML repositories in relational databases.”,刊登在 Computer Science(第 1983 卷)上的 Lecture Notes,Springer-Verlag,pp416-421,2000 年。
关于作者  | 
|  |
JoÃo Alberto de Oliveira Lima 于 1991
年作为一名程序员/分析员开始了他的软件生涯。自从 1993 起,他作为一名开发人员、DBA 和 SQL
性能分析师一直从事 DB2 的工作。他拥有计算机科学专业的学士学位和软件工程专业的硕士学位。最近,他取得了下面一些证书:“DB2
UDB V7.1 Database Administration for OS/390”、“DB2 UDB V7.1 Database Administration
for Unix, Windows and OS/2”和“DB2 UDB V7.1 Family Application Development”等方面的 IBM
Certified Solutions Expert。他居住在巴西利亚(巴西),可以通过
joaolima@acm.org与他联系。
致谢:作者十分感谢 JoÃo Batista de Holanda Neto、Omar FalcÃo Soares、Ronald Bourret 和 Berthold Reinwald 对本文所提供的意见和建议。
|
对本文的评价
|  |