﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-study-随笔分类-SQL</title><link>http://www.blogjava.net/xixidabao/category/15383.html</link><description>GROW WITH JAVA</description><language>zh-cn</language><lastBuildDate>Fri, 21 Dec 2007 19:48:22 GMT</lastBuildDate><pubDate>Fri, 21 Dec 2007 19:48:22 GMT</pubDate><ttl>60</ttl><item><title>SQL语句处理的过程</title><link>http://www.blogjava.net/xixidabao/archive/2007/12/20/169007.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Thu, 20 Dec 2007 05:30:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2007/12/20/169007.html</guid><description><![CDATA[在调整之前我们需要了解一些背景知识，只有知道这些背景知识，我们才能更好的去调整sql语句。<br />
本节介绍了SQL语句处理的基本过程，主要包括：<br />
　　&#183;&nbsp; &nbsp; &nbsp; &nbsp; 查询语句处理 <br />
　　&#183;&nbsp; &nbsp; &nbsp; &nbsp; DML语句处理(insert, update, delete) <br />
　　&#183;&nbsp; &nbsp; &nbsp; &nbsp; DDL 语句处理(create .. , drop .. , alter .. , ) <br />
　　&#183;&nbsp; &nbsp; &nbsp; &nbsp; 事务控制(commit, rollback) <br />
<br />
<strong>　　SQL 语句的执行过程(SQL Statement Execution)</strong><br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;<br />
　　 图3-1 概要的列出了处理和运行一个sql语句的需要各个重要阶段。在某些情况下，Oracle运行sql的过程可能与下面列出的各个阶段的顺序有所不同。如DEFINE阶段可能在FETCH阶段之前，这主要依赖你如何书写代码。<br />
<br />
　　对许多oracle的工具来说，其中某些阶段会自动执行。绝大多数用户不需要关心各个阶段的细节问题，然而，知道执行的各个阶段还是有必要的，这会帮助你写出更高效的SQL语句来，而且还可以让你猜测出性能差的SQL语句主要是由于哪一个阶段造成的，然后我们针对这个具体的阶段，找出解决的办法。<br />
<br />
　　图 3-1&nbsp;&nbsp;SQL语句处理的各个阶段<br />
<br />
<strong>　　DML语句的处理</strong><br />
&nbsp; &nbsp;&nbsp; &nbsp;<br />
　　本节给出一个例子来说明在DML语句处理的各个阶段到底发生了什么事情。假设你使用Pro*C程序来为指定部门的所有职员增加工资。程序已经连到正确的用户，你可以在你的程序中嵌入如下的SQL语句：<br />
EXEC SQL UPDATE employees <br />
SET salary = 1.10 * salary&nbsp;WHERE department_id = :var_department_id; var_department_id是程序变量，里面包含部门号，我们要修改该部门的职员的工资。当这个SQL语句执行时，使用该变量的值。<br />
<br />
　　每种类型的语句都需要如下阶段：<br />
　　&#183;&nbsp; &nbsp; &nbsp; &nbsp; 第1步: Create a Cursor&nbsp; &nbsp;&nbsp;&nbsp;创建游标<br />
　　&#183;&nbsp; &nbsp; &nbsp; &nbsp; 第2步: Parse the Statement&nbsp;&nbsp;分析语句<br />
　　&#183;&nbsp; &nbsp; &nbsp; &nbsp; 第5步: Bind Any Variables&nbsp; &nbsp; 绑定变量<br />
　　&#183;&nbsp; &nbsp; &nbsp; &nbsp; 第7步: Run the Statement&nbsp; &nbsp; 运行语句<br />
　　&#183;&nbsp; &nbsp; &nbsp; &nbsp; 第9步: Close the Cursor&nbsp; &nbsp;&nbsp;&nbsp;关闭游标<br />
<br />
　　如果使用了并行功能，还会包含下面这个阶段：<br />
　　&#183;&nbsp; &nbsp; &nbsp; &nbsp; 第6步: Parallelize the Statement&nbsp; &nbsp;并行执行语句<br />
<br />
　　如果是查询语句，则需要以下几个额外的步骤，如图 3所示：<br />
　　&#183;&nbsp; &nbsp; &nbsp; &nbsp; 第3步: Describe Results of a Query&nbsp; &nbsp;描述查询的结果集<br />
　　&#183;&nbsp; &nbsp; &nbsp; &nbsp; 第4步: Define Output of a Query&nbsp; &nbsp;&nbsp; &nbsp;定义查询的输出数据<br />
　　&#183;&nbsp; &nbsp; &nbsp; &nbsp; 第8步: Fetch Rows of a Query&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;取查询出来的行<br />
<br />
　　下面具体说一下每一步中都发生了什么事情：.<br />
<strong><br />
　　第1步: 创建游标(Create a Cursor)</strong><br />
<br />
&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;&nbsp;由程序接口调用创建一个游标（cursor）。任何SQL语句都会创建它，特别在运行DML语句时，都是自动创建游标的，不需要开发人员干预。多数应用中，游标的创建是自动的。然而，在预编译程序(pro*c)中游标的创建，可能是隐含的，也可能显式的创建。在存储过程中也是这样的。<br />
<br />
<strong>　　第2步:分析语句(Parse the Statement)</strong><br />
&nbsp;<br />
　　在语法分析期间，SQL语句从用户进程传送到Oracle，SQL语句经语法分析后，SQL语句本身与分析的信息都被装入到共享SQL区。在该阶段中，可以解决许多类型的错误。<br />
<br />
　　语法分析分别执行下列操作：<br />
l&nbsp; &nbsp; &nbsp; &nbsp; 翻译SQL语句，验证它是合法的语句，即书写正确<br />
l&nbsp; &nbsp; &nbsp; &nbsp; 实现数据字典的查找，以验证是否符合表和列的定义<br />
l&nbsp; &nbsp; &nbsp; &nbsp; 在所要求的对象上获取语法分析锁，使得在语句的语法分析过程中不改变这些对象的定义<br />
l&nbsp; &nbsp; &nbsp; &nbsp; 验证为存取所涉及的模式对象所需的权限是否满足<br />
l&nbsp; &nbsp; &nbsp; &nbsp; 决定此语句最佳的执行计划<br />
l&nbsp; &nbsp; &nbsp; &nbsp; 将它装入共享SQL区<br />
l&nbsp; &nbsp; &nbsp; &nbsp; 对分布的语句来说，把语句的全部或部分路由到包含所涉及数据的远程节点<br />
&nbsp; &nbsp;&nbsp; &nbsp;<br />
　　以上任何一步出现错误，都将导致语句报错，中止执行。<br />
<br />
　　只有在共享池中不存在等价SQL语句的情况下，才对SQL语句作语法分析。在这种情况下，数据库内核重新为该语句分配新的共享SQL区，并对语句进行语法分析。进行语法分析需要耗费较多的资源，所以要尽量避免进行语法分析，这是优化的技巧之一。<br />
<br />
　　语法分析阶段包含了不管此语句将执行多少次，而只需分析一次的处理要求。Oracle只对每个SQL语句翻译一次，在以后再次执行该语句时，只要该语句还在共享SQL区中，就可以避免对该语句重新进行语法分析，也就是此时可以直接使用其对应的执行计划对数据进行存取。这主要是通过绑定变量(bind variable)实现的，也就是我们常说的共享SQL，后面会给出共享SQL的概念。<br />
<br />
　　虽然语法分析验证了SQL语句的正确性，但语法分析只能识别在SQL语句执行之前所能发现的错误(如书写错误、权限不足等)。因此，有些错误通过语法分析是抓不到的。例如，在数据转换中的错误或在数据中的错（如企图在主键中插入重复的值）以及死锁等均是只有在语句执行阶段期间才能遇到和报告的错误或情况。<br />
<br />
<strong>　　查询语句的处理</strong><br />
&nbsp; &nbsp;&nbsp; &nbsp;<br />
　　查询与其它类型的SQL语句不同，因为在成功执行后作为结果将返回数据。其它语句只是简单地返回成功或失败，而查询则能返回一行或许多行数据。查询的结果均采用表格形式，结果行被一次一行或者批量地被检索出来。从这里我们可以得知批量的fetch数据可以降低网络开销，所以批量的fetch也是优化的技巧之一。<br />
<br />
&nbsp; &nbsp;&nbsp; &nbsp; 有些问题只与查询处理相关，查询不仅仅指SELECT语句，同样也包括在其它SQL语句中的隐含查询。例如，下面的每个语句都需要把查询作为它执行的一部分：<br />
INSERT INTO table SELECT... <br />
UPDATE table SET x = y WHERE... <br />
DELETE FROM table WHERE... <br />
CREATE table AS SELECT... <br />
<br />
　　具体来说，查询<br />
&#183;&nbsp; &nbsp; &nbsp; &nbsp; 要求读一致性<br />
&#183;&nbsp; &nbsp; &nbsp; &nbsp; 可能使用回滚段作中间处理<br />
&#183;&nbsp; &nbsp; &nbsp; &nbsp; 可能要求SQL语句处理描述、定义和取数据阶段<br />
<br />
<strong>　　第3步: 描述查询结果(Describe Results of a Query)</strong><br />
&nbsp;<br />
　　描述阶段只有在查询结果的各个列是未知时才需要；例如，当查询由用户交互地输入需要输出的列名。在这种情况要用描述阶段来决定查询结果的特征（数据类型，长度和名字）。<br />
<br />
<strong>　　第4步: 定义查询的输出数据(Define Output of a Query)&nbsp;&nbsp;</strong><br />
&nbsp; &nbsp;&nbsp; &nbsp; <br />
　　在查询的定义阶段，你指定与查询出的列值对应的接收变量的位置、大小和数据类型，这样我们通过接收变量就可以得到查询结果。如果必要的话，Oracle会自动实现数据类型的转换。这是将接收变量的类型与对应的列类型相比较决定的。<br />
<br />
<strong>　　第5步: 绑定变量(Bind Any Variables)</strong><br />
&nbsp; &nbsp;&nbsp; &nbsp;<br />
　　此时，Oracle知道了SQL语句的意思，但仍没有足够的信息用于执行该语句。Oracle 需要得到在语句中列出的所有变量的值。在该例中，Oracle需要得到对department_id列进行限定的值。得到这个值的过程就叫绑定变量(binding variables)<br />
<br />
　　此过程称之为将变量值捆绑进来。程序必须指出可以找到该数值的变量名（该变量被称为捆绑变量，变量名实质上是一个内存地址，相当于指针）。应用的最终用户可能并没有发觉他们正在指定捆绑变量，因为Oracle 的程序可能只是简单地指示他们输入新的值，其实这一切都在程序中自动做了。因为你指定了变量名，在你再次执行之前无须重新捆绑变量。你可以改变绑定变量的值，而Oracle在每次执行时，仅仅使用内存地址来查找此值。如果Oracle 需要实现自动数据类型转换的话（除非它们是隐含的或缺省的），你还必须对每个值指定数据类型和长度。关于这些信息可以参考oracle的相关文档，如Oracle Call Interface Programmer's Guide<br />
<br />
<strong>　　第6步: 并行执行语句(Parallelize the Statement )</strong><br />
&nbsp; &nbsp;&nbsp;&nbsp;<br />
　　ORACLE 可以在SELECTs, INSERTs, UPDATEs, MERGEs, DELETEs语句中执行相应并行查询操作，对于某些DDL操作，如创建索引、用子查询创建表、在分区表上的操作，也可以执行并行操作。并行化可以导致多个服务器进程(oracle server processes)为同一个SQL语句工作，使该SQL语句可以快速完成，但是会耗费更多的资源，所以除非很有必要，否则不要使用并行查询。<br />
<br />
<strong>　　第7步: 执行语句(Run the Statement)</strong><br />
&nbsp; &nbsp;&nbsp; &nbsp;<br />
　　到了现在这个时候，Oracle拥有所有需要的信息与资源，因此可以真正运行SQL语句了。如果该语句为SELECT查询或INSERT语句，则不需要锁定任何行，因为没有数据需要被改变。然而，如果语句为UPDATE或DELETE语句，则该语句影响的所有行都被锁定，防止该用户提交或回滚之前，别的用户对这些数据进行修改。这保证了数据的一致性。对于某些语句，你可以指定执行的次数，这称为批处理(array processing)。指定执行N次，则绑定变量与定义变量被定义为大小为N的数组的开始位置，这种方法可以减少网络开销，也是优化的技巧之一。<br />
<br />
<strong>　　第8步: 取出查询的行(Fetch Rows of a Query)</strong><br />
&nbsp; &nbsp;&nbsp; &nbsp;<br />
　　在fetch阶段，行数据被取出来，每个后续的存取操作检索结果集中的下一行数据，直到最后一行被取出来。上面提到过，批量的fetch是优化的技巧之一。<br />
<br />
<strong>　　第9步: 关闭游标(Close the Cursor)</strong><br />
&nbsp; &nbsp;&nbsp; &nbsp;<br />
　　SQL语句处理的最后一个阶段就是关闭游标<br />
<br />
<strong>　　DDL语句的处理(DDL Statement Processing)</strong><br />
&nbsp; &nbsp;&nbsp;&nbsp;<br />
　　DDL语句的执行不同与DML语句和查询语句的执行，这是因为DDL语句执行成功后需要对数据字典数据进行修改。对于DDL语句，语句的分析阶段实际上包括分析、查找数据字典信息和执行。事务管理语句、会话管理语句、系统管理语句只有分析与执行阶段，为了重新执行该语句，会重新分析与执行该语句。<br />
<br />
<strong>　　事务控制(Control of Transactions)</strong><br />
&nbsp; &nbsp;&nbsp; &nbsp;<br />
　　一般来说，只有使用ORACLE编程接口的应用设计人员才关心操作的类型，并把相关的操作组织在一起，形成一个事务。一般来说，我门必须定义事务，这样在一个逻辑单元中的所有工作可以同时被提交或回滚，保证了数据的一致性。一个事务应该由逻辑单元中的所有必须部分组成，不应该多一个，也不应该少一个。<br />
　　&#183;&nbsp; &nbsp; &nbsp; &nbsp; 在事务开始和结束的这段时间内，所有被引用表中的数据都应该在一致的状态(或可以被回溯到一致的状态)<br />
　　&#183;&nbsp; &nbsp; &nbsp; &nbsp; 事务应该只包含可以对数据进行一致更改(one consistent change to the data)的SQL语句<br />
<br />
　　例如，在两个帐号之间的转帐(这是一个事务或逻辑工作单元)，应该包含从一个帐号中借钱(由一个SQL完成)，然后将借的钱存入另一个帐号(由另一个SQL完成)。这2个操作作为一个逻辑单元，应该同时成功或同时失败。其它不相关的操作，如向一个帐户中存钱，不应该包含在这个转帐事务中。<br />
<br />
　　在设计应用时，除了需要决定哪种类型的操作组成一个事务外，还需要决定使用BEGIN_DISCRETE_TRANSACTIO存储过程是否对提高小的、非分布式的事务的性能有作用。
<img src ="http://www.blogjava.net/xixidabao/aggbug/169007.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2007-12-20 13:30 <a href="http://www.blogjava.net/xixidabao/archive/2007/12/20/169007.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>SQL触发器语法参考 </title><link>http://www.blogjava.net/xixidabao/archive/2007/12/07/165947.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Fri, 07 Dec 2007 01:13:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2007/12/07/165947.html</guid><description><![CDATA[<div class="postTitle"><a id="AjaxHolder_ctl01_TitleUrl" href="http://www.cnblogs.com/yesun/archive/2007/02/06/641481.html">SQL触发器语法参考</a> </div>
<div class="postText">
<p><font face="新宋体"><code>CREATE TRIGGER <em>trigger_name </em></code><br />
<code>ON { <em>table</em> | <em>view </em></code></font>}<font face="新宋体"><code> <br />
[ WITH ENCRYPTION ] <br />
{</code><br />
</font>&nbsp;&nbsp;&nbsp;&nbsp;{<code><font face="新宋体"> </font></code><strong>{<code><font face="新宋体"> </font></code></strong><code><font face="新宋体">FOR | AFTER | INSTEAD OF </font></code><strong>}</strong><font face="新宋体"><code> { [ INSERT ] [ <strong>, </strong>] [ UPDATE ] }</code><br />
</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font face="新宋体"><code>[ WITH APPEND ]</code><br />
</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font face="新宋体"><code>[ NOT FOR REPLICATION ]</code><br />
</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font face="新宋体"><code>AS</code><br />
</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font face="新宋体"><code>[ { IF UPDATE <strong>( </strong><em>column </em><strong>)</strong></code><br />
</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font face="新宋体"><code>[ { AND | OR } UPDATE <strong>( </strong><em>column </em><strong>) </strong>]</code><br />
</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<font face="新宋体"><code>[ ...<em>n </em>]</code><br />
</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<code><font face="新宋体">| IF <strong>(</strong> COLUMNS_UPDATED <strong>( ) </strong></font></code>{<code><font face="新宋体"> <em>bitwise_operator </em></font></code>}<font face="新宋体"><code> <em>updated_bitmask </em><strong>)</strong></code><br />
</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{<code><font face="新宋体"> <em>comparison_operator </em></font></code>}<font face="新宋体"><code> <em>column_bitmask</em> [ ...<em>n </em>]</code><br />
</font>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<code><font face="新宋体"> ] <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<em>sql_statement</em> [<strong> </strong>...<em>n </em>] <br />
&nbsp;&nbsp;&nbsp;&nbsp;} <br />
} </font></code></p>
<h5>参数</h5>
<p><em>trigger_name</em></p>
<p class="indent">是触发器的名称。触发器名称必须符合标识符规则，并且在数据库中必须唯一。可以选择是否指定触发器所有者名称。</p>
<p><em>Table</em> | <em>view</em></p>
<p class="indent">是在其上执行触发器的表或视图，有时称为触发器表或触发器视图。可以选择是否指定表或视图的所有者名称。</p>
<p>WITH ENCRYPTION</p>
<p class="indent">加密 <strong>syscomments</strong> 表中包含 CREATE TRIGGER 语句文本的条目。使用 WITH ENCRYPTION 可防止将触发器作为 SQL Server 复制的一部分发布。</p>
<p>AFTER</p>
<p class="indent">指定触发器只有在触发 SQL 语句中指定的所有操作都已成功执行后才激发。所有的引用级联操作和约束检查也必须成功完成后，才能执行此触发器。</p>
<p class="indent">如果仅指定 FOR 关键字，则 AFTER 是默认设置。</p>
<p class="indent">不能在视图上定义 AFTER 触发器。</p>
<p>INSTEAD OF</p>
<p class="indent">指定执行触发器而不是执行触发 SQL 语句，从而替代触发语句的操作。</p>
<p class="indent">在表或视图上，每个 INSERT、UPDATE 或 DELETE 语句最多可以定义一个 INSTEAD OF 触发器。然而，可以在每个具有 INSTEAD OF 触发器的视图上定义视图。</p>
<p class="indent">INSTEAD OF 触发器不能在 WITH CHECK OPTION 的可更新视图上定义。如果向指定了 WITH CHECK OPTION 选项的可更新视图添加 INSTEAD OF 触发器，SQL Server 将产生一个错误。用户必须用 ALTER VIEW 删除该选项后才能定义 INSTEAD OF 触发器。</p>
<p>{ [DELETE] [,] [INSERT] [,] [UPDATE] }</p>
<p class="indent">是指定在表或视图上执行哪些数据修改语句时将激活触发器的关键字。必须至少指定一个选项。在触发器定义中允许使用以任意顺序组合的这些关键字。如果指定的选项多于一个，需用逗号分隔这些选项。</p>
<p class="indent">对于 INSTEAD OF 触发器，不允许在具有 ON DELETE 级联操作引用关系的表上使用 DELETE 选项。同样，也不允许在具有 ON UPDATE 级联操作引用关系的表上使用 UPDATE 选项。</p>
<p>WITH APPEND</p>
<p class="indent">指定应该添加现有类型的其它触发器。只有当兼容级别是 65 或更低时，才需要使用该可选子句。如果兼容级别是 70 或更高，则不必使用 WITH APPEND 子句添加现有类型的其它触发器（这是兼容级别设置为 70 或更高的 CREATE TRIGGER 的默认行为）。有关更多信息，请参见<strong> <a href="http://www.51windows.net/pages/ts_sp_da-di_5c8c.htm">sp_dbcmptlevel</a></strong>。</p>
<p class="indent">WITH APPEND 不能与 INSTEAD OF 触发器一起使用，或者，如果显式声明 AFTER 触发器，也不能使用该子句。只有当出于向后兼容而指定 FOR 时（没有 INSTEAD OF 或 AFTER），才能使用 WITH APPEND。以后的版本将不支持 WITH APPEND 和 FOR（将被解释为 AFTER）。</p>
<p>NOT FOR REPLICATION</p>
<p class="indent">表示当复制进程更改触发器所涉及的表时，不应执行该触发器。</p>
<p>AS</p>
<p class="indent">是触发器要执行的操作。</p>
<p><em>sql_statement</em></p>
<p class="indent">是触发器的条件和操作。触发器条件指定其它准则，以确定 DELETE、INSERT 或 UPDATE 语句是否导致执行触发器操作。</p>
<p class="indent">当尝试 DELETE、INSERT 或 UPDATE 操作时，Transact-SQL语句中指定的触发器操作将生效。</p>
<p class="indent">触发器可以包含任意数量和种类的 Transact-SQL 语句。触发器旨在根据数据修改语句检查或更改数据；它不应将数据返回给用户。触发器中的 Transact-SQL 语句常常包含控制流语言。CREATE TRIGGER 语句中使用几个特殊的表： </p>
<ul type="disc">
    <li><strong>deleted</strong> 和 <strong>inserted</strong> 是逻辑（概念）表。这些表在结构上类似于定义触发器的表（也就是在其中尝试用户操作的表）；这些表用于保存用户操作可能更改的行的旧值或新值。例如，若要检索 <strong>deleted </strong>表中的所有值，请使用：
    <pre><code>SELECT *
    FROM deleted
    </code></pre>
    <li>如果兼容级别等于 70，那么在 DELETE、INSERT 或 UPDATE 触发器中，SQL Server 将不允许引用 <strong>inserted</strong> 和 <strong>deleted</strong> 表中的 <strong>text</strong>、<strong>ntext</strong> 或 <strong>image</strong> 列。不能访问 <strong>inserted</strong> 和 <strong>deleted</strong> 表中的 <strong>text</strong>、<strong>ntext</strong> 和 <strong>image</strong> 值。若要在 INSERT 或 UPDATE 触发器中检索新值，请将 <strong>inserted</strong> 表与原始更新表联接。当兼容级别是 65 或更低时，对 <strong>inserted</strong> 或 <strong>deleted</strong> 表中允许空值的<strong>text</strong>、<strong>ntext</strong> 或 <strong>image</strong> 列，将返回空值；如果这些列不可为空，则返回零长度字符串。
    <p class="tl">当兼容级别是 80 或更高时，SQL Server 允许在表或视图上通过 INSTEAD OF 触发器更新 <strong>text</strong>、<strong>ntext</strong> 或 <strong>image</strong> 列。</p>
    </li>
</ul>
<p><em>n</em></p>
<p class="indent">是表示触发器中可以包含多条 Transact-SQL 语句的占位符。对于 IF UPDATE <strong>(</strong><em>column</em><strong>)</strong> 语句，可以通过重复 UPDATE <strong>(</strong><em>column</em><strong>) </strong>子句包含多列。</p>
<p>IF UPDATE <strong>(</strong><em>column</em><strong>)</strong></p>
<p class="indent">测试在指定的列上进行的 INSERT 或 UPDATE 操作，不能用于 DELETE 操作。可以指定多列。因为在 ON 子句中指定了表名，所以在 IF UPDATE 子句中的列名前不要包含表名。若要测试在多个列上进行的 INSERT 或 UPDATE 操作，请在第一个操作后指定单独的 UPDATE<strong>(</strong><em>column</em><strong>) </strong>子句。在 INSERT 操作中 IF UPDATE 将返回 TRUE 值，因为这些列插入了显式值或隐性 (NULL) 值。</p>
<p class="level2"></p>
<p id="Alert_Note"><strong>说明</strong>&nbsp;&nbsp;IF UPDATE <strong>(</strong><em>column</em><strong>)</strong> 子句的功能等同于 IF、IF...ELSE 或 WHILE 语句，并且可以使用 BEGIN...END 语句块。有关更多信息，请参见<a href="http://www.51windows.net/pages/ts_ca-co_0tnp.htm">控制流语言</a>。 </p>
<p>&nbsp;</p>
<p class="indent">可以在触发器主体中的任意位置使用 UPDATE (<em>column</em>)。</p>
<p><em>column</em></p>
<p class="indent">是要测试 INSERT 或 UPDATE 操作的列名。该列可以是 SQL Server 支持的任何数据类型。但是，计算列不能用于该环境中。有关更多信息，请参见<a href="http://www.51windows.net/pages/ts_da-db_7msw.htm">数据类型</a>。 </p>
<p>IF <strong>(</strong>COLUMNS_UPDATED<strong>()) </strong></p>
<p class="indent">测试是否插入或更新了提及的列，仅用于 INSERT 或 UPDATE 触发器中。COLUMNS_UPDATED 返回 <strong>varbinary</strong> 位模式，表示插入或更新了表中的哪些列。</p>
<p class="indent">COLUMNS_UPDATED 函数以从左到右的顺序返回位，最左边的为最不重要的位。最左边的位表示表中的第一列；向右的下一位表示第二列，依此类推。如果在表上创建的触发器包含 8 列以上，则 COLUMNS_UPDATED 返回多个字节，最左边的为最不重要的字节。在 INSERT 操作中 COLUMNS_UPDATED 将对所有列返回 TRUE 值，因为这些列插入了显式值或隐性 (NULL) 值。</p>
<p class="indent">可以在触发器主体中的任意位置使用 COLUMNS_UPDATED。</p>
<p><em>bitwise_operator</em></p>
<p class="indent">是用于比较运算的位运算符。</p>
<p><em>updated_bitmask</em></p>
<p class="indent">是整型位掩码，表示实际更新或插入的列。例如，表 <strong>t1 </strong>包含列 <strong>C1</strong>、<strong>C2</strong>、<strong>C3</strong>、<strong>C4</strong> 和 <strong>C5</strong>。假定表 <strong>t1</strong> 上有 UPDATE 触发器，若要检查列 <strong>C2、C3 </strong>和 <strong>C4</strong> 是否都有更新，指定值 14；若要检查是否只有列 <strong>C2</strong> 有更新，指定值 2。</p>
<p><em>comparison_operator</em></p>
<p class="indent">是比较运算符。使用等号 (=) 检查 <em>updated_bitmask</em> 中指定的所有列是否都实际进行了更新。使用大于号 (&gt;) 检查 <em>updated_bitmask </em>中指定的任一列或某些列是否已更新。</p>
<p><em>column_bitmask</em></p>
<p class="indent">是要检查的列的整型位掩码，用来检查是否已更新或插入了这些列。 </p>
<h5>注释</h5>
<p>触发器常常用于强制业务规则和数据完整性。SQL Server 通过表创建语句（ALTER TABLE 和 CREATE TABLE）提供声明引用完整性 (DRI)；但是 DRI 不提供数据库间的引用完整性。若要强制引用完整性（有关表的主键和外键之间关系的规则），请使用主键和外键约束（ALTER TABLE 和 CREATE TABLE 的 PRIMARY KEY 和 FOREIGN KEY 关键字）。如果触发器表存在约束，则在 INSTEAD OF 触发器执行之后和 AFTER 触发器执行之前检查这些约束。如果违反了约束，则回滚 INSTEAD OF 触发器操作且不执行（激发）AFTER 触发器。</p>
<p>可用 <strong>sp_settriggerorder </strong>指定表上第一个和最后一个执行的 AFTER 触发器。在表上只能为每个 INSERT、UPDATE 和 DELETE 操作指定一个第一个执行和一个最后一个执行的 AFTER 触发器。如果同一表上还有其它 AFTER 触发器，则这些触发器将以随机顺序执行。</p>
<p>如果 ALTER TRIGGER 语句更改了第一个或最后一个触发器，则将除去已修改触发器上设置的第一个或最后一个特性，而且必须用 <strong>sp_settriggerorder </strong>重置排序值。</p>
<p>只有当触发 SQL 语句（包括所有与更新或删除的对象关联的引用级联操作和约束检查）成功执行后，AFTER 触发器才会执行。AFTER 触发器检查触发语句的运行效果，以及所有由触发语句引起的 UPDATE 和 DELETE 引用级联操作的效果。</p>
<h6>触发器限制</h6>
<p>CREATE TRIGGER 必须是批处理中的第一条语句，并且只能应用到一个表中。 </p>
<p>触发器只能在当前的数据库中创建，不过触发器可以引用当前数据库的外部对象。 </p>
<p>如果指定触发器所有者名称以限定触发器，请以相同的方式限定表名。 </p>
<p>在同一条 CREATE TRIGGER 语句中，可以为多种用户操作（如 INSERT 和 UPDATE）定义相同的触发器操作。 </p>
<p>如果一个表的外键在 DELETE/UPDATE 操作上定义了级联，则不能在该表上定义 INSTEAD OF DELETE/UPDATE 触发器。</p>
<p>在触发器内可以指定任意的 SET 语句。所选择的 SET 选项在触发器执行期间有效，并在触发器执行完后恢复到以前的设置。</p>
<p>与使用存储过程一样，当触发器激发时，将向调用应用程序返回结果。若要避免由于触发器激发而向应用程序返回结果，请不要包含返回结果的 SELECT 语句，也不要包含在触发器中进行变量赋值的语句。包含向用户返回结果的 SELECT 语句或进行变量赋值的语句的触发器需要特殊处理；这些返回的结果必须写入允许修改触发器表的每个应用程序中。如果必须在触发器中进行变量赋值，则应该在触发器的开头使用 SET NOCOUNT 语句以避免返回任何结果集。 </p>
<p>DELETE 触发器不能捕获 TRUNCATE TABLE 语句。尽管 TRUNCATE TABLE 语句实际上是没有 WHERE 子句的 DELETE（它删除所有行），但它是无日志记录的，因而不能执行触发器。因为 TRUNCATE TABLE 语句的权限默认授予表所有者且不可转让，所以只有表所有者才需要考虑无意中用 TRUNCATE TABLE 语句规避 DELETE 触发器的问题。</p>
<p>无论有日志记录还是无日志记录，WRITETEXT 语句都不激活触发器。</p>
<p>触发器中不允许以下 Transact-SQL 语句：</p>
<table cols="3" cellpadding="2" rules="all" width="587" border="1" frame="box">
    <tbody>
        <tr valign="top">
            <td width="33%">ALTER DATABASE</td>
            <td width="35%">CREATE DATABASE</td>
            <td width="32%">DISK INIT</td>
        </tr>
        <tr valign="top">
            <td width="33%">DISK RESIZE</td>
            <td width="35%">DROP DATABASE</td>
            <td width="32%">LOAD DATABASE</td>
        </tr>
        <tr valign="top">
            <td width="33%">LOAD LOG</td>
            <td width="35%">RECONFIGURE</td>
            <td width="32%">RESTORE DATABASE</td>
        </tr>
        <tr valign="top">
            <td width="33%">RESTORE LOG</td>
            <td width="35%">&nbsp;</td>
            <td width="32%">&nbsp;</td>
        </tr>
    </tbody>
</table>
<br />
<p class="indent"></p>
<p id="Alert_Note"><strong>说明</strong>&nbsp;&nbsp;由于 SQL Server 不支持系统表中的用户定义触发器，因此建议不要在系统表中创建用户定义触发器。</p>
<p>&nbsp;</p>
<h6>多个触发器</h6>
<p>SQL Server 允许为每个数据修改事件（DELETE、INSERT 或 UPDATE）创建多个触发器。例如，如果对已有 UPDATE 触发器的表执行 CREATE TRIGGER FOR UPDATE，则将创建另一个更新触发器。在早期版本中，在每个表上，每个数据修改事件（INSERT、UPDATE 或 DELETE）只允许有一个触发器。 </p>
<p class="indent"></p>
<p id="Alert_Note"><strong>说明</strong>&nbsp;&nbsp;如果触发器名称不同，则 CREATE TRIGGER（兼容级别为 70）的默认行为是在现有的触发器中添加其它触发器。如果触发器名称相同，则 SQL Server 返回一条错误信息。但是，如果兼容级别等于或小于 65，则使用 CREATE TRIGGER 语句创建的新触发器将替换同一类型的任何现有触发器，即使触发器名称不同。有关更多信息，请参见<strong> </strong><a href="http://www.51windows.net/pages/ts_sp_da-di_5c8c.htm">sp_dbcmptlevel</a>。 </p>
<p>&nbsp;</p>
<h6>递归触发器</h6>
<p>当在 <strong>sp_dboption</strong> 中启用 <strong>recursive triggers</strong> 设置时，SQL Server 还允许触发器的递归调用。</p>
<p>递归触发器允许发生两种类型的递归： </p>
<ul type="disc">
    <li>间接递归<br />
    <br />
    <li>直接递归 </li>
</ul>
<p>使用间接递归时，应用程序更新表 <strong>T1</strong>，从而激发触发器 <strong>TR1</strong>，该触发器更新表 <strong>T2</strong>。在这种情况下，触发器 <strong>T2</strong> 将激发并更新 <strong>T1</strong>。</p>
<p>使用直接递归时，应用程序更新表 <strong>T1</strong>，从而激发触发器 <strong>TR1</strong>，该触发器更新表 <strong>T1</strong>。由于表 <strong>T1</strong> 被更新，触发器 <strong>TR1</strong> 再次激发，依此类推。</p>
<p>下例既使用了间接触发器递归，又使用了直接触发器递归。假定在表 <strong>T1 </strong>中定义了两个更新触发器 <strong>TR1</strong> 和 <strong>TR2</strong>。触发器 <strong>TR1</strong> 递归地更新表 <strong>T1</strong>。UPDATE 语句使 <strong>TR1</strong> 和 <strong>TR2 </strong>各执行一次。而 <strong>TR1</strong> 的执行将触发 <strong>TR1</strong>（递归）和 <strong>TR2</strong> 的执行。给定触发器的 <strong>inserted</strong> 和 <strong>deleted</strong><em> </em>表只包含与唤醒调用触发器的 UPDATE 语句相对应的行。</p>
<p class="indent"></p>
<p id="Alert_Note"><strong>说明</strong>&nbsp;&nbsp;只有启用 <strong>sp_dboption</strong> 的 <strong>recursive triggers </strong>设置，才会发生上述行为。对于为给定事件定义的多个触发器，并没有确定的执行顺序。每个触发器都应是自包含的。</p>
<p>&nbsp;</p>
<p>禁用 <strong>recursive triggers</strong> 设置只能禁止直接递归。若要也禁用间接递归，请使用 <strong>sp_configure</strong> 将 <strong>nested triggers</strong> 服务器选项设置为 0。</p>
<p>如果任一触发器执行了 ROLLBACK TRANSACTION 语句，则无论嵌套级是多少，都不会进一步执行其它触发器。</p>
<h6>嵌套触发器</h6>
<p>触发器最多可以嵌套 32 层。如果一个触发器更改了包含另一个触发器的表，则第二个触发器将激活，然后该触发器可以再调用第三个触发器，依此类推。如果链中任意一个触发器引发了无限循环，则会超出嵌套级限制，从而导致取消触发器。若要禁用嵌套触发器，请用 <strong>sp_configure </strong>将 <strong>nested triggers</strong> 选项设置为 0（关闭）。默认配置允许嵌套触发器。如果嵌套触发器是关闭的，则也将禁用递归触发器，与 <strong>sp_dboption</strong> 的 <strong>recursive triggers </strong>设置无关。</p>
<h6>延迟名称解析</h6>
<p>SQL Server 允许 Transact-SQL 存储过程、触发器和批处理引用编译时不存在的表。这种能力称为延迟名称解析。但是，如果 Transact-SQL 存储过程、触发器或批处理引用在存储过程或触发器中定义的表，则只有当兼容级别设置（通过执行 <strong>sp_dbcmptlevel </strong>设置）等于 65 时，才会在创建时发出警告。如果使用批处理，则在编译时发出警告。如果引用的表不存在，将在运行时返回错误信息。有关更多信息，请参见<a ="javascript:hhobj_1.Click()">延迟名称解析和编译</a>。 </p>
<h5>权限</h5>
<p>CREATE TRIGGER 权限默认授予定义触发器的表所有者、<strong>sysadmin</strong> 固定服务器角色成员以及 <strong>db_owner</strong> 和 <strong>db_ddladmin </strong>固定数据库角色成员，并且不可转让。</p>
<p>若要检索表或视图中的数据，用户必须在表或视图中拥有 SELECT 语句权限。若要更新表或视图的内容，用户必须在表或视图中拥有 INSERT、DELETE 和 UPDATE 语句权限。</p>
<p>如果视图中存在 INSTEAD OF 触发器，用户必须在该视图中有 INSERT、DELETE 和 UPDATE 特权，以对该视图发出 INSERT、DELETE 和 UPDATE 语句，而不管实际上是否在视图上执行了这样的操作。</p>
<h5>示例</h5>
<h6>A. 使用带有提醒消息的触发器</h6>
<p>当有人试图在 <strong>titles</strong> 表中添加或更改数据时，下例将向客户端显示一条消息。</p>
<p class="indent"></p>
<p id="Alert_Note"><strong>说明</strong>&nbsp;&nbsp;消息 50009 是 <strong>sysmessages</strong> 中的用户定义消息。有关创建用户定义消息的更多信息，请参见 <a href="http://www.51windows.net/pages/ts_sp_adda_8zdx.htm">sp_addmessage</a>。 </p>
<p>&nbsp;</p>
<pre><code>USE pubs
IF EXISTS (SELECT name FROM sysobjects
WHERE name = 'reminder' AND type = 'TR')
DROP TRIGGER reminder
GO
CREATE TRIGGER reminder
ON titles
FOR INSERT, UPDATE
AS RAISERROR (50009, 16, 10)
GO
</code></pre>
<h6>B. 使用带有提醒电子邮件的触发器</h6>
<p>当 <strong>titles</strong> 表更改时，下例将电子邮件发送给指定的人员 (MaryM)。</p>
<pre><code>USE pubs
IF EXISTS (SELECT name FROM sysobjects
WHERE name = 'reminder' AND type = 'TR')
DROP TRIGGER reminder
GO
CREATE TRIGGER reminder
ON titles
FOR INSERT, UPDATE, DELETE
AS
EXEC master..xp_sendmail 'MaryM',
'Don''t forget to print a report for the distributors.'
GO
</code></pre>
<h6>C. 在 employee 和 jobs 表之间使用触发器业务规则</h6>
<p>由于 CHECK 约束只能引用定义了列级或表级约束的列，表间的任何约束（在下例中是指业务规则）都必须定义为触发器。</p>
<p>下例创建一个触发器，当插入或更新雇员工作级别 (<strong>job_lvls</strong>) 时，该触发器检查指定雇员的工作级别（由此决定薪水）是否处于为该工作定义的范围内。若要获得适当的范围，必须引用 <strong>jobs</strong> 表。</p>
<pre><code>USE pubs
IF EXISTS (SELECT name FROM sysobjects
WHERE name = 'employee_insupd' AND type = 'TR')
DROP TRIGGER employee_insupd
GO
CREATE TRIGGER employee_insupd
ON employee
FOR INSERT, UPDATE
AS
/* Get the range of level for this job type from the jobs table. */
DECLARE @min_lvl tinyint,
@max_lvl tinyint,
@emp_lvl tinyint,
@job_id smallint
SELECT @min_lvl = min_lvl,
@max_lvl = max_lvl,
@emp_lvl = i.job_lvl,
@job_id = i.job_id
FROM employee e INNER JOIN inserted i ON e.emp_id = i.emp_id
JOIN jobs j ON j.job_id = i.job_id
IF (@job_id = 1) and (@emp_lvl   10)
BEGIN
RAISERROR ('Job id 1 expects the default level of 10.', 16, 1)
ROLLBACK TRANSACTION
END
ELSE
IF NOT (@emp_lvl BETWEEN @min_lvl AND @max_lvl)
BEGIN
RAISERROR ('The level for job_id:%d should be between %d and %d.',
16, 1, @job_id, @min_lvl, @max_lvl)
ROLLBACK TRANSACTION
END
</code></pre>
<h6>D. 使用延迟名称解析</h6>
<p>下例创建两个触发器以说明延迟名称解析。 </p>
<pre><code>USE pubs
IF EXISTS (SELECT name FROM sysobjects
WHERE name = 'trig1' AND type = 'TR')
DROP TRIGGER trig1
GO
-- Creating a trigger on a nonexistent table.
CREATE TRIGGER trig1
on authors
FOR INSERT, UPDATE, DELETE
AS
SELECT a.au_lname, a.au_fname, x.info
FROM authors a INNER JOIN does_not_exist x
ON a.au_id = x.au_id
GO
-- Here is the statement to actually see the text of the trigger.
SELECT o.id, c.text
FROM sysobjects o INNER JOIN syscomments c
ON o.id = c.id
WHERE o.type = 'TR' and o.name = 'trig1'
-- Creating a trigger on an existing table, but with a nonexistent
-- column.
USE pubs
IF EXISTS (SELECT name FROM sysobjects
WHERE name = 'trig2' AND type = 'TR')
DROP TRIGGER trig2
GO
CREATE TRIGGER trig2
ON authors
FOR INSERT, UPDATE
AS
DECLARE @fax varchar(12)
SELECT @fax = phone
FROM authors
GO
-- Here is the statement to actually see the text of the trigger.
SELECT o.id, c.text
FROM sysobjects o INNER JOIN syscomments c
ON o.id = c.id
WHERE o.type = 'TR' and o.name = 'trig2'
</code></pre>
<h6>E. 使用 COLUMNS_UPDATED</h6>
<p>下例创建两个表：一个 <strong>employeeData </strong>表和一个 <strong>auditEmployeeData </strong>表。人力资源部的成员可以修改 <strong>employeeData </strong>表，该表包含敏感的雇员薪水信息。如果更改了雇员的社会保险号码 (SSN)、年薪或银行帐户，则生成审核记录并插入到 <strong>auditEmployeeData </strong>审核表。</p>
<p>通过使用 COLUMNS_UPDATED() 功能，可以快速测试对这些包含敏感雇员信息的列所做的更改。只有在试图检测对表中的前 8 列所做的更改时，COLUMNS_UPDATED() 才起作用。</p>
<pre><code>USE pubs
IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'employeeData')
DROP TABLE employeeData
IF EXISTS(SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'auditEmployeeData')
DROP TABLE auditEmployeeData
GO
CREATE TABLE employeeData (
emp_id int NOT NULL,
emp_bankAccountNumber char (10) NOT NULL,
emp_salary int NOT NULL,
emp_SSN char (11) NOT NULL,
emp_lname nchar (32) NOT NULL,
emp_fname nchar (32) NOT NULL,
emp_manager int NOT NULL
)
GO
CREATE TABLE auditEmployeeData (
audit_log_id uniqueidentifier DEFAULT NEWID(),
audit_log_type char (3) NOT NULL,
audit_emp_id int NOT NULL,
audit_emp_bankAccountNumber char (10) NULL,
audit_emp_salary int NULL,
audit_emp_SSN char (11) NULL,
audit_user sysname DEFAULT SUSER_SNAME(),
audit_changed datetime DEFAULT GETDATE()
)
GO
CREATE TRIGGER updEmployeeData
ON employeeData
FOR update AS
/*Check whether columns 2, 3 or 4 has been updated. If any or all of columns 2, 3 or 4 have been changed, create an audit record. The bitmask is: power(2,(2-1))+power(2,(3-1))+power(2,(4-1)) = 14. To check if all columns 2, 3, and 4 are updated, use = 14 in place of &gt;0 (below).*/
IF (COLUMNS_UPDATED() &amp; 14) &gt; 0
/*Use IF (COLUMNS_UPDATED() &amp; 14) = 14 to see if all of columns 2, 3, and 4 are updated.*/
BEGIN
-- Audit OLD record.
INSERT INTO auditEmployeeData
(audit_log_type,
audit_emp_id,
audit_emp_bankAccountNumber,
audit_emp_salary,
audit_emp_SSN)
SELECT 'OLD',
del.emp_id,
del.emp_bankAccountNumber,
del.emp_salary,
del.emp_SSN
FROM deleted del
-- Audit NEW record.
INSERT INTO auditEmployeeData
(audit_log_type,
audit_emp_id,
audit_emp_bankAccountNumber,
audit_emp_salary,
audit_emp_SSN)
SELECT 'NEW',
ins.emp_id,
ins.emp_bankAccountNumber,
ins.emp_salary,
ins.emp_SSN
FROM inserted ins
END
GO
/*Inserting a new employee does not cause the UPDATE trigger to fire.*/
INSERT INTO employeeData
VALUES ( 101, 'USA-987-01', 23000, 'R-M53550M', N'Mendel', N'Roland', 32)
GO
/*Updating the employee record for employee number 101 to change the salary to 51000 causes the UPDATE trigger to fire and an audit trail to be produced.*/
UPDATE employeeData
SET emp_salary = 51000
WHERE emp_id = 101
GO
SELECT * FROM auditEmployeeData
GO
/*Updating the employee record for employee number 101 to change both the bank account number and social security number (SSN) causes the UPDATE trigger to fire and an audit trail to be produced.*/
UPDATE employeeData
SET emp_bankAccountNumber = '133146A0', emp_SSN = 'R-M53550M'
</code>   <code>WHERE emp_id = 101</code>
<code>GO</code>
<code>SELECT * FROM auditEmployeeData</code>
<code>GO</code>
</pre>
<h6>F. 使用 COLUMNS_UPDATED 测试 8 列以上</h6>
<p>如果必须测试影响到表中前 8 列以外的列的更新时，必须使用 UBSTRING 函数测试由 COLUMNS_UPDATED 返回的适当的位。下例测试影响 <strong>Northwind.dbo.Customers</strong> 表中的第 3、第 5 或第 9 列的更新。</p>
<pre><code>USE Northwind
DROP TRIGGER  tr1
GO
CREATE TRIGGER tr1 ON Customers
FOR UPDATE AS
IF ( (SUBSTRING(COLUMNS_UPDATED(),1,1)=power(2,(3-1))
+ power(2,(5-1)))
AND (SUBSTRING(COLUMNS_UPDATED(),2,1)=power(2,(1-1)))
)
PRINT 'Columns 3, 5 and 9 updated'
GO
UPDATE Customers
SET ContactName=ContactName,
Address=Address,
Country=Country
GO</code></pre>
</div>
<img src ="http://www.blogjava.net/xixidabao/aggbug/165947.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2007-12-07 09:13 <a href="http://www.blogjava.net/xixidabao/archive/2007/12/07/165947.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>标准SQL参考</title><link>http://www.blogjava.net/xixidabao/archive/2007/07/23/131875.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Mon, 23 Jul 2007 06:41:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2007/07/23/131875.html</guid><description><![CDATA[<p>一、 简单查询 <br>　　简单的Transact-SQL查询只包括选择列表、FROM子句和WHERE子句。它们分别说明所查询列、查询的表或视图、以及搜索条件等。<br>　　例如，下面的语句查询testtable表中姓名为"张三"的nickname字段和email字段。
<p>&nbsp;　　SELECT nickname,email<br>　　FROM testtable<br>　　WHERE name='张三'
<p>　　(一) 选择列表
<p>　　选择列表(select_list)指出所查询列，它可以是一组列名列表、星号、表达式、变量(包括局部变量和全局变量)等构成。
<p>　　1、选择所有列
<p>　　例如，下面语句显示testtable表中所有列的数据：
<p>&nbsp;　　SELECT *<br>　　FROM testtable
<p>　　2、选择部分列并指定它们的显示次序
<p>　　查询结果集合中数据的排列顺序与选择列表中所指定的列名排列顺序相同。<br>　　例如：
<p>&nbsp;　　SELECT nickname,email<br>　　FROM testtable
<p>　　3、更改列标题
<p>　　在选择列表中，可重新指定列标题。定义格式为：<br>　　列标题=列名<br>　　列名 列标题<br>　　如果指定的列标题不是标准的标识符格式时，应使用引号定界符，例如，下列语句使用汉字显示列标题：
<p>&nbsp;　　SELECT 昵称=nickname,电子邮件=email<br>　　FROM testtable
<p>　　4、删除重复行
<p>　　SELECT语句中使用ALL或DISTINCT选项来显示表中符合条件的所有行或删除其中重复的数据行，默认为ALL。使用DISTINCT选项时，对于所有重复的数据行在SELECT返回的结果集合中只保留一行。
<p>　　5、限制返回的行数
<p>　　使用TOP n [PERCENT]选项限制返回的数据行数，TOP n说明返回n行，而TOP n PERCENT时，说明n是表示一百分数，指定返回的行数等于总行数的百分之几。<br>　　例如：
<p>&nbsp;　 SELECT TOP 2 *<br>　　FROM testtable<br>　　SELECT TOP 20 PERCENT *<br>　　FROM testtable
<p>　　(二)FROM子句
<p>　　FROM子句指定SELECT语句查询及与查询相关的表或视图。在FROM子句中最多可指定256个表或视图，它们之间用逗号分隔。<br>　　在FROM子句同时指定多个表或视图时，如果选择列表中存在同名列，这时应使用对象名限定这些列所属的表或视图。例如在usertable和citytable表中同时存在cityid列，在查询两个表中的cityid时应使用下面语句格式加以限定：
<p>&nbsp;　&nbsp; SELECT username,citytable.cityid<br>　　FROM usertable,citytable<br>　　WHERE usertable.cityid=citytable.cityid
<p>　　在FROM子句中可用以下两种格式为表或视图指定别名：<br>　　表名 as 别名<br>　　表名 别名
<p>　　(二) FROM子句
<p>　　FROM子句指定SELECT语句查询及与查询相关的表或视图。在FROM子句中最多可指定256个表或视图，它们之间用逗号分隔。<br>　　在FROM子句同时指定多个表或视图时，如果选择列表中存在同名列，这时应使用对象名限定这些列所属的表或视图。例如在usertable和citytable表中同时存在cityid列，在查询两个表中的cityid时应使用下面语句格式加以限定：
<p>&nbsp;　 SELECT username,citytable.cityid<br>　　FROM usertable,citytable<br>　　WHERE usertable.cityid=citytable.cityid
<p>　　在FROM子句中可用以下两种格式为表或视图指定别名：<br>　　表名 as 别名<br>　　表名 别名<br>　　例如上面语句可用表的别名格式表示为：
<p>&nbsp;　 SELECT username,b.cityid<br>　　FROM usertable a,citytable b<br>　　WHERE a.cityid=b.cityid
<p>　　SELECT不仅能从表或视图中检索数据，它还能够从其它查询语句所返回的结果集合中查询数据。
<p>　　例如：
<p>&nbsp;　&nbsp; SELECT a.au_fname+a.au_lname<br>　　FROM authors a,titleauthor ta<br>　　(SELECT title_id,title<br>　　FROM titles<br>　　WHERE ytd_sales&gt;10000<br>　　) AS t<br>　　WHERE a.au_id=ta.au_id<br>　　AND ta.title_id=t.title_id
<p>　　此例中，将SELECT返回的结果集合给予一别名t，然后再从中检索数据。
<p>(三) 使用WHERE子句设置查询条件
<p>　　WHERE子句设置查询条件，过滤掉不需要的数据行。例如下面语句查询年龄大于20的数据：
<p>&nbsp;　 SELECT *<br>　　FROM usertable<br>　　WHERE age&gt;20
<p>　　WHERE子句可包括各种条件运算符：<br>　　比较运算符(大小比较)：&gt;、&gt;=、=、&lt;、&lt;=、&lt;&gt;、!&gt;、!&lt;<br>　　范围运算符(表达式值是否在指定的范围)：BETWEEN...AND...<br>　　NOT BETWEEN...AND...<br>　　列表运算符(判断表达式是否为列表中的指定项)：IN (项1,项2......)<br>　　NOT IN (项1,项2......)<br>　　模式匹配符(判断值是否与指定的字符通配格式相符):LIKE、NOT LIKE<br>　　空值判断符(判断表达式是否为空)：IS NULL、NOT IS NULL<br>　　逻辑运算符(用于多条件的逻辑连接)：NOT、AND、OR
<p>　　1、范围运算符例：age BETWEEN 10 AND 30相当于age&gt;=10 AND age&lt;=30<br>　　2、列表运算符例：country IN ('Germany','China')<br>　　3、模式匹配符例：常用于模糊查找，它判断列值是否与指定的字符串格式相匹配。可用于char、varchar、text、ntext、datetime和smalldatetime等类型查询。<br>　　可使用以下通配字符：<br>　　百分号%：可匹配任意类型和长度的字符，如果是中文，请使用两个百分号即%%。<br>　　下划线_：匹配单个任意字符，它常用来限制表达式的字符长度。<br>　　方括号[]：指定一个字符、字符串或范围，要求所匹配对象为它们中的任一个。[^]：其取值也[] 相同，但它要求所匹配对象为指定字符以外的任一个字符。<br>　　例如：<br>　　限制以Publishing结尾，使用LIKE '%Publishing'<br>　　限制以A开头：LIKE '[A]%'<br>　　限制以A开头外：LIKE '[^A]%'
<p>　　4、空值判断符例WHERE age IS NULL
<p>　　5、逻辑运算符：优先级为NOT、AND、OR
<p>　　(四)查询结果排序
<p>　　使用ORDER BY子句对查询返回的结果按一列或多列排序。ORDER BY子句的语法格式为：<br>　　ORDER BY {column_name [ASC|DESC]} [,...n]<br>　　其中ASC表示升序，为默认值，DESC为降序。ORDER BY不能按ntext、text和image数据类型进行排<br>　　序。<br>　　例如：
<p>&nbsp;　&nbsp; SELECT *<br>　　FROM usertable<br>　　ORDER BY age desc,userid ASC
<p>　　另外，可以根据表达式进行排序。
<p>　　二、 联合查询
<p>　　UNION运算符可以将两个或两个以上上SELECT语句的查询结果集合合并成一个结果集合显示，即执行联合查询。UNION的语法格式为：
<p>&nbsp;　&nbsp; select_statement<br>　　UNION [ALL] selectstatement<br>　　[UNION [ALL] selectstatement][...n]
<p>　　其中selectstatement为待联合的SELECT查询语句。
<p>　　ALL选项表示将所有行合并到结果集合中。不指定该项时，被联合查询结果集合中的重复行将只保留一行。
<p>　　联合查询时，查询结果的列标题为第一个查询语句的列标题。因此，要定义列标题必须在第一个查询语句中定义。要对联合查询结果排序时，也必须使用第一查询语句中的列名、列标题或者列序号。
<p>　　在使用UNION 运算符时，应保证每个联合查询语句的选择列表中有相同数量的表达式，并且每个查询选择表达式应具有相同的数据类型，或是可以自动将它们转换为相同的数据类型。在自动转换时，对于数值类型，系统将低精度的数据类型转换为高精度的数据类型。
<p>　　在包括多个查询的UNION语句中，其执行顺序是自左至右，使用括号可以改变这一执行顺序。例如：
<p>　　查询1 UNION (查询2 UNION 查询3)
<p>　　三、连接查询
<p>　　通过连接运算符可以实现多个表查询。连接是关系数据库模型的主要特点，也是它区别于其它类型数据库管理系统的一个标志。
<p>　　在关系数据库管理系统中，表建立时各数据之间的关系不必确定，常把一个实体的所有信息存放在一个表中。当检索数据时，通过连接操作查询出存放在多个表中的不同实体的信息。连接操作给用户带来很大的灵活性，他们可以在任何时候增加新的数据类型。为不同实体创建新的表，尔后通过连接进行查询。
<p>　　连接可以在SELECT 语句的FROM子句或WHERE子句中建立，似是而非在FROM子句中指出连接时有助于将连接操作与WHERE子句中的搜索条件区分开来。所以，在Transact-SQL中推荐使用这种方法。
<p>　　SQL-92标准所定义的FROM子句的连接语法格式为：
<p>&nbsp;　　FROM join_table join_type join_table<br>　　[ON (join_condition)]
<p>　　其中join_table指出参与连接操作的表名，连接可以对同一个表操作，也可以对多表操作，对同一个表操作的连接又称做自连接。
<p>　　join_type 指出连接类型，可分为三种：内连接、外连接和交叉连接。内连接(INNER JOIN)使用比较运算符进行表间某(些)列数据的比较操作，并列出这些表中与连接条件相匹配的数据行。根据所使用的比较方式不同，内连接又分为等值连接、自然连接和不等连接三种。外连接分为左外连接(LEFT OUTER JOIN或LEFT JOIN)、右外连接(RIGHT OUTER JOIN或RIGHT JOIN)和全外连接(FULL OUTER JOIN或FULL JOIN)三种。与内连接不同的是，外连接不只列出与连接条件相匹配的行，而是列出左表(左外连接时)、右表(右外连接时)或两个表(全外连接时)中所有符合搜索条件的数据行。
<p>　　交叉连接(CROSS JOIN)没有WHERE 子句，它返回连接表中所有数据行的笛卡尔积，其结果集合中的数据行数等于第一个表中符合查询条件的数据行数乘以第二个表中符合查询条件的数据行数。
<p>　　连接操作中的ON (join_condition) 子句指出连接条件，它由被连接表中的列和比较运算符、逻辑运算符等构成。
<p>　　无论哪种连接都不能对text、ntext和image数据类型列进行直接连接，但可以对这三种列进行间接连接。例如：
<p>&nbsp;　　SELECT p1.pub_id,p2.pub_id,p1.pr_info<br>　　FROM pub_info AS p1 INNER JOIN pub_info AS p2<br>　　ON DATALENGTH(p1.pr_info)=DATALENGTH(p2.pr_info)
<p>　　(一)内连接<br>　　内连接查询操作列出与连接条件匹配的数据行，它使用比较运算符比较被连接列的列值。内连接分三种：<br>　　1、等值连接：在连接条件中使用等于号(=)运算符比较被连接列的列值，其查询结果中列出被连接表中的所有列，包括其中的重复列。<br>　　2、不等连接： 在连接条件使用除等于运算符以外的其它比较运算符比较被连接的列的列值。这些运算符包括&gt;、&gt;=、&lt;=、&lt;、!&gt;、!&lt;和&lt;&gt;。<br>　　3、自然连接：在连接条件中使用等于(=)运算符比较被连接列的列值，但它使用选择列表指出查询结果集合中所包括的列，并删除连接表中的重复列。<br>　　例，下面使用等值连接列出authors和publishers表中位于同一城市的作者和出版社：
<p>&nbsp;　 SELECT *<br>　　FROM authors AS a INNER JOIN publishers AS p<br>　　ON a.city=p.city<br>　　又如使用自然连接，在选择列表中删除authors 和publishers 表中重复列(city和state)：<br>　　SELECT a.*,p.pub_id,p.pub_name,p.country<br>　　FROM authors AS a INNER JOIN publishers AS p<br>　　ON a.city=p.city
<p>　　(二)外连接<br>　　内连接时，返回查询结果集合中的仅是符合查询条件( WHERE 搜索条件或 HAVING 条件)和连接条件的行。而采用外连接时，它返回到查询结果集合中的不仅包含符合连接条件的行，而且还包括左表(左外连接时)、右表(右外连接时)或两个边接表(全外连接)中的所有数据行。如下面使用左外连接将论坛内容和作者信息连接起来：
<p>&nbsp;　 SELECT a.*,b.* FROM luntan LEFT JOIN usertable as b<br>　　ON a.username=b.username
<p>　　下面使用全外连接将city表中的所有作者以及user表中的所有作者，以及他们所在的城市：
<p>&nbsp;　&nbsp; SELECT a.*,b.*<br>　　FROM city as a FULL OUTER JOIN user as b<br>　　ON a.username=b.username
<p>　　(三)交叉连接<br>　　交叉连接不带WHERE 子句，它返回被连接的两个表所有数据行的笛卡尔积，返回到结果集合中的数据行数等于第一个表中符合查询条件的数据行数乘以第二个表中符合查询条件的数据行数。例，titles表中有6类图书，而publishers表中有8家出版社，则下列交叉连接检索到的记录数将等于6*8=48行。<br>&nbsp;&nbsp; SELECT type,pub_name<br>　　FROM titles CROSS JOIN publishers<br>　　ORDER BY type </p>
<img src ="http://www.blogjava.net/xixidabao/aggbug/131875.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2007-07-23 14:41 <a href="http://www.blogjava.net/xixidabao/archive/2007/07/23/131875.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>