XML与数据库

2006年3月15日 #

word

initiative   n 主动
bolster      n 支持
bolster up sb.'s pride 助长了某人的傲慢、自大
enterprise   n 企业, 事业, 计划, 事业心, 进取心, 干事业
deploy  v 展开, 配置, 部署
IT specialist=integration specialist 系统集成专家
specialist, consultant, architect, project manager
matrix 矩阵
---------------------------------------------
1.what's your hobby?
2.do you think of going to study aboard?
3. 你遇到的最大的困难是什么?
4. 你最大的成就是什么?
5. 给一个在压力下工作的例子
6.what personality do you admire?
7.decribe your weakness?
8.how do you coorperate with others?
9.how long will you work for the company?
10.职业计划
11.what's the most important factor do you think in your career?

posted @ 2006-03-15 17:47 Flysir 阅读(24) | 评论 (0)编辑

2006年2月8日 #

流水号生成(格式:YYYYMMDD+6位流水号)

------------------------------------------------
-- 插入用户买点数信息
------------------------------------------------
CREATE PROCEDURE InsertUserAccount
    @Phone        NVARCHAR(15),
    @Point          int,
    @Status            NVARCHAR(50),
    @Step1Time     DATETIME  ,
    @PayBy       NVARCHAR(100),
    @ID           INT = 0 OUTPUT
AS
    SET NOCOUNT ON

-----------------------------------------生成流水号作为单据号--------------------------------
declare @ordid nvarchar(100)
declare @ord nvarchar(100)
declare @time nvarchar(100)

select @ord=isnull(max(right(orderNO,6)),'000000') from UserAccount  where left(orderNO,8)=CONVERT(varchar,getdate(),112)
set @ord=cast((cast(@ord as bigint)+1) as varchar)
if(len(@ord)<6)
begin
 SET @ord = '000000' + @ord
 SET @ord = substring(@ord, len(@ord) - 5, 6)
end

set @time=CONVERT(varchar,getdate(),112)

set @ordid=@time+@ord
-------------------------------------------流水号生成完毕----------------------------------------

    INSERT [UserAccount]
 (
          orderNO, 
          phone,
                      point,
         status,
                     Step1Time,
                     PayBy
 )

    SELECT
         @ordid, 
        @Phone,
           @Point,
           @Status,
 @Step1Time,
 @PayBy

select @ordid

------------------------------------------------
GO

posted @ 2006-02-08 16:00 Flysir 阅读(102) | 评论 (0)编辑

2006年1月31日 #

XML与数据库

1.0 简介 (Introduction)

本文对怎样使用XML和数据库进行了总体回顾,描述了以数据为中心和以文档文中心的文档之间的差异如何影响其在数据库中的应用,将XML和关系型数据库的一起使用的常见用法,以及什么是原生XML数据库和何时使用它。

2.0 XML是数据库吗?(Is XML a Database?)

在开始讨论XML和数据库之前,我们先回答许多人都遇到过的问题:“XML是数据库吗?”

如果仅按数据库这个术语的本质来看,XML文件就是数据库,它是数据的集合。在许多方面看起来它和其他文件没什么区别 -- 无论如何,每个文件都含有某种类型的数据。作为一种“数据库”格式,XML有一些优势:例如,它是自描述的(所用的标记描述了数据的结构和类型,尽管缺乏语义),可交换的(portable)(Unicode),能够以树型或图形结构描述数据。同样它也有缺点,例如,它显得有些繁琐,由于要对它进行解析和文本转换,所以数据访问速度较慢。

一个更有用的问题就是在较为宽松的意义上,XML及其周边技术是否可以算作“数据库” -- 数据库管理系统(DBMS)。答案是“在某种程度上是(sort of)”。从正面来说,XML提供了许多数据库所具备的东西:存储(XML文档), 模式(DTD, XML schema,RElAX NG 等等), 查询语言(XQuery, XPath, XQL, XML-QL, QUILT等等),编程接口(SAX, DOM,JDOM)等等。从反面来说,它缺少一些作为实用的数据库所应具备的特性:高效的存储,索引,安全,事务和数据一致性,多用户访问,触发器,在查询多个文件等等。

因此,尽管在数据量小、用户少和性能要求不太高的环境下,可以将XML文档用作数据库,但是却不适用于用户量大、数据集成度高以及性能要求高的作业环境。

XML适合于用作所谓“数据库”的一个好例子就是 .ini文件 -- 它包含应用程序的配置信息。与其写一个处理以逗号分隔(comma-delimited)的文件的解析器,开发一种小型的XML语言并写一个解释它的 SAX程序要容易的多。此外,XML允许使用嵌套的实体,而逗号分隔的文件(comma-delimited files)很难做到这点。然而,说它就是数据库还很勉强,因为它是线性读写的,而且仅用在程序开始和结束时。

比较适合于XML数据库的一些复杂的数据集就是个人通讯录(名字,电话号码,地址等),或用于描述浏览器书签以及用Napster偷来的MP3。然而,由于dBase和Access之类的数据库物美价廉,即使在这种情况下似乎也没有多少理由把XML文件作为数据库使用。XML的唯一真正好处就是数据的可交换性(portable),由于有越来越多的工具可以用来对数据库进行XML序列化(serializing),这一点好处似乎也要打些折扣。

3.0 为什么要用数据库?(Why Use a Database?)

当你开始考虑XML和数据库的时候,可能首先会问这个问题:为什么你会先想到使用数据库?你是否有可用的历史数据?是否想找个地方存储Web页面? 是否在电子商务中使用数据库,而XML在其中作为数据传输载体? 对这些问题的回答将会极大地影响你对数据库和中间件(如果有的话)的选择,以及如何使用所选的数据库。

例如,你有个电子商务的应用,将XML用作数据交换。那么你的数据最好有个非常规则的结构并且可供非XML程序使用。还有,XML文档所用的某些东西如实体和编码对你来说并不重要 --总之,你感兴趣的是数据,而不是它在XML内如何存储。在这种情况下,你大概需要一个关系型数据库以及在XML和数据库之间转换数据的软件。如果你的应用程序是面向对象的,你甚至还需要一个在数据库或XML中存取这些对象的系统。

另一方面,假如你要从一些结构松散的XML文档建立一个网站。你不但要管理这个网站,还要提供站点内容搜索。你的文档看起来结构比较松散,其中的实体的使用对你来说可能更重要,因为它们是文档结构的重要部分。这种情况下,你也许需要一个原生XML数据库(native XML database)或内容管理系统(content management system)。这使你可以保持文档的物理结构,支持文件级的事务处理,以及使用XML Query语言进行查询。

4.0 数据和文档 (Data versus Documents)

在选择数据库时最重要因素大概就是你想在数据库存储的究竟是数据还是文档。例如,是简单地把XML当作数据库和(可能不支持XML)应用程序之间的数据转换工具,还是用于集成,就像XHTML和DocBook中的那样?通常这是个偏好,但是却非常重要,因为所有以数据为中心(data-centric)文档有着许多相同的特性,所有以文档为中心(document-centric)也有许多相同的特性。这会影响到XML在数据库中如何存储。下面两部分中我们就来考察这些特性。

(历史背景:我在xml-dev邮件列表上第一次听说data-centricdocument-centric这些术语,不知道是谁发明的,但是我在1997的消息中发现有使用document-centric的,从1998年以后这两个术语都有使用。)

4.1 以数据为中心的文档 (Data-Centric Documents)

以数据为中心的文档就是将XML用作数据的传输载体,只提供给机器消费的文档,在此XML通常并不是绝对必要的。也就是说,对于应用程序或数据库而言,(在某个时间段内)数据是否以XML文档的形式存储并不重要。以数据为中心的文档的例子有销售订单、航班时刻表、科研数据及股市汇率。

以数据为中心的文档的特点是结构相当规整,数据粒度精细(fine-grained data)(即最小的独立数据单位只存在于PCDATA元素或属性这一级别),很少或没有混合内容。除非在对文档进行验证的时候,同级元素或PCDATA的出现次序一般来说并不重要。

以数据为中心的文档中的这类数据可以来自数据库(此时要输入给XML)或在数据库之外(此时要将其存入数据库)。前者的一个例子就是关系数据库现存的大量数据;而从测量系统采集并转化为XML的科研数据就是后者的例子。

例如,下面的销售订单就是以数据为中心的:

   <SalesOrder SONumber="12345">
<Customer CustNumber="543">
<CustName>ABC Industries</CustName>
<Street>123 Main St.</Street>
<City>Chicago</City>
<State>IL</State>
<PostCode>60609</PostCode>
</Customer>
<OrderDate>981215</OrderDate>
<Item ItemNumber="1">
<Part PartNumber="123">
<Description>
<p><b>Turkey wrench:</b><br />
Stainless steel, one-piece construction,
lifetime guarantee.</p>
</Description>
<Price>9.95</Price>
</Part>
<Quantity>10</Quantity>
</Item>
<Item ItemNumber="2">
<Part PartNumber="456">
<Description>
<p><b>Stuffing separator:<b><br />
Aluminum, one-year guarantee.</p>
</Description>
<Price>13.27</Price>
</Part>
<Quantity>5</Quantity>
</Item>
</SalesOrder>

除了像销售订单这种显而易见的以数据为中心的文档之外,许多以文本为主的(prose- rich)文档也可以是以数据为中心的。例如,Amazon.com用来显示书籍信息的一个页面。尽管页面上大部分内容都是文本,这些文本的结构是非常规则的,许多都和其它书籍的描述相同,每个页面特有的文本并不很多。这样,就可以从数据库中取出书籍的相关资料,转换为简单的、以数据为中心的XML文档,再用XSL样式表生成页面。一般来说,那些用数据库中的数据填充模板,动态生成HTML文件的网站都可以转而使用一系列以数据为中心的XML文档和XSL样式表。

例如,下面是个描述航班信息的文档:

   <FlightInfo>
<Airline>ABC Airways</Airline> provides <Count>three</Count>
non-stop flights daily from <Origin>Dallas</Origin> to
<Destination>Fort Worth</Destination>. Departure times are
<Departure>09:15</Departure>, <Departure>11:15</Departure>,
and <Departure>13:15</Departure>. Arrival times are minutes later.
</FlightInfo>

从下面的XML文件和一个简单的样式表中创建这个文档:

   <Flights>
<Airline>ABC Airways</Airline>
<Origin>Dallas</Origin>
<Destination>Fort Worth</Destination>
<Flight>
<Departure>09:15</Departure>
<Arrival>09:16</Arrival>
</Flight>
<Flight>
<Departure>11:15</Departure>
<Arrival>11:16</Arrival>
</Flight>
<Flight>
<Departure>13:15</Departure>
<Arrival>13:16</Arrival>
</Flight>
</Flights>

4.2 以文档为中心的文档 (Document-Centric Documents)

以文档为中心的文档通常是供人消费的。例如书籍、email、广告以及几乎所有人工写成的XHTML文件。其特性为结构不太或根本不规则、数据粒度大(larger grained data)(最小的独立数据单位可能存在于包含混合内容的元素甚至整个文档本身),混合内容多。同级元素或PCDATA出现的次序一般来说总是非常重要的。

以文档为中心的文档通常是以XML手工写成,或从其他格式(如RTF, PDF, SGML)转换到XML,与以数据为中心的文档不同,它们的来源通常不是数据库。(将数据插入到模板而得到的文档是以数据为中心的;更多信息请看4.1节末尾部分)。将各种格式转换为XML的软件信息,请参阅XML软件相关链接

例如,下面这个产品说明是以文档为中心的:

   <Product>
<Intro>
The <ProductName>Turkey Wrench</ProductName> from <Developer>Full
Fabrication Labs, Inc.</Developer> is <Summary>like a monkey wrench,
but not as big.</Summary>
</Intro>
<Description>
<Para>The turkey wrench, which comes in <i>both right- and left-
handed versions (skyhook optional)</i>, is made of the <b>finest
stainless steel</b>. The Readi-grip rubberized handle quickly adapts
to your hands, even in the greasiest situations. Adjustment is
possible through a variety of custom dials.</Para>
<Para>You can:</Para>
<List>
<Item><Link URL="Order.html">Order your own turkey wrench</Link></Item>
<Item><Link URL="Wrenches.htm">Read more about wrenches</Link></Item>
<Item><Link URL="Catalog.zip">Download the catalog</Link></Item>
</List>
<Para>The turkey wrench costs <b>just $19.99</b> and, if you
order now, comes with a <b>hand-crafted shrimp hammer</b> as a
bonus gift.</Para>
</Description>
</Product>

4.3 数据、文档和数据库(Data, Documents, and Databases)

在现实当中,以数据为中心和以文档为中心的文档之间的差别不一定很明显。例如,另一种以数据为中心的文档比如发票,可能含有大粒度的、结构不规则的数据比如零件说明;另一种以文档文中心的文件如用户手册,可能包含细粒度的结构规则的数据(通常为元数据)比如作者和修订日期。其它例子包括法律和医学文书,虽然以松散的形式写成但是却包含离散的数据块例如日期、名称和操作程序,出于法规的原因通常要以完整的文件形式存储。

除此之外,弄清文件的这两种特点有助于选择数据库的类型。一般来说,将数据存储于传统的数据库,例如关系型,面向对象型或层次型数据库。这可由第三方的中间件完成或由数据库本身提供内在支持。对于后者,该数据库被称作支持XML的(XML-enabled)。文档可被存储在原生(native)XML数据库(专为存储XML而设计的数据库)或内容管理系统(建在原生XML数据库之上专门用来管理文档的程序)。

这些原则并不是绝对的。如果对XML特有的功能不很看重,数据,特别是半结构化的数据可以存储在原生XML数据库,文档也可以存储到传统数据库。何况传统数据库与原生XML数据库之间的界限越来越模糊,传统数据库增加了原生XML的能力,而原生XML数据库增加了对文档存储在外部(通常为关系型)数据库的支持。

本文剩下的部分就有关数据(第5节)和文档(第6节)的存储和读取的策略与问题展开讨论。关于最新的XML数据库产品,请见XML数据库产品

5.0 数据的存取(Storing and Retrieving Data)

为了在XML文件和数据库之间交换数据,必须将XML文件的schema(DTD,XML Schema, RELAX NG等)映射到数据库的schema。用于数据转换的软件位于这种映射的上层。该软件可以使用XML Query语言(如XPath,XQuery,或其他专用语言)或简单地按照映射(SELECT * FROM Table的XML对应形式)转换数据。

对于后者,文档的结构必须完全符合映射所要求的结构。由于通常不易做到这点,使用这种策略的产品一般要和XSLT一起使用。在数据转换到数据库之前,先将文件按照映射所要求的结构进行转换,然后转存数据。相应地,数据从数据库中取出以后,结果文件要被转换成应用程序所需的结构。

5.1 映射[XML]文件Schema到数据库Schema (Mapping Document Schemas to Database Schemas)

文件schema到数据库schema的映射是在元素类型、属性和文本上进行的。这时几乎总是忽略物理结构(例如实体、CDATA部分及编码信息)及某些逻辑结构(如处理指令、注释以及元素和PCDATA在父元素内出现的顺序)。这样做是自然而然的,因为数据库和应用程序只需关心XML文件中的数据。例如,在上述的销售订单中,客户代号是在CDATA部分,还是外部实体中,或直接就是PCDATA并不重要,同样,客户代号出现在订货日期之前或之后也无关紧要。

这种方法的一个后果是能否保证文件有“往返车票” -- 将文件中的数据存入数据库后,又从数据库中的数据重新构建文件,得到的文件往往和原来的文件不同(哪怕从最简单的角度来讲)。这种情形是否可以接受取决于你的要求,在选择软件时要考虑到这一点。

将一个XML文件的schema映射到数据库的schema有两种方法:基于表格的映射对象-关系映射

5.1.1 基于表格的映射 (Table-Based Mapping)

许多转换XML到数据库的中间软件都采用基于表格的映射。它把XML文件看作一个(或一组)表格,也就是说,XML文件的结构必须是下面这种样子,如果只是单一表格的话,就不再需要<database>元素和其他<table>元素:

   <database>
<table>
<row>
<column1>...</column1>
<column2>...</column2>
...
</row>
<row>
...
</row>
...
</table>
<table>
...
</table>
...
</database>

根据所用软件的不同,可以将各字段数据以子元素的形式或以属性的形式存储,同样也可以指定这些元素或属性的名字。此外,采用基于表格映射方式的软件还可能允许在文件开始的地方包含表格或各字段的元数据,或者将其作为各表格或元素的属性。注意这里所说的“表格”是泛指的表格。当将数据从数据库中转到XML文件时,“表格”可以是任何结果集,反之,“表格”可以是普通的表格或可更新的视图。

基于表格的映射对存取关系型数据比较适用,比如在两个关系型数据库之间转换数据。其明显不足就是不适于格式不符的XML文件。

5.1.2 对象-关系映射 (Object-Relational Mapping)

所有支持XML的关系型数据库和某些中间件都可以使用对象-关系的映射方式。它将XML文件中的数据视为特定的对象树的模型。在这个模型中,元素及其类型、元素内容或混合内容(复合元素类型)通常被视为类。只具有PCDATA内容的元素(简单元素类型)、属性以及PCDATA都被当作简单属性。然后通过传统的对象-关系映射技术或 SQL 3的对象视图将该模型映射到关系型数据库。也就是说,类被映射到表格,简单属性被映射到字段,而值为对象属性被映射为成对的主键/外键(primary key/foreign key)。

(所谓“对象-关系映射”有些名不副实。因为对象树可以被直接映射到面向对象型和层次型数据库,然而,但是由于大多数使用这种映射方式的主流产品使用的其实是关系型数据库,所以“对象-关系映射”也就广为人知。)

我们在理解这种映射所用的对象模型的时候要知道,这个对象模型不是文件对象模型(DOM)。所有XML文件的DOM都是一样的,而上述描述文件数据的模型对于每个DTD所定义的XML文件都不一样,例如,上述销售订单的模型是一个由四个类所组成的对象树--SalesOrder, Customer, Item, 和Part, 如下图所示:

                    SalesOrder
/    |    \
Customer   Item   Item
|      |
Part   Part

在由同一个文件产生的DOM中,对象树的组成是元素、属性和文本:

                          元素 --- 属性
(SalesOrder) (SONumber)
____/   /   \   \_____
/       /     \        \
元素      文本     元素     元素
(Customer) (OrderDate)  (Item)    (Item)
|                    |         |
etc.                 etc.      etc.

模型中的对象是否被实例化要取决于所用的软件。有些软件允许依据模型产生类,然后可以在程序中使用由这些类所产生的对象。在这些产品中,数据是在XML文件 - 对象 -数据库之间传递的。其他产品是直接在XML文件和数据库之间进行数据转换的,对象只是作为这种过程的可视化帮助工具。生成这些中间对象是否有用完全取决于你的应用程序。

(根据Sun的 Java Architecture for XML Binding,XML文件与对象的绑定通常被称为XML数据绑定(XML data binding),有些产品支持XML数据绑定,其中许多还可以在对象和数据库之间进行数据交换,更多的信息,请看 XML数据绑定相关资源 XML Data Binding Resources.)

各种产品对对象-关系映射的具体支持各不相同。例如:

  • 所有产品都支持从复合元素类型到类,以及从简单元素类型和属性(attributes)到[类的]属性(properties)的映射。
  • 所有(?)产品都允许你指定是否将根元素映射到对象模型或数据库。如果你想在同一个XML文档中存储多个顶级对象时,这个包装元素(wrapper element)就显得有用了。例如,如果你想在同一个XML文件中存储多个销售订单,就必须把它们包装在一个根元素内。

    当使用基于表格的映射的XML文件有多个表格组成时,这个包装元素就相当于<database>元素,他的存在只是为了满足 XML 文件只能有一个根元素的要求。少数产品允许你就像指定包装元素那样,在较低的层次上指定[某些元素是否被映射 -译注]。

  • 大多数产品允许你说明将[对象的]属性(properties)映射到XML文件中的属性(attribute)还是子元素。某些产品还允许你混合使用属性和子元素,其他的只允许你使用两者之一。
  • 大多数产品允许XML文件和数据库的名字可以不同。少数产品要求必须使用相同的名字。
  • 大多数产品允许你说明子元素在父元素中的出现次序,尽管如此,仍然不可能建立多个内容模型。幸好对于大多数以数据为中心的文档而言,所支持的内容模型已经足够。(当文件需要验证时,子元素的次序才显得相当重要。)
  • 某些产品允许你将复合元素类型映射到简单属性 (properties)。当某个元素包含混合内容(例如销售订单中的< Description>元素)时,这点相当有用。尽管<Description>元素像XHTML一样包含子元素和文本,但最好还是将这个description视为单个属性,而不是把它分成许多碎块。
  • 支持混合内容中的PCDATA映射的产品不多。

关于对象-关系型映射的详细描述,请看第三部分“将DTD映射到数据库”。

5.2 查询语言(Query Languages)

许多产品都是按照它们所建的模型直接进行数据交换。由于XML文件的结构和数据库的结构一般不同,这些产品一般都使用了或包括了XSLT样式表。这就允许用户在数据被交换到数据库之前(或相反)根据模型规定的格式对文件进行转换。由于XSLT处理的代价较高,某些产品只提供有限几种这样的转换。

这个问题的长久解决方案就是设计一种可以返回XML[文件]的查询语言。目前来看(2002年11月),大多数这种语言都依赖于在模板中嵌入 SELECT 语句。到 XQuery 最后定稿时这个局面有望得到改变,正如主流的数据库产品提供商已经做的那样。不幸的是,几乎所有的XML查询语言(包括 XQuery1.0 和 SQL/XML的最初发布版本)对文件都是只读的,所以短期来说,最需要的是能够插入、更新、删除数据的手段。(从长远来看,XQuery 和 SQL/XML 将会增加这种功能。)

5.2.1 基于模板的查询 (Template-Based Query)

大多数从数据库读取XML的查询语言是基于模板的,这些语言并没有预先定义XML文件和数据库之间的映射,相反,是将SELECT语句嵌入到模板中,由数据交换软件对结果进行处理。例如,下面的模板(并不是真正产品中的)使用包含了SELECT语句和$column-name值的< SelectStmt>元素来决定将结果放在何处。 

<?xml version="1.0"?>
<FlightInfo>
<Introduction>The following flights have available seats:</Introduction>
<SelectStmt>SELECT Airline, FltNumber, Depart, Arrive FROM Flights</SelectStmt>
<Flight>
<Airline>$Airline</Airline>
<FltNumber>$FltNumber</FltNumber>
<Depart>$Depart</Depart>
<Arrive>$Arrive</Arrive>
</Flight>
<Conclusion>We hope one of these meets your needs</Conclusion>
</FlightInfo>

这个模板处理之后的结果可能是:

<?xml version="1.0"?>
<FlightInfo>
<Introduction>The following flights have available seats:</Introduction>
<Flights>
<Flight>
<Airline>ACME</Airline>
<FltNumber>123</FltNumber>
<Depart>Dec 12, 1998 13:43</Depart>
<Arrive>Dec 13, 1998 01:21</Arrive>
</Flight>
...
</Flights>
<Conclusion>We hope one of these meets your needs.</Conclusion>
</FlightInfo>

基于模板的查询语言非常灵活。尽管各个产品的功能不尽相同,但有一些共同特性:

  • 可以将返回结果放在输出文件中的任何地方,包括作为后续SELECT语句的参数。
  • 可由程序构建 for 循环和 if 语句。
  • 可以定义变量和函数。
  • 可通过HTTP参数实现SELECT语句的参数化(parameterization)。

从关系数据库中将数据转换到XML文件时,几乎一定要用到基于模板的查询语言。虽然有些产品也将基于模板的查询语言用于XML文件到关系数据库的数据转换,这时所用的并不是完整的模板语言。相反,他们使用的是上面所述的基于表格的映射。

5.2.2 基于SQL的查询语言 (SQL-Based Query Languages)

基于SQL的查询语言使用的是经过修改的SELECT语句,[查询]结果被转换为XML。目前已经有一些私有的基于SQL的语言可用。通过简单地使用嵌套的SELECT语句,就可直接转换成符合对象 - 关系映射的嵌套的XML。或者使用SQL 3的对象视图也可直接转换成XML。最后还可使用 OUTER UNION 语句和特殊标记来决定怎样将结果转换成 XML。

除了这些私有语言以外,2000年一些公司联合提出了 SQL 的 XML 扩展标准,现在已经成为被称为 SQL/XML 的 ISO SQL 标准的一部分;最终可望于2003年得到批准。 SQL/XML 引入了一种 XML 数据类型,并且为 SQL 增加了几个函数,可以从关系型数据构造 XML 元素和属性。

例如,下面的查询

SELECT Orders.SONumber,
XMLELEMENT(NAME "Order",
XMLATTRIBUTES(Orders.SONumber AS SONumber),
XMLELEMENT(NAME "Date", Orders.Date),
XMLELEMENT(NAME "Customer", Orders.Customer)) AS xmldocument
FROM Orders

构造了具有两个字段的结果集。第一个字段为销售订单号,第二个字段为一个 XML 文件。每一行有一个 XML 文件,它来自 Orders 表中相应的数据。例如,从订单号 123 生成的 XML 文件是:

<Order SONumber="123">
<Date>10/29/02</Date>
<Customer>Gallagher Industries</Customer>
</Order>

在网上很难找到有关 SQL/XML 的资料。关于其介绍和(可能)过时的一些资料,请参考 SQLX Group 网站 。 SQL/XML 标准的拷贝可从 sqlstandards.org 的FTP 目录得到(ftp://sqlstandards.org/SC32/WG3/Progression_Documents/Informal_working_drafts/)。你要找的文件名应当是WD5-14-xml-yyyy-mm.pdf 或 wd5-14-xml-yyyy-mm.pdf, 这里 yyyy-mm 是标准的年月。比如2003年11月的最新标准的文件名为 WD5-14-xml-2003-09.pdf。注意 sqlstandards.org 的 FTP 站点经常宕机,你可能要不断尝试才行。另外还可以在 Google 上搜索 XMLFOREST 或 XMLAGG。

5.2.3 XML Query Languages

基于模板和基于SQL的查询语言只能用于关系数据库,与此不同,XML查询语言可用于任何XML文档。为了把它用于关系数据库,必须把数据库中的数据看作XML的模型,这样才能对虚拟的XML文件进行查询。

对于XQuery,基于表格的映射或对象-关系型映射都可以使用。当使用基于表格的映射时,各个表格被视为单独的文件,像SQL中的一样, 这些表格的结合(joints)则在查询[语句]本身中指明。如果使用的是对象-关系型映射,各个表格的层次被当作单个文件,而[表格的]结合在映射中说明。对于大多数关系数据库而言,似乎基于表格的映射更为常用,这是因为它的实现似乎更简单些,SQL的用户对此也更为熟悉。

如果使用XPath在多个表格中进行查询,就必须使用对象-关系映射,这是因为XPath不支持多个文档的联合。因此,如果使用基于表格的映射,也许每次只对一个表格进行查询最好。

5.3 原生XML数据库的数据存储(Storing Data in a Native XML Database)

还可以将XML文件中的数据存储在原生XML数据库(native XML database)中。这么做有几个理由。首先,当你的数据是半结构化的数据时。也就是说,它的结构是普通的,但是如果将其映射到关系数据库,结果是要么出现大量空值(null)的字段,要么表格的数量过多,浪费空间或效率低下。虽然半结构化的数据可存储到面向对象的或层次型数据库中,你还可以选择将它以XML文件的形式存储于原生XML数据库。

将数据存储在原生XML数据库中的第二个理由是读出速度。根据XML数据库存储数据的物理方式的不同,数据的读出速度可以做到比关系型数据库[的读取速度]快得多。其原因是,原生XML数据库对整个文件一起进行物理存储,和[表示]文件各个部分的物理(而不是逻辑)指针可采用同一存储策略。这就可以不使用连接(joins)或只使用物理连接读取文件,无论哪种情况都比关系型数据库所用的逻辑联结要快。

以上述销售订单文件为例。在关系型数据库中,它可能被存为四个表格 -- SalesOrders, Items, Customers, 和 Parts -- 读取文件时需要将这些表格结合起来。在原生XML数据库中,整个文件可被存储在磁盘的一个地方,在读取文件或其片断时只需要一次查找和一次读取操作。关系数据库在读取数据时则需要四次查找以及至少四次读取操作。

这样做的一个明显缺点就是,只有数据的读取顺序和写入磁盘的顺序相同时,才可以提高速度。如果你想要的数据视图不同,比如只想要客户及其订单列表,性能可能比关系数据库更差。所以,如果你的应用中是单个数据视图为主,为了提高性能,才可以考虑将数据存储到原生XML数据库。

将数据存储在原生XML数据库中的第三个理由是你想利用XML的独有特性,如执行XML查询。由于今天以数据为中心的应用几乎没有这样做的,而且关系数据库正在逐步支持XML查询语言,这个理由越来越不充分。

将数据存储在原生XML数据库中的一个问题是,大多数原生数据库只能以XML[的形式]返回数据。(支持元素和属性到应用程序变量绑定的只是少数)。如果你的应用程序需要另一种数据格式(很有可能),使用数据之前必须先解析XML。对本地的应用程序而言显然是个缺点,而这种前期准备在(比如)ODBC中就不存在。对于将XML作为数据载体使用的分布式应用程序而言,这个问题不很严重,因为不管用的是哪种数据库,这种前期工作必须要有。

5.4 数据类型,空值,字符集,诸如此类 (Data Types, Null Values,Character Sets, and All That Stuff)

本节将就在传统数据库中存储XML文件数据的相关问题展开讨论。(有关数据类型和二进制数据存储的讨论同样适用于原生XML数据库)总的说来,你无法选择数据交换软件解决这些问题的方式。然而,你应当明白这些问题确实存在,这样会有助于你选择软件。

5.4.1 数据类型 Data Types

如果单从字面上讲,XML并不支持任何数据类型。除了非解析实体,XML文件中的所有数据都是文本,尽管它可能表示日期或整数等其他类型。一般来说,数据交换软件负责把(XML文件中的)文本转换成(数据库中的)其他数据类型,反之亦然。

至于软件如何确定该进行怎样的转换各不相同,常见的有两种方法。第一种方法是软件根据数据库模型来确定数据类型,因为它在运行时总是可用的。(而XML模型在运行时就不一定有,甚至根本就不存在)。第二种方法就是由用户明确指定数据类型,比如映射信息。它可以由用户写出,或者自动从数据库模型或XML 模型中产生。

类型转换时的另一个问题是,究竟能够识别(从 XML 转换)或创建什么文本格式。大多数情况下,能够被识别为特定数据类型的文本格式数目很有限,比如某个JDBC驱动程序支持单一的、特定的格式。日期看起来最容易出问题,因为它的格式可能非常多。而数字在各种语言中的格式也不尽相同,也有可能出问题。

5.4.2 二进制数据 (Binary Data)

二进制数据在XML文件中的存储方法一般有三种:非解析实体和Base64编码(一种 MIME 编码方式,将二进制数据映射到US-ASCII码的一个子集[0-9a-zA-Z+/] - 见RFC 2045 );十六进制编码(每二进制字节用两个十六进制字符[0-9a-fA-F]表示)以及非解析实体(二进制数据存储在与该XML分离的物理实体内)。

二进制数据的最大问题在于数据转换产品对它的支持不够,所以要检查你的软件是否支持。另一个问题是,大部分(全部?)数据转换产品都不存储符号和实体声明。这样,将 XML 中的数据转换到数据库中时,与某些实体对应的符号就会丢失。(关于符号的更多信息详见 XML 标准的 4.7 部分)。

5.4.3 空数据 (Null Data)

在数据库术语中,空数据(null data)意味着没有数据。这和值为0的数字或长度为零的字符串区别非常大。例如,假设你收集到的是气象数据,如果温度计不能工作,则数据库中存储的就是null而不是0,否则情况就完全不同了。

XML也支持这种空数据的概念(通过可选(optional)元素或属性)。如果一个可选元素或属性的值为空,就不会包含在文件内。而在数据库中,值为零长度字符串的属性和空元素并不是null:它们的值为零长度的字符串。

当你将XML文档的结构映射到数据库(或反过来)时,你当特别留意,可选元素类型或[空值]属性会被映射到允许空值的字段(nullable columns),反之亦然。如果不是这样的话,可能会产生插入错误(当转换数据到数据库时)或非法文件错误(从数据库中取出数据时)。

与数据库专业相比,XML社区对null含义的表示法更为自由-- 特别是许多XML用户都认为零长度字符串的属性和空元素是“空(null)”的,所以你应该检查一下你的软件是如何处理这种情况的。有些软件为用户提供了选择,可以定义XML中如何表示"null",以及支持XML Schema中的 xsi:null属性。

5.4.4 字符集 (Character Sets)

根据定义,除了某些控制字符,XML文件可包含任何Unicode字符。不过,许多数据库对Unicode的支持很有限或根本就不支持,而且需要特殊的配置才能处理非ASCII字符。如果你的文件包含非ASCII字符,要确保你的数据库及数据转换软件对这些字符是否支持及如何支持的。

5.4.5 处理指令和注释 (Processing Instructions and Comments)

处理指令和注释并不是XML文档“数据”的一部分,大多数(全部?)数据转换软件都不能处理它们。问题在于这些东西可能出现在XML文档中的任何地方,所以不容易进行基于表格或对象-关系型映射。(一个明显行不通的方案是为这些处理指令和注释建立一个表格,在其他表格加上指向这个表格的外部键(foreign key),每当处理别的表格时,就检查这些表)。所以大多数数据转换软件会简单地忽略它们。如果处理指令和注释对你相当重要,就要检查所用软件对此能否处理及处理方法。或者可以考虑使用原生XML数据库(native XML database)。

5.4.6 对标记的存储 (Storing Markup)

正如5.1.2 一节所提到的, 有时候需要在数据库中存储混合内容的元素而无须进一步解析。这时,标记被存储为标记字符(markup is stored with markup characters)。然而,问题出现在如何存储那些不作为标记使用的标记字符。在XML文档中,这些是以 lt, gt, amp, quot 和 apos 实体存储的。在数据库中也可这么做。例如,下面的description:

<description>
<b>Confusing example:</b> &lt;foo/&gt;
</description>

在数据库中可存为:

<b>Confusing example:</b> &lt;foo/&gt;

这样做的一个问题是一些非XML查询语言,如SQL不会在表中搜寻标记和实体的用法并将其正确翻译。这样,如果你打算用SQL来搜索字符串“<foo/>”,你应当知道实际的搜索字符串是“&lt;foo/&gt;”。

另外,如果实体引用被扩展了,数据转换软件无法区别标记和实体。例如,如果上述例子在数据库中按照下面的形式存储,则软件就无法知道<b> , </b> 和<foo/>到底是标记还是文本。

  <b>Confusing example:</b> <foo/>

解决这个问题的长久之计就是支持XML的数据库(XML-aware database),在那里,对真正的标记和看起来很像标记的东西的处理方式是不同的。非XML应用程序处理XML时,总是会出现这种问题。

5.5 从关系[数据库]Schema产生DTD或相反 (Generating DTDs from Relational Schema and Vice Versa)

在XML文件与数据库之间进行数据转换时经常遇到的一个问题是如何从数据库schema产生DTD或相反。在解释如何做之前,必须指出这是设计阶段的任务。其原因是大多数以数据为中心的应用以及几乎所有的盘点应用软件(vertical applications)都是在已知的DTD或数据库模型上工作的,因此,不会在运行时产生schema。再者,如下所述,schema的产生过程不十分严格。如果应用程序需要在数据库中随机存放XML文件,则应考虑使用使用原生XML数据库,而不是在运行时产生schema.

从DTD产生关系模型(或相反)的最简单方法就是直接借助于对象-关系映射,它有几个可选特性。对于面向对象的数据库也有相似的途径。(关于每种方法的逐步演示,请看Mapping DTDs to Databases.)。

从DTD产生关系模型:

  1. 对每个复合数据类型, 创建一个表格和主键字段
  2. 对每个混合内容的元素类型,另外建一个表用来存储PCDATA,通过其父表格的主键与父表格相连。
  3. 对该种数据类型的每个单值属性,以及每个单次出现简单子元素在表中建一个字段。如果子元素或属性是可选的,将该字段设置为可为空值的字段(nullable)。
  4. 对每个多值属性(multi-valued attribute)和每个多次出现的简单元素,单独创建一个表,通过其父表格的主键与父表格相连
  5. 对每个复合类型的字元素,通过其父表格的主键将父表与该子元素类型的表相连。

从关系[数据库]模型产生DTD:

  1. 为每个表创建一种元素类型。
  2. 对表中的每个数据(非键)字段以及主键字段,在元素类型上增加一个属性或在内容模型上增加一个PCDADA型的字元素。
  3. 对于每个引用该主键的表格,在内容模型上增加一个子元素,继续递归处理表格。
  4. 对于每个外来键,在内容模型上增加一个子元素,继续递归处理外来键表格。

这些过程有一些不足,其中许多都可手工解决,比如名称冲突及指定字段数据类型和长度。(DTD不包含数据类型信息,所以不可能预知数据库中的数据类型。注意,从XML Schema文件可以预测数据类型和长度。)

更加严重的问题是,XML文件所用的数据“模型”通常和数据库中所用的高效率的模型不同,(实际上更为复杂)。请看如下XML片断:

<Customer>
<Name>ABC Industries</Name>
<Address>
<Street>123 Main St.</Street>
<City>Fooville</City>
<State>CA</State>
<Country>USA</Country>
<PostCode>95041</PostCode>
</Address>
</Customer>

从DTD产生关系模型的一般过程来看,通常会产生两个表:一个是customers,另一个是addresses。然而,大多数情况下把 address存放在customer表中更为合理。

<Address>元素就是包装元素(wrapper element)的典型例子。采用包装元素的原因有二:首先,这种结构使得文档更容易理解;其次,它可作为一种数据类型使用。例如,可以将<Address>元素传给一个例程,该例程可以将所有地址转换为Address对象,不管它出现在哪里。

虽然包装元素在XML中很有用,但被映射到数据库中的时候会增加额外的结构,很容易出问题。因此,在产生关系模型之前,一般应将其从DTD中除去。由于DTD一般是不允许改变的,而在映射是又不包含包装元素,所以往往会导致实际文档与数据转换软件所要求的文档不匹配。对此可以在运行时转换文件(比如使用XSLT):数据被转到数据库之前去掉包装元素,转出后在加上它。

尽管有这些不足,上述步骤还是为DTD和关系模型之间的转换提供了一个起点,对于大型系统尤为如此。

6.0 文件的存取 (Storing and Retrieving Documents)

XML文件的存储方式有两大类:存储于文件系统或作为BLOB存储于关系数据库以获得有限的XML功能,或者将其存储于原生XML数据库。

6.1 将文件存储于文件系统 (Storing Documents in the File System)

如果你的文件比较简单,数量不多,最简便的方法就是存储于文件系统。以后可以使用grep之类的工具进行查询或sed来修改。(对XML文件进行全文检索显然不够精确,比如很难区分文本和标记,而且无法理解实体的用法。然而,对于小型系统,这还是可以接受的)如果你想实现简单的事务管理(transaction control),可以将文件放在诸如CVS或RCS等版本管理系统中。

6.2 将文件存储于BLOB (Storing Documents in BLOBs)

另一种较为复杂的方法就是将文件存储于关系数据库中的BLOB。这就利用了数据库的一些优点:事务管理、安全、多用户访问等。此外许多关系数据库提供的检索工具可以进行全文检索、近似检索、同义词检索和模糊检索。其中某些工具将会支持XML,这样就可消除将XML文件作为纯文本检索所带来的问题。

如果以BLOB的形式存储XML文件,即使数据库不支持对XML的检索,你也很容易实现自己的XML检索。方法之一是创建两个表:索引表(DB2中的side table)和文件表。文件表包含一个主键和一个存储文件的BLOB字段,索引表内有一个已建立索引值字段以及一个外来键指向文件表中的主键。

文件被存在数据库中之后,就可以搜索所有已建索引的元素和属性实例。每个实例及文件的主键都存于索引表中。这样已建立索引的字段使应用程序可以快速检索某个元素或属性值并获取相应的文件。

举例来说,假如你有一些符合下列DTD的文件,希望建立作者的索引:

<!ELEMENT Brochure (Title, Author, Content)>
<!ELEMENT Title (#PCDATA)>
<!ELEMENT Author (#PCDATA)>
<!ELEMENT Content (%Inline;)> <!-- Inline entity from XHTML -->

你可以用下表来存储它:

   Authors                     Brochures
----------------------      ---------
Author     VARCHAR(50)      BrochureID INTEGER
BrochureID INTEGER          Brochure   LONGVARCHAR

假如你在数据库中插入了一个brochure,程序就会在Brochures表中插入该 brochure,然后寻找<Author>元素,将它的值和brochure ID存入Authors表中。以后就可通过简单的SELECT语句得到某个Author的所有brochure。比如想得到author为Chen的所有 brochure,就可以执行下面的语句:

   SELECT Brochure
FROM Brochures
WHERE BrochureID IN (SELECT BrochureID FROM Authors WHERE Author='Chen')

更复杂的索引表可包含四个字段:元素类型或属性名、(元素或属性)类型、值和文件ID。这样就可在一个表中存放多个标记[文件], 并按名字、类型和值建立索引。写个操作这个表的SAX程序应该不是件难事。

6.3 原生XML数据库 (Native XML Databases)

上述的简单系统所提供的功能如果不能满足你的需要,你可以考虑使用原生XML数据库(native XML database)。原生XML 数据库是专用于存储XML文件的数据库。和其他数据库一样,它支持事务管理、安全、多用户访问、编程API和查询语言等。与其它数据库的唯一区别就是其内部模型是基于XML的,而不是其他的模型--如关系模型。

显然原生XML数据库最适于存储以文档为中心的文件。这是由于原生XML数据库保留了文件[元素?]顺序、处理指令、注释、CDATA块以及实体引用等,而支持XML的数据库(XML-enabled database)无法做到。此外,原生数据库支持XML查询语言,你可以对它提这样的问题:“找出所有第三段内包含粗体字的文件”,用SQL显然很难进行这样的查询。

原生XML数据库还适用于存储那些“天然格式”为XML的文件,而不管这些文件包含什么内容。例如,电子商务系统消息所用的XML文件。尽管这些文件可能是以数据为中心的,而作为消息来说它们的天然格式是XML。这样当它们被存入消息队列后,建立在原生XML数据库上的消息队列使用起来更为方便。原生XML数据库保留了XML的特性比如XML查询语言,通常能更快地取出整条消息。Web cache是这类应用的另一个例子。

原生XML数据库的其他用途是存储半结构化数据、在某种特定情形下提高存取速度以及存储没有DTD的文件(良构的文件)。前两种已经在5.3 原生XML数据库的数据存储中叙述过。而后一种用非XML的数据库是做不好的。也就是说,原生XML数据库无须事先配置即可接受和存储任何XML文件。将XML文件中的数据转换到关系型或面向对象型数据库必须首先建立映射和数据库模型。无须事先配置对于搜索引擎之类的应用程序来说是有利的,因为没有任何DTD能适用于所有搜索文档。

6.3.1 什么是原生数据库(Native XML Database)?

"native XML database" 这个术语首先在 Software AG 为 Tamino 所做的营销宣传中露面。也许由于它的成功,后来这个术语在同类产品的开发商那里成了通用叫法。它是一个营销术语,从来没有正式的技术定义,这是它的一个缺陷。

有一个接近的定义(出自XML:DB mailing list的一个成员)这样定义原生XML数据库(native XML database):

它为 XML 文档(而不是文档中的数据)定义了一个(逻辑)模型,并根据该模型存取文件。这个模型至少应包括元素、属性、PCDATA 和文件顺序。这种模型的例子有XPath数据模型、XML Infoset 以及 DOM 所用的模型和SAX 1.0的事件。

它以 XML 文件作为其基本(逻辑)存储单位,正如关系数据库以表中的行作为基本(逻辑)存储单位。
它对底层的物理存储模型模型没有特殊要求。例如,它可以建在关系型、层次型或面向对象的数据库之上,或者使用专用的存储格式,比如索引或压缩文件。

该定义的第一部分与其他类型数据库的定义相似,都是关于数据库所用的模型的。不过,原生 XML 数据库所能存储的信息比模型中定义的多。例如,它可支持基于XPath 数据模型的查询,但所用的存储格式是纯文本。CDATA 部分和实体用法也可存储在数据库中,但是模型中没有包括。

定义的第二个部分是说原生数据库的基本存储单位是 XML 文件。看起来似乎也可存储 XML 文件片断,但几乎所有的原生 XML 数据库都是以文件方式存储的。

(基本存储单位就是可以容纳一份数据的最低级的上下文 (context),相当于关系数据库中的行。它的存在并不妨碍以更小的数据单位来读取数据,比如文件片断或个别元素,同样也不影响将不同文件中的片断进行组合。从关系数据库的术语来讲,相当于数据虽然以行的形式存放,并不意味着无法读取某个字段的值,或从现有的数据行创建新一行数据。)

该定义的第三部分讲的是底层的数据存储格式并不重要。确实如此,正如关系数据库所使用的物理存储格式与数据库是不是关系型之间毫无关系。

6.3.2 原生XML数据库的结构 (Native XML Database Architectures)

原生XML数据库的结构可分为两大类:基于文本的和基于模型的。

6.3.2.1 基于文本的原生XML数据库(Text-Based Native XML Databases)

基于文本的原生XML数据库将XML作为文本存储。它可以是文件系统中的文件、关系数据库中的BLOB或特定的文件格式。(事实上,就其能力来说,一个增加了支持CLOB(Character Large Object)字段的XML处理功能的关系数据库也可以是原生XML数据库了。)

索引对所有基于文本的原生XML数据库来说都是一样的,它可以使查询引擎很方便地跳到XML文件内的任何地方。这就可以大大提高数据库存取文件或文件片断的速度。这是因为数据库只需进行一次检索、磁头定位,再假如所读的文件在磁盘上是连续[存储]的话,只需一次读盘就可读出整个文件或文件片断。相反,如果像关系数据库或基于模型的原生XML数据库那样,文件由各个部分组合而成,就必须要进行多次查找定位和多次读盘动作。

从这个意义上讲,基于文本的原生XML数据库与层次结构的数据库很相似,当存取预先定义好层次的数据的时候,它比关系数据库更胜一筹。和层次结构的数据库一样,当以其他形式比如转置层次存取数据时,原生XML数据库也会遇到麻烦。这个问题的严重程度尚未可知,很多关系数据库都使用逻辑指针,使相同复杂度的查询以相同的速度完成。由此看来这确实是个问题。

6.3.2.2 基于模型的原生XML数据库 (Model-Based Native XML Databases)

第二类原生XML数据库是基于模型的原生XML数据库。它们不是用纯文本存储文件,而是根据文件构造一个内部模型并存储这个模型。至于模型究竟怎样存储取决于数据库。有些数据库将该模型存储于关系型和面向对象的数据库中,例如在关系型数据库中存储DOM时,就会有元素、属性、PCDATA、实体、实体引用等表格。其他数据库使用了专为这种模型作了优化的专有存储格式。

(Mark Birbeck 在 1998年12月的 XML-L 邮件列表中描述了一个建立在关系型数据库上的简单的、基于 模型的原生 XML 数据库系统,该系统用了5个表 - 属性定义、元素/属性关联、内容模型定义、属性值、元素值 (PCDATA 或指向其它元素的指针),以及只包含元素、属性、文本和文件顺序的模型。参见标题为 "Record ends, Mixed content, and storing XML documents on relational database" 和 "storing XML documents on relational database"的信件。)

建立在其他数据库之上的基于模型的原生XML数据库的文件存取性能与这些数据库相似,原因显而易见:其存取要依赖这些数据库。但是这个数据库,特别是建立在其他数据库之上的原生XML数据库的设计有很大的变化余地。例如直接以 DOM 方式进行对象-关系映射的数据库系统在获取节点的子元素时必须单独执行 SELECT 语句。另一方面,这种数据库大多对存取模型和软件作了优化。例如 Richard Edwards 在 system for storing the DOM in a relational database一文中曾经描述只用一个SELECT语句就可获取任意文件片断(或整个文件)。

使用专用存储格式的基于模型的原生XML数据库如果以文件的存储顺序读取文件,其性能与基于文本的原生XML数据库相似。这是因为这种数据库大多在节点间使用了物理指针,这样其读取性能和读取文本差不多。(究竟哪个快一些要取决于数据格式。如果返回文本格式,显然基于文本的系统要快一些;如果希望返回的是DOM,假如该模型很容易映射到DOM,则基于模型的系统更快。)

与基于文本的原生XML数据库一样,如果数据的读取顺序和存储顺序不同,基于模型的原生XML数据库很容易出现性能上的问题。这两种类型的数据库到底哪个快一些仍不是很清楚。

6.3.3 原生XML数据库的特性 (Features of Native XML Databases)

本节简单讨论原生XML数据库的一些特性,有助于大致了解其现状和未来。

6.3.3.1 文件集 (Document Collections)

许多原生XML数据库都支持集合(collection)的概念,其作用相当于关系数据库中的表和文件系统中的文件夹,例如你想在原生XML数据库中存储销售订单,就可以定义一个销售订单的集合,这样对销售订单的查询就限于这个集合内的文件。

作为另一个例子,假设你想把公司的所有产品的说明书存储在原生XML数据库中,在这种情形下,你要先定义一个层次结构。比如,为每种产品定义一个集合,并在其中为每种产品说明书的每个章节都指定一个集合。

这些集合是否被允许嵌套取决于所用的数据库。

6.3.3.2 查询语言 (Query Languages)

几乎所有的原生XML数据库都至少支持一种查询语言。最常用的有 XPath (对多个文件的查询作了扩充)和 XQL,以及很多专有的查询语言。在考虑原生XML数据库时应当确定其查询语言是否满足你的需要,比如从全文检索到多个文件片断的合并。

将来大多数原生XML数据库大概都要支持W3C的XQuery。

6.3.3.3 更新和删除 (Updates and Deletes)

原生XML数据库对文件的更新和删除方式有许多,从简单的替换或删除现有文件,到修改当前活动的 DOM 树,以及用于指定如何修改文件片断的语言。通常每种能修改文件片断的产品都有自己的语言,尽管有几种产品支持XML:DBInitiativeXUpdate语言。至于文件的更新正是业界和学术界的探讨领域,近期似乎没有完整的解决方案[2002年2月]。

6.3.3.4 事务、锁定和并发 (Transactions, Locking, and Concurrency)

基本上所有的原生XML数据库都支持事务处理(当然也应支持后退[rollback])。但是,锁定通常是对整个文档的而不是对文件片断的,所以多用户并发性[的支持]相对较低。问题的大小取决于应用程序以及“文件”的构成。

例如用户手册分成几个章节,每个章节都是一个文件,这时并发问题就小一些,因为两个作者同时对同一章节进行更新的情况不大可能发生。而另一方面,如果整个公司的客户数据都放在同一个文件中(糟糕的设计),文件级的锁定很容易造成灾难性的后果。

将来,大多数的原生数据库应该会提供文件片断级的锁定。

6.3.3.5 应用程序接口 (Application Programming Interfaces ,APIs)

几乎所有原生数据库都提供编程接口API。这种API很像ODBC,并提供有连接到数据库、浏览元数据、执行查询和返回结果的方法 (methods)。返回结果通常是XML字符串、DOM树、返回文档的SAX解析器或XMLReader。如果查询返回结果是多个文档的话,通常都会提供例举(iterating)这些结果的方法。

对以数据为中心的应用来说比较有趣的特性是将应用程序变量与返回文档的特定元素或属性相关联的能力。这就免除了应用程序为构件内部数据对象而不得不对文档进行解析的工作,随着XML数据绑定技术的应用越来越多,看起来这个特性会得到广泛支持。

虽然大多数原生XML数据库都提供有自己的API,但是XML:DB.org已经开发出一种与供应商无关的XML数据库API (vendor-neutral XML database API),许多原生XML数据库已经支持它,而且有些非原生的[XML]数据库也可能支持。不管这个或其他的API是否会成为工业标准,此类API的广泛采用最终是不可避免的。

大多数原生数据库还有将查询结果通过 HTTP 返回的能力。

6.3.3.6 “往返车票”(Round-Tripping)

原生XML数据库的一个重要特性是它可以为XML文档提供了“往返车票(round-trip)”。就是说你可以将XML文件存放在原生XML数据库中,而且再取回“同样的”文件。对于以文档为中心的应用程序来说非常重要,因为CDATA部分、实体用法、注释和处理指令是这些文档不可缺少的组成部分。特别是对于法律和医学文件,按规定这些文档必须要保持原样。

(对于以数据为中心的应用来说,由于它主要关心的是元素、属性、文本以及层次顺序,这种“往返车票”显得不是很重要。能够在XML文件和数据库之间交换数据的软件都可以处理这些往返问题,如果同级元素的顺序对以数据为中心的应用程序来说很重要,在有限几种情况下也可以保留这种顺序。但是由于它[指一般的交换软件--译者注]一般不保留兄弟元素的顺序,也不确保原样保持处理指令、注释以及物理结构(实体引用、CDATA等等),所以不适于以文档为中心的应用。)

所有原生XML数据库都能够在元素、属性、CDATA和文件顺序的级别上为文件提供“往返车票”,至于究竟能达到什么程度取决于数据库。一般来说,基于文本的原生XML数据库能够原样存取XML文件,而基于模型的原生XML数据库只能在文件模型的级别上原样存取XML文件。对于特别小的文档模型,意味着比普通的XML原样存取的级别低。

由于你的应用程序要求决定了应当在哪个级别原样存取,所以对原生XML数据库的选择余地可能很大,也可能很小。

6.3.3.7 外部数据 (Remote Data)

某些原生XML数据库可包含有外部数据,这些外部数据来自存储在数据库中的文档。通常这些数据通过ODBC、OLE DB或JDBC从关系数据中取出,模型可以是基于表格的或对象-关系型映射。原生XML数据库决定了这些数据是不是即时的(live)--即原生XML数据库中文档的更新是否在外部数据库中反映出来。大多数原生XML数据库大概最终都会支持即时的外部数据。

6.3.3.8 索引 (Indexes)

几乎所有的原生XML数据库都支持元素和属性的索引。像非XML数据库一样,索引用于提高检索速度。

6.3.3.9 外部实体存储 (External Entity Storage)

XML文档存储时的一个棘手问题就是怎样处理外部实体。也就是说,应当将其展开,把它的值存储在文件中,还是保留实体引用原封不动?这个问题没有统一的答案。

例如,假设文档中包含一个外部实体用来调用一个当前天气报告的CGI程序。如果将这个文件用于提供天气预报的网页,那么将这个实体展开就是错误的,因为网页中提供的不是即时的数据。相反,如果文件是气象历史资料的一部分,那么不展开它反而是不对的,否则文件总是含有当前的数据而不是历史资料了。

再看另外一个例子,假设一个产品手册只包含指向手册中其他章节的外部实体引用。如果这些章节又被其他文件(比如该产品的另一种型号的手册)使用,那么展开这些引用就不对了,因为这会造成同一个章节有多份拷贝。

我不知道原生XML数据库如何处理这个问题。最理想的当然是允许你根据不同情况指定是否展开外部实体引用。

6.3.4 规范化,引用完整性和可伸缩性 (Normalization, Referential Integrity, and Scalability)

对许多人,特别是有关系型数据库背景的人来说,由原生XML数据库引发出不少争论,特别是围绕数据的存储方面(相对于文档而言)。这里我们就来讨论这些话题。

6.3.4.1 规范化 (Normalization)

规范化指的是数据库模型的设计当中要保证每一份数据只能存储一次。规范化有几个好处,例如可以减少磁盘空间占用,消除可能的数据不一致性。这是关系型数据库技术的基础之一,也是人们在讨论原生XML数据库的数据存储时的热点问题。

在进一步讨论规范性之前,需要指出对许多以文档为中心的文件这不是大问题。例如有一些描述公司产品信息的文件,通常各个文件的公用数据很少--比如版权声明、公司地址和电话号码、产品标志等等,其数量相对来说太小了,几乎没有人考虑它的存储规范性。(但是,其他种类的文档可能有许多重叠内容,有必要进行规范化)。

与关系型数据库一样,原生XML数据库并不要求你一定要规范你的数据,用原生XML数据库你也很容易设计出一个糟糕的存储结构。所以在将文件存入原生XML数据库之前,你应仔细考虑文档的结构。(与关系型数据库相比,原生XML数据库在这点有些优势。因为原生XML数据库没有数据库模式,你可以同时以多种模式存储相似的文件,不过为了简化事务处理,你可能需要重新设计查询并转换你的文件(这相对并不重要))

原生XML数据库的规范化和关系型数据库的规范化差不多一样:你的文档的设计要保证不会有重复数据。两者的不同在于XML支持多值属性而(大多数)关系型数据库不支持。这样就有可能以一种在关系型数据库中无法实现的方式来“规范”原生XML数据库的数据。

例如销售订单,它含有头信息比如订单编号、日期和客户代码,还有具体项目如零件号、数量和总价。在关系型数据库中,头信息和具体项目必须存在于不同的表内。而在原生XML数据库中,这些信息可以存储在同一个文件内,不会产生冗余,因为XML与生俱来就支持一个父元素内包含多个子元素。

不幸的是,现实当中的规范化可没这么简单。例如你想要销售订单包含客户信息如合同名称、收货和付款地址,该怎样做? 你有两种选择:第一,你可以在销售订单中复制一份客户信息,结果带来数据冗余和其他问题;第二,你可以单独存储客户信息,在销售订单中提供一个指向客户信息文件的XLink,或者在查询时再将这两个文件连接起来。这就要求对XLink的支持(虽然正在计划,但大多并不支持)或者查询语言要支持联合[joins](同样并不总能如愿)。

在实践当中答案尚不明确。实际当中出于性能的考虑,数据并不总是规范的,所以有一些不规范的XML数据并不像初看起来那么糟糕,不过你必须做出抉择。如果你存储的是以文档为中心的文件并且在相当程度上做到了规范化,比如将章节或过程单独存储并将它们连接起来创建最终用户所用的文件,那么原生XML数据库可能就是一个不错的解决方案,尤其是它能提供别的数据库中所没有的特性比如XML查询语言。如果你要存储的文件是以数据为中心的,而原生XML数据库能够改进应用程序的性能,或者它提供的半结构化数据存储,而在别的数据库中是无法实现的,你也应当用原生XML数据库。如果你只不过想在原生XML数据库内实现一个关系型数据库,你就应该反思一下,为什么不把关系型数据库列为首选。

6.3.4.2 引用完整性 (Referential Integrity)

与规范性密切相关的是引用完整性(referential integrity)。引用完整性即相关数据的[引用]指针的有效性,是保持数据库数据一致性的必要条件。如果你的销售订单含有一个客户代码,而相应的客户信息并不存在,这对你没什么好处。发货部门不知道往哪里发货,而财务部门不知道给哪里寄发票。

在关系型数据库中,引用完整性意味着确保外部键指向合法的主键,也就是说,对每个外部键都要检查相应的主键记录。在原生XML数据库中,引用完整性意味着XLink或其他专有链接机制指向合法的文件或文件片断。

XML 文件中的指针有多种形式:ID/IDREF 属性,key/keyref 域(在 XML Schema 中定义),XLink 以及各种私有机制。后者包括语言相关的“referencing”元素和属性,例如 XML Schema 中 <element>元素的 ref 属性,以及特定数据库的链接机制。而语言相关的“referencing” 元素比较普遍,数据库特定的链接机制较为少见。

原生 XML 数据库的引用完整性可分为两大类:内部指针(同一文件内的指针)的完整性和外部指针(不同文件之间的指针)的完整性。使用非标准机制实现的内部指针的引用完整性一向难以达到,因为原生 XML 数据库无法识别此类指针。以标准机制比如 ID/IDREF 或 key/keyref 实现的内部引用完整性至少可通过验证获得部分支持。

之所以说部分支持,是因为大多数原生 XML 数据库仅在将文件存入数据库时才进行验证。这样,如果更新发生在文件级 - 即删除和替换文件,验证已足以确保内部指针的完整性。但是如果更新发生在节点一级 -即插入、修改和删除节点,则数据库必须执行额外的工作(比如校验所有的变化)来确保内部指针的引用完整性。支持该功能的原生 XML 数据库极少(如果有的话)。

对外部指针的引用完整性(据我所知)仍未有支持,甚至支持外部指针的数据库都很罕见。假如某个外部指针指向了数据库中的其他资源,没有理由不保证其完整性,但如果指向了数据库之外的资源,这种保证的缺乏尚有情可原。例如,某个文件内的一个 XLink 指向了外部网站的某个文件,数据库显然无法知道后者是否存在,因而也就无法保证该 XLink 的完整性。

将来,许多原生XML数据库大致都会以标准的机制支持内部指针的引用完整性。许多原生 XML数据库大约都会在某种程度上支持外部指针,以及支持指向同一个数据库中的资源的外部指针的引用完整性。而在目前,多数情况下还有赖于应用程序保证(内部或外部)指针的完整性。

6.3.4.3 可伸缩性 (Scalability)

可伸缩性完全不是我的所长,所以下面大多都是我的推测。总的来说,我想原生XML数据库的可伸缩性在某些环境下会非常好,而其他场合下可能非常糟糕。

与层次型和关系型数据库类似,原生XML数据库也使用索引来查找数据。这就意味着文件(或文件片断)的查找只与索引的大小有关,而与文件的大小和数量无关,因而原生XML数据库定位文件开始的速度和其他使用同一种索引技术的数据库一样。由此看来,原生XML数据库的可伸缩性和其他数据库一样。

与层次型数据库相同而与关系型数据库不同的是,许多原生XML数据库用物理方法链接相关数据。特别是基于文本的原生XML数据库用物理的方法对相关数据分组,使用专用存储格式的基于模型的原生XML数据库通常使用物理指针来对相关数据分组。(建立在关系数据库之上基于模型的原生XML数据库使用的是逻辑链接。)

由于物理链接比逻辑链接速度快,原生XML数据库和层次型数据库一样,数据的读出速度比关系型数据库快得多。因此,从数据的读取方面来看,它应具有同样的可伸缩性。事实上,在数据的读取能力上XML数据库比关系型数据库甚至更好,因为可伸缩性与初次索引查找有关,而不是关系型数据库所用的多次查找。(需要指出,关系型数据库也以集簇索引(clustered indexes)的形式提供数据的物理链接。不过,这种链接是应用于各个表格而不是整体层次上的。)

令人遗憾的是这种可伸缩性是有限的。就像层次型数据库,原生XML数据库中所用的物理连接只能作用于特定的层次。也就是说,如果数据的读取和数据的存储在同一层次下,则读取速度很快,否则就不一定快。例如将客户信息存储在各个销售订单文件中,则读取销售订单时的速度很快,而如果需要的数据视图不同,比如要找出某个客户的所有订单将会很慢,因为此时物理连接已不再适用。)

为了缓解这个问题,原生XML数据库大量使用了索引,经常对所有元素和属性都作了索引。这虽然有助于减少读取时间,却增加了更新时间,因为维护这种索引的代价很高。在只读的环境下这无关紧要,在交易频繁的环境下可能造成麻烦。

最后,对于未索引数据的查找来说,原生XML数据库的可伸缩性比关系型数据库差的多。此时这两种数据库都要线性地查找数据,而原生XML数据库的情况更为糟糕,因为它的数据不是完全规范的。比如你要查找某个日期的所有销售订单,而日期是未经索引的。如果用的是关系型数据库,就要读取所有OrderDate字段的值,而对于原生XML数据库,这意味着要读取每个销售订单文件,并从中查找<OrderDate>元素。不但需要读取<OrderDate>元素的内容;而且还要读取所有其它文件的内容。对于基于文本的原生XML数据库,情况也很不妙:在与目标日期比较之前,必须先对文本进行解析并转换为日期格式。

那么对你来说,可伸缩性是否严重?这完全取决于你的应用。如果你的应用程序中所需的数据一般都和其存储形式相同,则原生XML数据库的可伸缩性应是不错的。许多以文本为中心的应用就属于这种情形。例如组成产品手册的文档几乎总是作为整体读取的。反之,如果你的应用中数据视图不是很确定,则可伸缩性有可能出问题。

对于原生XML数据库的讨论就到此结束。在接下来的两部分中,我们将考察两种特殊的原生XML数据库:可持久化DOM和内容管理系统。

6.3 可持久化DOM (Persistent DOMs, PDOMs)

可持久化DOM(persistent DOM)或PDOM是在某种持久性存储[介质]上实现了DOM 的一种特殊的原生XML数据库。与大多数以DOM树返回文档的原生XML数据库不同,PDOM返回的DOM是实时的(live)。也就是说,对DOM所作的改变直接反映在数据库中。(这种改变实际上是否马上做出或者通过调用一个方法来实现取决于数据库)大多数原生XML数据库返回个应用程序的DOM树是一个复制品,而数据库中的改变是通过XML更新语言,或者是通过替换整个文件来实现的。

由于PDOM树是实时的,数据库通常是在本地。这就是说,它和应用程序在同一个进程空间,或者至少在同一部机器(尽管这并不是必需的)。这是出于性能上的考虑,因为外部数据库上的PDOM必须经常向远程服务器发出请求。

PDOM在DOM应用程序中所起的作用和在面向对象的应用程序中面向对象的数据库的作用一样:它为应用程序的数据提供了可持久化的存储,也可作为应用程序的虚拟内存。后一种作用对于操作大型文件的DOM应用来说有着特殊的重要性,因为DOM与XML文件长度之比很容易超过10。(实际的系数取决于文件中文本的平均长度,文本的平均长度较小的文件其系数较高。)

6.4 内容管理系统 (Content Management Systems)

内容管理系统是原生XML数据库的另一种特殊形式。它们是为管理手工写成的文件例如用户手册和技术草稿(white paper)而专门设计的,并且建立在原生XML数据库之上。这个数据库一般是处于用户看不到的后台,而提供给用户的功能有:

  • 版本和访问控制。
  • 搜索引擎。
  • XML/SGML编辑器。
  • 发布引擎。比如以书面、CD形式或在Web上发布。
  • 内容与样式的分离。
  • 可通过脚本或编程扩展。
  • 数据库数据的集成(integration)。

相对于文件管理系统,内容管理系统这个术语道出了这样的事实:它使你可以将文件分为离散的片断(比如示例、过程、章节或旁注)和元数据(例如作者、修订日期、文件编号),而不是以整体管理文件。这不但简化了一个文件多个作者时的协调工作,而且能使你从现有文件部分组合成全新的文件。

7.0 XML 数据库产品 (Database Products)

关于最新的XML数据库产品,参见XML Database Products.

8.0 相关链接 (Additional Links)

有关XML/数据库的相关资源,包括软件和文章,参见XML / Database Links

9.0 意见和反馈 (Comments and Feedback)

请将意见和反馈发送给Ronald Bourret rpbourret@rpbourret.com。我经常外出旅行,有可能延误两到三个星期。

感谢Michael Champion, John Cowan, Marc Cyrenne, Marc de Graauw, Kelvin Ginn, Ingo Macherius, Lars Martin, Nick Leaton, Evan Lenz, Michael Rys, Walter Perry, Kimbro Staken, Jim Tivy, Phillipe Vauclair, Dylan Walsh, Irsan Widarto, Morten Primdahl 及其他人的意见和耐心。


您对本文有什么看法或意见,请 发表留言。| 返回 onestab.net 首页

posted @ 2006-01-31 00:34 Flysir 阅读(290) | 评论 (0)编辑

2006年1月30日 #

PING命令的高级用法

    对于Windows下ping命令相信大家已经再熟悉不过了,但是能把ping的功能发挥到最大的人却并不是很多,当然我也并不是说我可以让ping发挥最大的功能,我也只不过经常用ping这个工具,也总结了一些小经验,现在和大家分享一下。

    现在我就参照ping命令的帮助说明来给大家说说我使用ping时会用到的技巧,ping只有在安装了TCP/IP协议以后才可以使用:
ping [-t] [-a] [-n count] [-l length] [-f] [-i ttl] [-v tos] [-r count] [-s count] [-j computer-list] │ [-k computer-list] [-w timeout] destination-list

Options:

-t Ping the specified host until stopped.To see statistics and continue - type Control-Break;To stop - type Control-C.

不停的ping地方主机,直到你按下Control-C。

此功能没有什么特别的技巧,不过可以配合其他参数使用,将在下面提到。

-a Resolve addresses to hostnames.

解析计算机NetBios名。

示例:

C:\>ping -a 192.168.1.21

Pinging iceblood.yofor.com [192.168.1.21] with 32 bytes of data:
Reply from 192.168.1.21: bytes=32 time<10ms TTL=254
Reply from 192.168.1.21: bytes=32 time<10ms TTL=254
Reply from 192.168.1.21: bytes=32 time<10ms TTL=254
Reply from 192.168.1.21: bytes=32 time<10ms TTL=254
Ping statistics for 192.168.1.21:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),Approximate round trip times in milli-seconds:
Minimum = 0ms, Maximum = 0ms, Average = 0ms

从上面就可以知道IP为192.168.1.21的计算机NetBios名为iceblood.yofor.com。

-n count Number of echo requests to send.
发送count指定的Echo数据包数。

  在默认情况下,一般都只发送四个数据包,通过这个命令可以自己定义发送的个数,对衡量网络速度很有帮助,比如我想测试发送50个数据包的返回的平均时间为多少,最快时间为多少,最慢时间为多少就可以通过以下获知:

C:\>ping -n 50 202.103.96.68
Pinging 202.103.96.68 with 32 bytes of data:
Reply from 202.103.96.68: bytes=32 time=50ms TTL=241
Reply from 202.103.96.68: bytes=32 time=50ms TTL=241
Reply from 202.103.96.68: bytes=32 time=50ms TTL=241
Request timed out.
………………
Reply from 202.103.96.68: bytes=32 time=50ms TTL=241
Reply from 202.103.96.68: bytes=32 time=50ms TTL=241
Ping statistics for 202.103.96.68:
Packets: Sent = 50, Received = 48, Lost = 2 (4% loss),Approximate round trip times in milli-seconds:
Minimum = 40ms, Maximum = 51ms, Average = 46ms

  从以上我就可以知道在给202.103.96.68发送50个数据包的过程当中,返回了48个,其中有两个由于未知原因丢失,这48个数据包当中返回速度最快为40ms,最慢为51ms,平均速度为46ms。

-l size Send buffer size.
定义echo数据包大小。

  在默认的情况下windows的ping发送的数据包大小为32byt,我们也可以自己定义它的大小,但有一个大小的限制,就是最大只能发送65500byt,也许有人会问为什么要限制到65500byt,因为Windows系列的系统都有一个安全漏洞(也许还包括其他系统)就是当向对方一次发送的数据包大于或等于65532时,对方就很有可能当机,所以微软公司为了解决这一安全漏洞于是限制了ping的数据包大小。虽然微软公司已经做了此限制,但这个参数配合其他参数以后危害依然非常强大,比如我们就可以通过配合-t参数来实现一个带有攻击性的命令:(以下介绍带有危险性,仅用于试验,请勿轻易施于别人机器上,否则后果自负)

C:\>ping -l 65500 -t 192.168.1.21
Pinging 192.168.1.21 with 65500 bytes of data:
Reply from 192.168.1.21: bytes=65500 time<10ms TTL=254
Reply from 192.168.1.21: bytes=65500 time<10ms TTL=254
………………

  这样它就会不停的向192.168.1.21计算机发送大小为65500byt的数据包,如果你只有一台计算机也许没有什么效果,但如果有很多计算机那么就可以使对方完全瘫痪,我曾经就做过这样的试验,当我同时使用10台以上计算机ping一台Win2000Pro系统的计算机时,不到5分钟对方的网络就已经完全瘫痪,网络严重堵塞,HTTP和FTP服务完全停止,由此可见威力非同小可。

-f Set Don't Fragment flag in packet.
在数据包中发送“不要分段”标志。

在一般你所发送的数据包都会通过路由分段再发送给对方,加上此参数以后路由就不会再分段处理。

-i TTL Time To Live.
指定TTL值在对方的系统里停留的时间。
此参数同样是帮助你检查网络运转情况的。

-v TOS Type Of Service.
将“服务类型”字段设置为 tos 指定的值。

-r count Record route for count hops.
在“记录路由”字段中记录传出和返回数据包的路由。

  在一般情况下你发送的数据包是通过一个个路由才到达对方的,但到底是经过了哪些路由呢?通过此参数就可以设定你想探测经过的路由的个数,不过限制在了9个,也就是说你只能跟踪到9个路由,如果想探测更多,可以通过其他命令实现,我将在以后的文章中给大家讲解。以下为示例:

C:\>ping -n 1 -r 9 202.96.105.101 (发送一个数据包,最多记录9个路由)

Pinging 202.96.105.101 with 32 bytes of data:

Reply from 202.96.105.101: bytes=32 time=10ms TTL=249
Route: 202.107.208.187 ->
202.107.210.214 ->
61.153.112.70 ->
61.153.112.89 ->
202.96.105.149 ->
202.96.105.97 ->
202.96.105.101 ->
202.96.105.150 ->
61.153.112.90

Ping statistics for 202.96.105.101:
Packets: Sent = 1, Received = 1, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 10ms, Maximum = 10ms, Average = 10ms

  从上面我就可以知道从我的计算机到202.96.105.101一共通过了202.107.208.187 ,202.107.210.214 , 61.153.112.70 , 61.153.112.89 , 202.96.105.149 , 202.96.105.97这几个路由。

-s count Timestamp for count hops.
指定 count 指定的跃点数的时间戳。

此参数和-r差不多,只是这个参数不记录数据包返回所经过的路由,最多也只记录4个。

-j host-list Loose source route along host-list.
利用 computer-list 指定的计算机列表路由数据包。连续计算机可以被中间网关分隔(路由稀疏源)IP 允许的最大数量为 9。

-k host-list Strict source route along host-list.
利用 computer-list 指定的计算机列表路由数据包。连续计算机不能被中间网关分隔(路由严格源)IP 允许的最大数量为 9。

-w timeout Timeout in milliseconds to wait for each reply.
指定超时间隔,单位为毫秒。

此参数没有什么其他技巧。

  ping命令的其他技巧:在一般情况下还可以通过ping对方让对方返回给你的TTL值大小,粗略的判断目标主机的系统类型是Windows系列还是UNIX/Linux系列,一般情况下Windows系列的系统返回的TTL值在100-130之间,而UNIX/Linux系列的系统返回的TTL值在240-255之间,当然TTL的值在对方的主机里是可以修改的,Windows系列的系统可以通过修改注册表以下键值实现:

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters]
"DefaultTTL"=dword:000000ff
255---FF
128---80
64----40
32----20

好了,ping命令的高级参数用法基本上被我COPY过来了,有什么疑问欢迎提出,大家一起探讨。

posted @ 2006-01-30 23:24 Flysir 阅读(35) | 评论 (0)编辑

执行数据库命令Command对象——ADO.NET学习&应用笔记之三

数据提供程序的Command类是IDBCommand接口的实现,通过Command来执行数据库命令,数据库数据的查询、更新、插入都通过Command来实现。
Command的构造函数通常都有下面三种形式:
1, public xxxCommand();
2, public xxxCommand(string);
3, public xxxCommand(string,xxxConnection);
一般我们创建Command对象都通过类似于下面的语句来实现:
xxxConnection conn = new xxxConnection("myString");
xxxCommand myCmd = new xxxCommand("select * from orders",conn);
构造函数的两个参数是SQL语句和Connection对象,创建了Command对象。

下面说明Command对象的执行,程序范例将采用SqlClient数据提供程序来访问一个Sql Server的数据库Northwind。

一、设置连接和SQL命令
Command类的属性CommandText用来设置命令语句,Connection属性用来设置连接对象。设置命令对象的数据连接和命令语句除了可以在通过创建对象时通过构造函数定义外,这两个属性设置和更改。
SqlConnection conn = new SqlConnection("Server=localhost;Database=Northwind;User ID=sa;PWD=sa");
conn.Open();
SqlCommand cmd = new SqlCommand("select * from [Orders]",conn);
cmd.CommandText = "delete [Orders] where [OrderID]=10248";

二、执行命令
建立了数据源的连接和设置了命令之后,Command对象执行SQL命令有三种方法:ExecuteNonQuery、ExecuteReader和ExecuteScalar。
使用ExecuteNonQuery执行命令不会返回结果集,只会返回语句影响的记录行数,它适合执行插入、更新、删除之类不返回结果集的命令。如果是SELECT语句,那么返回的结果是-1,如果发生回滚这个结果也是-1。下面的程序范例对Orders表执行了更新并做了查询。

using System;
using System.Data;
using System.Data.SqlClient;
public class myDataAccess{
 public static void Main(){
  SqlConnection conn = new SqlConnection("Server=localhost;Database=Northwind;User ID=sa;PWD=sa");
  SqlCommand cmd = new SqlCommand("update [Orders] set [OrderDate]='2004-9-1' where [OrderID]=10248",conn);
  try{
   conn.Open();
   int i = cmd.ExecuteNonQuery();
   Console.WriteLine(i.ToString() + " rows affected by UPDATE");
   cmd.CommandText = "select * from [Orders]";
   i = cmd.ExecuteNonQuery();
   Console.WriteLine(i.ToString() + " rows affected by SELECT");
  }
  catch(Exception ex){
   Console.WriteLine(ex.Message);
  }
  finally{
   conn.Close();
  }
 }
}



使用ExecuteReader方法执行的命令,可以返回一个类型化的DataReader实例或者IDataReader接口的结果集。通过DataReader对象就能够获得数据的行集合,本文不准备详细讨论DataReader,关于DataReader的使用将来再说明。下面是一个例子。
using System;
using System.Data;
using System.Data.SqlClient;
public class myDataAccess{
 public static void Main(){
  SqlConnection conn = new SqlConnection("Server=localhost;Database=Northwind;User ID=sa;PWD=sa");
  SqlCommand cmd = new SqlCommand("select top 20 * from [Orders]",conn);
  SqlDataReader reader; //或者IDataReader reader;
  try{
   conn.Open();
   reader = cmd.ExecuteReader();
   while(reader.Read()){
    Console.WriteLine(reader[0].ToString());
   }
   reader.Close();
  }
  catch(Exception ex){
   Console.WriteLine(ex.Message);
  }
  finally{
   conn.Close();
  }
 }
}



对于ExecuteReader方法,如果想获得数据的记录行数,可以通过select count(*)这样的语句取得一个聚合的行集合。对于这样求单个值的语句,Command对象还有更有效率的方法——ExecuteScalar。它能够返回对应于第一行第一列的对象(System.Object),通常使用它来求聚合查询结果。需要注意的是,如果需要把返回结果转化成精确的类型,数据库在查询中就必须强制将返回的结果转换,否则引发异常。下面是例子:

using System;
using System.Data;
using System.Data.SqlClient;
public class myDataAccess{
 public static void Main(){
  SqlConnection conn = new SqlConnection("Server=localhost;Database=Northwind;User ID=sa;PWD=sa");
  SqlCommand cmd = new SqlCommand("select count(*) from [Orders]",conn);
  try{
   conn.Open();
   int i = (int)cmd.ExecuteScalar();
   Console.WriteLine("record num : " + i.ToString());
   cmd.CommandText = "select cast(avg([Freight]) as int) from [Orders]";
   int avg = (int)cmd.ExecuteScalar();
   Console.WriteLine("avg : " + avg.ToString());
   cmd.CommandText = "select avg([Freight]) from [Orders]";
   avg = (int)cmd.ExecuteScalar(); //引发异常
   Console.WriteLine("avg : " + avg.ToString());
  }
  catch(Exception ex){
   Console.WriteLine(ex.Message);
  }
  finally{
   conn.Close();
  }
 }
}


这个程序中,最后一个查询将引发异常,因为聚合返回的结果是float类型的,无法转换。

三、参数化查询
参数化的查询能够对性能有一定的优化,因为带参数的SQL语句只需要被SQL执行引擎分析过一次。Command的Parameters能够为参数化查询设置参数值。Parameters是一个实现IDataParamterCollection接口的参数集合。
不同的数据提供程序的Command对参数传递的使用不太一样,其中SqlClient和OracleClient只支持SQL语句中命名参数而不支持问号占位符,必须使用命名参数,而OleDb和Odbc数据提供程序只支持问号占位符,不支持命名参数。
对于查询语句SqlClient必须使用命名参数,类似于下面的写法:
SELECT * FROM Customers WHERE CustomerID = @CustomerID --Oracle的命名参数前面不用@,使用(:),写为(:CustomerID)
而对于OleDb或者Odbc必须使用?占位符,类似于下面的写法:
SELECT * FROM Customers WHERE CustomerID = ?

下面以Sql Server为范例,说明其使用方法:
using System;
using System.Data;
using System.Data.SqlClient;
public class myDataAccess{
 public static void Main(String[] args){
  SqlConnection conn = new SqlConnection("Server=localhost;Database=Northwind;User ID=sa;PWD=sa");
  SqlCommand cmd = new SqlCommand("select * from [Orders] where [OrderID]=@oid",conn);
  SqlDataReader reader;
  try{
   int param = Convert.ToInt32(args[0]);
   cmd.Parameters.Add("@oid",param);  //使用命名参数
   cmd.Parameters[0].Direction = ParameterDirection.Input;
   conn.Open();
   reader = cmd.ExecuteReader();
   while(reader.Read()){
    Console.WriteLine(reader[0].ToString());
   }
   reader.Close();
  }
  catch(Exception ex){
   Console.WriteLine(ex.Message);
  }
  finally{
   conn.Close();
  }
 }
}



对于OleDb或者Odbc数据提供程序的命令参数,只需要把参数按照占位符从左到右的顺序,匹配给Parameters集合就行了。 下面是程序范例:

using System;
using System.Data;
using System.Data.OleDb;
public class myDataAccess{
 public static void Main(String[] args){
  OleDbConnection conn = new OleDbConnection("Provider=SQLOLEDB;Server=localhost;Database=Northwind;User ID=sa;PWD=sa");
  OleDbCommand cmd = new OleDbCommand("select * from [Orders] where [OrderID]=? or [EmployeeID]=?",conn);
  OleDbDataReader reader;
  try{
   int param1 = Convert.ToInt32(args[0]);
   int param2 = Convert.ToInt32(args[1]);
   cmd.Parameters.Add("aaa",param1);
   cmd.Parameters.Add("bbb",param2); //参数对象还需要名字,但是和查询语句中的参数名无关
   cmd.Parameters[0].Direction = ParameterDirection.Input;
   cmd.Parameters[1].Direction = ParameterDirection.Input;
   conn.Open();
   reader = cmd.ExecuteReader();
   while(reader.Read()){
    Console.WriteLine(reader[0].ToString());
   }
   reader.Close();
  }
  catch(Exception ex){
   Console.WriteLine(ex.Message);
  }
  finally{
   conn.Close();
  }
 }
}



四、执行存储过程
使用Command对象访问数据库的存储过程,需要指定CommandType属性,这是一个CommandType枚举类型,默认情况下CommandType表示CommandText命令为SQL批处理,CommandType.StoredProcedure值指定执行的命令是存储过程。类似于参数化查询,存储过程的参数也可以使用Parameters集合来设置,其中Parameter对象的Direction属性用于指示参数是只可输入、只可输出、双向还是存储过程返回值参数。
需要注意的是如果使用ExecuteReader返回存储过程的结果集,那么除非DataReader关闭,否则无法使用输出参数。下面是一个例子:
存储过程
---------------------------------------------
CREATE procedure myProTest (
 @orderID as int,
 @elyTitle as varchar(50) output
)
as
select @elyTitle=ely.Title from [Orders] o join [Employees] ely on ely.EmployeeID=o.EmployeeID where o.OrderID=@orderID
select * from [Orders] where OrderID=@orderID
return 1
---------------------------------------------

程序
---------------------------------------------
using System;
using System.Data;
using System.Data.SqlClient;
public class myDataAccess{
 public static void Main(){
  SqlConnection conn = new SqlConnection("Server=localhost;Database=Northwind;User ID=sa;PWD=sa");
  SqlCommand cmd = new SqlCommand("myProTest",conn);
  cmd.CommandType = CommandType.StoredProcedure;
  cmd.Parameters.Add("@orderID",10252);
  cmd.Parameters.Add("@elyTitle",SqlDbType.VarChar,50);
  cmd.Parameters.Add("@return",SqlDbType.Int);
  cmd.Parameters[0].Direction = ParameterDirection.Input;
  cmd.Parameters[1].Direction = ParameterDirection.Output;
  cmd.Parameters[2].Direction = ParameterDirection.ReturnValue;
  SqlDataReader reader;
  try{
   conn.Open();
   Console.WriteLine("execute reader...");
   reader = cmd.ExecuteReader();
   Console.WriteLine("@orderID = {0}",cmd.Parameters[0].Value);
   Console.WriteLine("@elyTitle = {0}",cmd.Parameters[1].Value);
   Console.WriteLine("Return = {0}",cmd.Parameters[2].Value);
   Console.WriteLine("reader close...");
   reader.Close();
   Console.WriteLine("@orderID = {0}",cmd.Parameters[0].Value);
   Console.WriteLine("@elyTitle = {0}",cmd.Parameters[1].Value);
   Console.WriteLine("Return = {0}",cmd.Parameters[2].Value);
   Console.WriteLine("execute none query...");
   cmd.ExecuteNonQuery();
   Console.WriteLine("@orderID = {0}",cmd.Parameters[0].Value);
   Console.WriteLine("@elyTitle = {0}",cmd.Parameters[1].Value);
   Console.WriteLine("Return = {0}",cmd.Parameters[2].Value);
  }
  catch(Exception ex){
   Console.WriteLine(ex.Message);
  }
  finally{
   conn.Close();
  }
 }
}


和参数化查询一样,OleDb或者Odbc数据提供程序不支持存储过程的命名参数,需要把参数按照从左到右的顺序,匹配Parameters集合。

posted @ 2006-01-30 22:55 Flysir 阅读(147) | 评论 (0)编辑

ADO.NET的数据提供程序和数据连接——ADO.NET学习&应用笔记之二

类似于ADODB连接模式的数据库操作程序的,是ADO.NET的数据提供程序。.NET Framework 1.1版的ADO.NET提供了四种托管数据提供程序。

 

一、ADO.NET的数据提供程序
ADO.NET配套的四种数据提供程序,分别是用于SQL Server7.0及更高版本的SqlClient数据提供程序,对应的名字空间是System.Data.SqlClient;用于OLE DB数据源的OleDb数据提供程序,对应的名字空间是System.Data.OleDb;用于ODBC数据源的Odbc数据提供程序,对应的名字空间是System.Data.Odbc;用于Oracle数据源的OracleClient数据提供程序,对应的名字空间是System.Data.OracleClient。(注意:.NET Framework1.0版本不包括Odbc和OracleClient数据提供程序,需要从微软官方站点下载安装)

ADO.NET的SQL Server数据提供程序比OLE DB数据提供程序有着更优异的性能。前者专用于SQL Server,并有针对的优化,通过TDS数据包与SQL Server直接对话,后者必须通过一个COM组件集,即数据源的OLE DB提供程序和OLE DB服务组件来与数据源进行交流。因此对于SQL Server的访问,推荐使用SQL Server数据提供程序。下图为两者的数据访问比较图:



二、使用Connection对象连接数据库
需要访问数据源的数据,首先要通过Connection对象,连接到指定的数据源,FCL数据提供程序的Connection类是一个通用接口System.Data.IDBConnection的实现。
Connection的构造函数通常都有一个连接字符串作为参数,连接字符串也可以使用Connction.ConnectionString属性设置,如果使用c#表示,它们创建连接语句形式通常如下:

IDBConnection conn = new xxxConnection("connection string");
conn.Open();

Connection的Close方法能够关闭数据连接,但是有些时候Close不能够物理性质的中断到数据库的连接。除了Close方法,还有一个Dispose方法,它会调用Close方法,此外通过设置一个Boolean参数指定非托管资源是否应该释放(包括COM接口指针、ODBC句柄等),该方法调用之后,Connection对象就完全释放而无法重用了。

(一)连接字符串的差异
对于不同的数据提供程序,连接字符串存在着差异。
SqlConnection的数据库连接字符串通常如下:
"Server=mySQLServer;Database=northwind;User ID=sa;Password=mypwd;"
OleDbConnection的数据库连接字符串如下:
"Provider=SQLOLEDB;Data Source=mySqlServer;Initial Catalog=northwind;User ID=sa;Password=mypwd;"
OdbcConnection的数据库连接字符串如下:
"Driver={SQL Server};Server=localhost;Database=northwind"或者"DSN=dsnname"
OracleConnection的数据库连接字符串如下:
"Data Source=Oracle8i;User ID=sa;pwd=mypwd"

(二)应用程序中连接字符串的存储
为了方便管理数据连接,通常数据连接字符串不写为硬码,而存储在应用程序之外。
《.NET 数据访问架构指南》一文用一节讨论了链接字符串的存储。链接字符串的存储可以采用下面5种方式:
1、应用程序配置文件 例如用于ASP.NET Web应用程序的Web.config文件。
2、通用数据链接文件(UDL) (只被OLE DB .NET 数据供应器所支持)
3、Windows 注册表
4、定制文件
5、COM+ 目录,通过过使用构造字符串(只用于服务组件)
这些方式各有优缺点,在ASP.NET中最常见的是使用Web.config存储连接字符串,包含在元素appSettings的一个add节点当中。
 <appSettings>
  <add key="DBConnStr"
     value="server=(local);Integrated Security=SSPI;database=northwind"/>
 </appSettings>
在ASP.NET应用程序当中使用System.Configuration.ConfigurationSettings类的AppSettings静态属性,可以获取应用程序的定制设置。

(三)连接池(Connection pooling)
连接池能让数据库中使用同一个帐号的不同会话共享连接,避免频繁的打开和关闭连接,它能够显著的提高应用程序的性能。
使用SQL Server数据提供程序的连接池需要注意,每当应用打开一个连接,.NET Framework将创建池,每个连接池都和一个不同的连接字符串相关联。也就是说,如果新创建的一个连接使用的连接字符串和现有的池中连接相匹配,将创建新的池。注意:池化机制对名称-值对间的空格敏感。
下面的三个连接创建了三个不同的池:
SqlConnection conn = new SqlConnection("Integrated Security=SSPI;Initial Catalog=pubs");
conn.Open(); //创建池A

SqlConnection conn = new SqlConnection("Integrated Security=SSPI;Initial Catalog=Northwind");
conn.Open(); //创建池B,和前面的连接字符串不一样

SqlConnection conn = new SqlConnection("Integrated Security=SSPI ;Initial Catalog=Northwind");
conn.Open(); //创建池C,SSPI后多了一个空格

posted @ 2006-01-30 22:36 Flysir 阅读(125) | 评论 (0)编辑

ADO.NET的结构——ADO.NET学习&应用笔记之一

ADO.NET的核心组件由Data Provider模块和DataSet模块组成。Data Provider程序库实现数据的连接、操作和对数据快速只进只读访问。DataSet实现独立于数据源的数据访问、操作,有点类似于ADO的断开连接的静态数据集。

ADO.NET的结构图如下:



一、.NET Framework Data Provider
.NET Framework数据提供程序是一组连接数据源,并且能够对数据执行命令,获取数据的程序结构。

(一).NET Framework的四个数据提供程序
.NET Framework提供了四组数据提供程序,用于访问四类数据源。
1、SQL Server .NET Framework数据提供程序,该程序只能访问MS SQL Server7.0或更高版本,更早版本的只能通过OLE DB数据提供程序访问。它的命名空间为System.Data.SqlClient。
2、OLE DB .NET Framework 数据提供程序,用于访问OLE DB数据提供程序,该程序不支持OLE DB 2.5版接口。它的命名空间为System.Data.OleDb。
3、ODBC .NET Framework 数据提供程序。用于访问ODBC数据提供程序。它的命名空间为System.Data.Odbc。
4、Oracle .NET Framework 数据提供程序。用于访问Oracle数据,该程序需要Oracle客户端软件8.1.7或更高版本的支持。它的命名空间为System.Data.OracleClient。

通过对IDbConnection接口、IDbCommand接口、IDataAdapter接口、IDbDataAdapter接口、IDataReader接口、IDataParameter接口、IDbTransaction接口等的实现我们也可以编写自己的数据提供程序组。


(二).Net数据提供程序的四个核心对象
.Net数据提供程序通常包含有四个核心对象:
1、Connection 对象提供与数据源的连接。数据提供程序的Connection类是继承System.Data.IDbConnection接口的实现。
2、Command对象使您能够访问用于返回数据、修改数据、运行存储过程以及发送或检索参数信息的数据库命令。数据提供程序的Command类是继承System.Data.IDbCommand接口的实现。
3、DataReader 从数据源中提供高性能的数据流。DataReader的数据流是只进且只读的。数据提供程序的DataReader类是继承System.Data.IDataReader接口的实现。
4、DataAdapter 提供连接DataSet对象和数据源的桥梁。DataAdapter 使用Command 对象在数据源中执行 SQL 命令,以便将数据加载到 DataSet 中,并使对 DataSet中数据的更改与数据源保持一致。数据提供程序的DataAdapter类是继承System.Data.IDbDataAdapter接口的实现。
.Net数据提供程序还包括Transaction对象、Parameter对象等。


二、.NET Framework DataSet
连接、命令、事务、数据读取作用于特定的提供程序,唯有数据集可以独立于特定的数据提供的,在.NET Framework中数据集对象DataSet的命名空间位于System.Data中。

(一)、DataSet对象模型
DataSet对象是支持ADO.NET的断开式、分布式数据方案的核心对象。DataSet是数据的内存驻留表示形式,无论数据源是什么,它都会提供一致的关系编程模型。你可以把它想象成一个浓缩在内存当中的关系数据库。它的对象模型如下图:



DataSet包含三组集合:
(1)、DataTableCollection。关系数据库当中最主要的对象就是Table,那么在DataSet当中使用该集合来包含多个DataTable对象。DataTable就是是在内存中的数据表,你可以通过唯一名称来标识一个表。DataSet的DataTable可存储的最大行数是16,777,216。
(2)、DataRelationCollection。在关系数据库当中除了表之外,还有表示表和表之间的关系Relation。该集合就是包含表关系对象的集合。
(3)、ExtendedProperties。该是一组自定义信息,概念上点类似于ASP的Session之类的。


(二)、DataSet与DataAdapter
DataAdapter对象是数据提供程序和DataSet连接的桥梁。数据提供程序的DataAdapter类是IDataAdapter或者IDbDataAdapter的实现。通常DataAdapter的构造函数是:
XxxDataAdapter(SqlCommand selectCommand)
XxxDataAdapter(String selectCommandText, String selectConnectionString)
XxxDataAdapter(String selectCommandText, SqlConnection selectConnection)

DataAdapter通过实现Fill、FillSchema将数据源数据、架构等添加到DataSet中,通过实现Update将DataSet中的数据更新到数据源中。

posted @ 2006-01-30 22:26 Flysir 阅读(135) | 评论 (0)编辑

图解软件工程

很形象的一幅图,我想每个从事软件开发的同行们看了一定深有感触吧

posted @ 2006-01-30 15:18 Flysir 阅读(40) | 评论 (0)编辑

中国最好的一些源码下载站点排名

1.中国站长网下载 http://www.chinaz.com/download/
为什么没有选择K6 理由很简单 做为比K6年轻的源码下载站点 靠自己的特色 越做越大并且提供建站相关的一切东西更新速度也很快! 估计很快就可以超过K6了
 

2.KK66下载中心 http://down.kk66.com
瘦死的骆驼比马大,是全国最多的建站源码基地,但垃圾很多,现在K6的极端商业化已经使他逐渐的失去他自身的魅力


3.ASP300 http://www.asp300.com
选择他,其实主要是他在国内做的很早,又是比较早进行收费的代码下载站点,姑且不论他收费的对错,但网络告别免费是迟早的,这一点,他做的很先进,他提供的代码也是一些商业全站很不错的东西,虽然有人说他拿其他站点免费的当自己的,但是就现在各个下载站提供他们的代码都要加上一句"ASP300会员代码"就可以看出他们做的还不错


4,信心网络工作室 http://www.xxsky.com/
做的也挺早,库存代码也多,现在不过好象有些迷失自己了,逐渐的被网友所遗忘


5谁与争锋的下载栏目 http://www.china-code.net/
东西比较多 商业的也多 现在免费了 不错 支持


6,ASP酷 http://www.aspcool.com
还是老代码下载基地,没有什么说的 排名主要靠他的资格在那里 ,代码更新慢,程序的相关资料还比较多


7,源码之家 http://www.mycodes.net
也是比较早的源码下载站点,好象今年是改版过的,提供的代码现在也很不错了,里面还有很多商业的东西哟,现在库存少! 更新速度还可以

8 ,华迅鲲翔 http://www.ftpshow.com
后起之秀,靠做论坛起家做然后做下载的,商业的东西很多,但是介绍少,现在库存很少,但是快赶上CNCODE了,代码全部是本地提供下载速度很快! 值得期待

9,中国源码中心 http://www.cncode.com
老牌的,名字挺大的,沉寂了很长一段时间后重新改版,提供的东西不怎么样,全是其他站有的,说明比较齐全,更新比较快


10,ASPDOWN http://www.aspdown.com
靠提供商业的东西火了一段时间,然后开始不成熟的收费,使网站慢慢的往下走.现在好象又提出了什么免费的ASPDOWN又回来了,其实又是一种变相的收费,咳 ,ASPDOWN前途为卜

posted @ 2006-01-30 14:57 Flysir 阅读(86) | 评论 (0)编辑

关于select top * 的注意问题

假如Table1有10条记录,n=3

select top n * from Table1
选择记录集的前n条记录,取出的记录为1,2,3

select top n * from Table1 order by id desc
如果用到order by则是先排序,然后再取前n条,取出的记录为10,9,8

posted @ 2008-07-13 10:04  网络金领  阅读(613)  评论(0编辑  收藏  举报