首页 > 数据库 > SQL Server > 正文

SQL Server 2005的XML支持与异常处理

2024-08-31 00:50:09
字体:
来源:转载
供稿:网友

  本文给出了类型化和非类型化xml列各自的优势、将sql server 2000数据库迁移到sql server2005,新的异常处理如何工作;如果表的架构发生更改,我如何执行操作等,供大家参考!

  sql server 2005 包括对transact-sql (t-sql) 语言的几项重要改进。新增功能之一是一种新的触发器,它在数据定义语言 (ddl) 语句运行时激发。在跟踪或保护数据库对象的创建和修改,或对数据库服务器进行更改时,ddl 触发器非常有用。另一个新功能涉及异常处理,该功能因包含try/catch 块而在异常处理方面向前飞跃了一大步。另一组新功能则以新的xml数据类型为中心,该数据类型在与sql server交互的企业应用程序中管理xml数据方面前进了一大部。现在,xml 文档或片段可以存储于 xml 列、绑定到架构、还可以利用 xquery 语法查询。本文汇总各类问题来做出详细解答。

  问:类型化和非类型化 xml 列各自的优势是什么?

  答:非类型化 xml 列可以存储所有格式规范的 xml 片段或文档,而类型化 xml 列可以绑定到 xml 架构。如果不确定 xml 将遵循哪个架构,则非类型化 xml 很有用。例如,如果您必须使用另一个应用程序的某些 xml,但无法确定数据将遵循哪个 xml 架构,则可以使用非类型化 xml 列来执行这项工作。当然,非类型化 xml 也会带来其他问题。例如,不能针对某个架构编程,因此很难有效地使用 xml。但有时无法避免非类型化 xml。创建非类型化 xml 列非常简单,如下所示:

create table foo(fooid int, somexml xml)

  架构不绑定到 xml 列的另一个结果是,sql server® 2005 会将非类型化 xml 作为一个字符串存储。这是件好事还是坏事?实际上,既是好事也是坏事。将 xml 作为字符串存储使您可以更灵活地存储任何 xml 片段或文档。根据应用程序的业务规则,您可能需要这种方式。另一方面,将 xml 作为字符串存储意味着,与类型化 xml 相比,既不能有效地存储也不能有效地搜索 xml 数据。并且既不会告诉 sql server xml 将包含哪些内容,也不会告诉它层次结构和 xml 节点的数据类型。但请记住,仅仅因为这些是非类型化 xml 列并不意味着它们可以接受您设计出的任何格式。类型化和非类型化 xml 列仍然只接受格式规范的 xml。

|||

  如果您要存储架构已知的 xml 文档,类型化 xml 列很理想。架构可以定义元素、属性、它们的数据类型、需要哪些字段以及数据的整个层次结构。由于这种详细信息有关 xml 列的数据,因此 sql server 2005 可以在内部更加有效地存储 xml 数据。如果您尝试存储与架构不匹配的 xml 数据,则 sql server 会检测到这一点并阻止您。

  创建类型化 xml 列就像在括号中添加架构名称一样简单,如下所示:

create table foo(fooid int, somexml xml(content fooschema))

  该语句指出 somexml 列必须遵循名为 fooschema 的 xml 架构集合。通过分别包含相应的关键字 document 或 content,xml 可指定为必须是一个文档或者可以包含一个片段。如果省略,则默认值为 content。

  可以使用 t-sql 命令将 xml 架构集合添加到数据库中,如下所示:

create xml schema collection [fooschema] as n 'put your schema here'

  也可以使用 sql server management studio (ssms) 创建 xml 架构集合模板。从 view 菜单打开 template explorer,然后导航到 xml schema collections 节点并展开它。然后,您可以双击 create 模板打开一个模板,该模板为您创建 xml 架构集合提供了一个良好的语法开端(参见图 1)。

  图1:xml架构集合模板是ssms

  sql server 2005 随附的 adventureworks 数据库有一个名为 humanresources.jobcandidate 的表。该表包含一个名为 resume 的列,该列是一个绑定到架构集合 humanresources.hrresumeschemacollection 的 xml 列。该列中存储的所有 xml 数据都必须遵循该架构。



|||

  图2:resume架构

  类型化和非类型化 xml 列都可以进行索引,但索引时,绑定到架构的 xml 列比非类型化 xml 列具有更多优点。将 xml 索引应用于非类型化 xml 列时,必须分析大部分 xml 结构来定位匹配的节点。但是,将 xml 索引应用于类型化 xml 列时,特定节点是可识别的并可根据架构定位。因此,索引可以在类型化 xml 中更有效地工作,因为它知道在哪里查找。此外,如果需要搜索一个数值范围,则索引将用非类型化 xml 执行一个数据类型转换(因为数据类型是未知的)。类型化 xml 定义自己的数据类型,从而避免了转换开销。

  问:我刚刚将 sql server 2000 数据库迁移到 sql server 2005。我听说 sql server 2005 在 t-sql 中引入了 try/catch 异常处理。与现有的 if @@error <> 0 技术相比,新的异常处理如何工作呢?

  答:通常对使用 t-sql 编写代码的抱怨是,t-sql 始终缺少一个健壮的异常处理结构。通过 sql server 2000,您可以编写 t-sql 批处理代码,以检查是否存在错误甚至何时引发错误(如果需要)。但与 try/catch 技术相比,执行此操作的技术是最基本的。首先,我将使用 @@error 函数定义与异常处理相关联的问题,然后展示新的 try/catch 技术与之相比是如何执行的。

  sql server 公开内置的 @@error 函数,该函数返回所执行的上一条 t-sql 命令的错误号。该函数的问题在于,它始终返回从刚刚执行的上一个语句返回的错误。这意味着,如果您执行一个导致错误的 insert 语句,然后执行另一个不会导致错误的任意 sql 语句,之后再查看 @@error 的返回值,则该函数将返回 0,原因是上一个语句没有返回错误。您必须十分小心,以确保在执行每个单独语句之后检查 @@error 的值。


|||begin transaction
delete [order details] where orderid in
(select orderid from orders where customerid = 'alfki')
if @@error <> 0
begin
rollback transaction
return
end
delete orders where customerid = 'alfki'
if @@error <> 0
begin
rollback transaction
return
end
delete customers where customerid = 'alfki'
if @@error <> 0
begin
rollback transaction
return
end
print 'i got here'
-- normally do a commit transaction here.
-- but i do a rollback so i don't truly delete my test data.
rollback transaction

  展示一些示例代码,这些代码将在事务内部运行,依次删除客户的定单详情、定单以及客户本身。在每个 delete 语句之后,代码都会检查 @@error 函数的值,以查看 delete 语句是否导致了错误。如果是,则回滚事务,停止执行代码,然后从批处理操作返回。

  这是标准的事务管理,即如果发生错误,则中止事务,然后将数据的状态返回至其初始状态。请注意,如果一切顺利并且没有发生错误,我会打印一个成功消息,然后回滚事务。通常,我不会成功地进行回滚,因为这是一个测试事务,我实际上并不希望删除我的数据。

  请注意,我必须遵循带有 @@error 函数的每个语句,然后再使用 rollback 和 return。该方法十分糟糕。假设编写一个包含许多查询(这些查询都需要检查)的事务。该过程可以通过 goto 语句来稍加简化,当然我并不是 goto 语句的提倡者,因为它们依旧未解决需要在查询操作之后立即检查 @@error 函数的问题。


|||

  sql server 2005 仍然支持 @@error 函数,但它还包括了广为人知的 try/catch 模式。try/catch 结构与 c# try/catch 结构的类似之处在于,在 try 块中捕获错误,然后转移到 catch 块执行。(t-sql 版本的 try/catch 没有 finally 子句选项,但 c# 版本有。您可以通过捕获异常、不执行返回、然后使用适当的清除代码执行 catch 块来进行模拟。)因此,在某些情况下需要注意这一点,例如,在指定了 cursor 并在 t-sql try 块中引发错误时打开该游标的情况。在这种情况下,应该在 catch 块中检查游标以查看它是否已打开;如果已打开,则应将其关闭并取消指定。

begin try
begin transaction
delete [order details] where orderid in
(select orderid from orders where customerid = 'alfki')
delete orders where customerid = 'alfki'
delete customers where customerid = 'alfki'
print 'committing deletes'
commit transaction
end try
begin catch
rollback transaction
return
end catch

  看到上面代码中的 try/catch 结构后,您可能会震惊于它与 @@error 技术相比是多么简洁。请注意,对于 try/catch 块,不需要重复检查是否有错误,因而减少了代码行数同时也减少了出现编码错误的地方。

  上面代码中的代码试图开始一个事务,执行一系列 delete 语句,然后提交该事务。(另请注意,在我的示例中,我没有提交事务而是回滚该事务,因此实际上并没有删除数据。实际上,这应该是一个 commit tran 语句。)

  可以在 try/catch 结构中访问多个内置函数,以帮助您确定导致代码进入 catch 块的原因。例如,可以在 catch 块内部添加以下语句,返回有关该错误的信息:

  • 网站运营seo文章大全
  • 提供全面的站长运营经验及seo技术!
  • |||select
    error_line() as errorline,
    error_message() as errormessage,
    error_number() as errornumber,
    error_procedure() as errorprocedure,
    error_severity() as errorseverity,
    error_state() as errorstate

      这些函数(如果适用)将返回发生错误的行号、错误消息、错误号、存储过程或发生错误的触发器、错误的严重级别以及错误的状态级别。

      try/catch 结构捕获严重度为 11 到 19(包括 11 和 19)的错误。低于 11 的严重级别是警告,不视为错误。严重级别为 20 及以上的错误视为严重错误。然而,如果这些严重级别为 20 及以上的错误没有导致数据库引擎停止,则它们将由 try/catch 捕获。总之,try/catch 语句更易于读取、更易于维护,且更不容易导致复制和粘贴错误。此外,@@error 技术也不总是很可靠。例如,某些错误会中止计划或整个批处理。

      问:我知道可以创建触发器来审核对数据的更改,但是如果表的架构发生更改,我如何执行操作呢?

      答:我在简介中提到过,现在有了一个新的触发器:sql server 2005 中的 ddl 触发器。数据操作语言 (dml) 触发器基于数据操作激发,而 ddl 触发器在对数据库架构或服务器进行更改时激发。创建的 ddl 触发器只能在发生触发事件之后激发,而 dml 触发器则不同,它可以在事件之后或代替事件激发。以下是创建 ddl 触发器的语法,在 sql server 2005 文档中进行了概述:

    create trigger trigger_name
    on { all server | database }
    [ with encryption ]
    { for | after } { event_type [ ,...n ] | ddl_database_level_events }
    [ with append ]
    [ not for replication ]
    { as
    { sql_statement [ ...n ] | external name < method specifier > }
    }
    < method_specifier > ::= assembly_name:class_name[::method_name]


    |||

      请注意,您可以创建触发器,以便它在对数据库的架构或数据库服务器进行更改时激发。您还可以通过指定事件类型(这些类型都列在 sql server 2005 文档中)来定义导致触发器激发的事件。或者,也可以通过指定 ddl_database_level_events 标识符让 ddl 触发器在所有事件之后激发。

      通过 ddl 触发器,您可以记录对数据库的架构所作的更改。可以使用 ddl 触发器审核更改,而不是防止更改。或者,也可以通过 ddl 触发器记录尝试操作,然后将其回滚。您可能还希望在创建对象时对其强制命名约定。例如,您可能希望所有存储过程都以 pr 为前缀。通过 ddl 触发器,您可以强制该命名约定。

      ddl 触发器与 dml 触发器的类似之处在于,它们都可以回滚事务。然而,ddl 触发器没有插入或删除的表。ddl 触发器能够访问一个名为 eventdata 的内置函数,该函数在 xml 数据类型中返回 xml(包含有关激发触发器的事件的信息)。每个事件在事件数据中显示的信息都略有不同。但是,eventdata 函数始终为所有事件返回以下信息:

      ◆事件的时间

      ◆事件的类型

      ◆导致触发器激发的连接 spid

      ◆发生事件的用户上下文的登录名和用户名

    create trigger ddltrigger_procnamingconvention
    on database
    after create_procedure, alter_procedure
    as
    declare @eventdataxml xml
    set @eventdataxml = eventdata()
    if substring(@eventdataxml.value('(//objectname)[1]',
    'varchar(200)'), 1, 2) = 'pr'
    print 'starts with pr'
    else
    begin
    print 'does not start with pr'
    rollback transaction
    end
    go

    最大的网站源码资源下载站,

    |||

      请注意,这段代码中的 ddl 触发器定义为,在当前数据库上创建或更改存储过程时激发。当触发器激发后,事件的数据会被捕获并存储在本地 xml 变量中。尽管这不是必要的,但如果您打算多次访问 eventdata 函数,这有助于使触发器更高效。然后,触发器会检查对象的名称是否以 pr 开头。如果是,则允许事务完成;如果不是,则回滚事务,并且存储过程将保持不变。在本例中,我知道 objectname 元素将包含存储过程的名称,因为仅在创建或更改存储过程时才会调用触发器。

    <event_instance>
    <eventtype>create_procedure</eventtype>
    <posttime>2005-10-20t00:52:16.160</posttime>
    <spid>51</spid>
    <servername>mydbserver</servername>
    <loginname>camelotjpapa</loginname>
    <username>dbo</username>
    <databasename>adventureworks</databasename>
    <schemaname>dbo</schemaname>
    <objectname>test1</objectname>
    <objecttype>procedure</objecttype>
    <tsqlcommand>
    <setoptions ansi_nulls="on" ansi_null_default="on" ansi_padding="on"
    quoted_identifier="on" encrypted="false" />
    <commandtext>create proc test1 as select getdate()</commandtext>
    </tsqlcommand>
    </event_instance>

      显示执行以下命令后该触发器的事件数据:

    create proc test1 as select getdate

      您还可以捕获事件数据,并将其完整存储到审核表的 xml 列中。另一个选择是抽出特定节点的内容,并将其单独存储。显然,实现视具体情况而定,但 ddl 触发器为您提供的选择有很多。使用 ddl 触发器时需要记住的一件事是,它们是同步操作的。由于是同步执行,触发器应该尽可能不执行费时的操作。否则,它们会对数据库服务器的性能造成负面影响。与 dml 触发器一样,应该将 ddl 触发器定义为仅执行所需的操作并快速完成。如果需要异步执行,可以使用 service broker 和 event notification 模型。

    发表评论 共有条评论
    用户名: 密码:
    验证码: 匿名发表