publisher进入的时候提示账户中无法找到favicon ico产品不能用于激活怎么回事啊!

charvarchar18000
char字符数据,速度快。
varchar字符数据,。
textUnicode2^31-1(2,147,483,647)
ncharnvarcharntext UnicodeUnicodecharvarcharncharnvarchar4000charvarchar800040001ncharnvarchar
bigint-2^63(-4775808)2^63-1(4775807)
int-2^31(-2,147,483,648)2^31-1(2,147,483,647)
smallint-2^15(-32,768)2^15-1(32,767)
tinyint0255
float-1.79^3081.79^308
real-3.40^383.40^38SQL Serverrealfloat(24)
2.SQL Server
select @@version
select convert(varchar(30),login_time,120) from master..sysprocesses where spid=1
print 'Server Name...............' + convert(varchar(30),@@SERVERNAME)
print 'Instance..................' + convert(varchar(30),@@SERVICENAME)
sp_helprotect
sp_helpremotelogin
use @database_name
sp_stored_procedures
sp_helptext [url=mailto:'@procedure_name']'@procedure_name'[/url]
SQL Server
sp_who 'active'
1--50SQL Server,50.
spid,dbid,objid
dbcc sqlperf(logspace)
@database_name_logM
backup log @database_name with no_log
dbcc shrinkfile (@database_name_log, 5)
select distinct * from tableName
select distinct * into #Tmp from tableName
drop table tableName
select * into tableName from #Tmp
drop table #Tmp
& 的记录,比如Name
Name,Address
select identity(int,1,1) as autoID, * into #Tmp from tableName& //identity(int,1,1) autoID
select min(autoID) as autoID into #Tmp2 from #Tmp group by Name,Address
select * from #Tmp where autoID in(select autoID from #tmp2)
selectNameAddressautoIDselect
4.& Sql Server
1SQLMicrosoft SQL Server
2SQL Server--&--&
3Forum--&--&
1SQLMicrosoft SQL Server
2SQL Server--&--&
3Forum--&--&
4--&--&--&--&1--&
5SQLSQLD:\Program Files\Microsoft SQL Server\MSSQL\Databbs_data.mdfforumforum_data.mdf*_log.ldfd:\sqldata\bbs_data.mdfd:\sqldata\bbs_log.ldf
6SQLSQLSQL
1SQLMicrosoft SQL Server--&SQL Server--&--&--&Forum--&--&--&
1Microsoft SQL Server--&SQL Server--&
5Dd:\databak47bak
6--&--&--&
7Sql Server AgentSQLSql Server AgentOS
1Microsoft SQL Server--&SQL Server--&--&--&--&
& char(size):sizeChar255
& varchar(size):size
& number(size):size
& number(size,d):sized
SELECTSQLDISTINCT:
SELECT DISTINCT "column_name" FROM "table_name"
1GROUP BYSQL
SELECT store_name, SUM(Sales) FROM Store_Information GROUP BY store_name
store_name SUM(Sales)
Los Angeles 00
San Diego&& 0
Boston&&&&& 0
2SQLHAVINGWHEREHAVINGSQL
SELECT store_name, SUM(sales)
FROM Store_Information
GROUP BY store_name
HAVING SUM(sales) & 1500
store_name SUM(Sales)
Los Angeles 2000
SQL ServerNTsp_changedbownerSA
sp_changedbowner@mapnull
sp_helpuser
CREATE DATABASE test
EXEC sp_helpuser
db_ownerWindows NTNULLSID
ISUser1ISUser2db_owner
EXEC sp_addlogin @loginame = 'ISUser1', @passwd = 'ISUser1',@defdb = 'master'
EXEC sp_addlogin @loginame = 'ISUser2', @passwd = 'ISUser2',@defdb = 'master'
EXEC sp_addalias @loginame = 'ISUser1', @name_in_db = 'dbo'
EXEC sp_changedbowner @loginame = 'sa', @map = 'TRUE'
EXEC sp_helpuser
db_ownerISUser1db_owner
Microsoft? SQL Server? 2000
SQL Server
#employees
##employees
SQL Server
8. IDENTITY
ROWGUIDCOL
ROWGUIDCOL
9.SQL Server 2005 Compact Edition
Microsoft SQL Server 2005 Compact Edition (SQL Server Compact Edition)
SQL Server Compact Edition
SQL Server Compact Edition
SQL Server Compact
System.IO.File.Delete
SQL Server Compact Edition
File.Delete("Test.sdf");
SQL Server Compact Edition
System.IO.File.Delete("Test.sdf");
string connString = "Data Source='Test.sdf'; LCID=1033; Password=\"s$;2'!dS64\"; Encrypt = TRUE;";
SqlCeEngine engine = new SqlCeEngine(connString);
engine.CreateDatabase();
----------------------
select sum(data1)
from (select substr('1101', rownum, 1) * power
(2, length('1101') - rownum) data1
connect by rownum
----------------------
select sum(data1)
from (select substr('1101', rownum, 1) * power
(8, length('1101') - rownum) data1
connect by rownum
----------------------
select sum(data1)
from (select (CASE upper(substr('2D', rownum, 1))
WHEN 'A' THEN '10'
WHEN 'B' THEN '11'
WHEN 'C' THEN '12'
WHEN 'D' THEN '13'
WHEN 'E' THEN '14'
WHEN 'F' THEN '15'
ELSE substr('2D', rownum, 1)
END) * power(16, length('2D') - rownum) data1
connect by rownum
SQL :CREATE INDEX mycolumn_index ON mytable (myclumn)
SQL DROP INDEX mytable.mycolumn_index
(CLUSTERED)
CREATE CLUSTERED INDEX mycolumn_clust_index ON mytable(mycolumn)
;ALLOW_DUP_ROWSQL Sever
CREATE CLUSTERED INDEX mycolumn_cindex ON mytable(mycolumn) WITH ALLOW_DUP_ROW
CREATE UNIQUE COUSTERED INDEX myclumn_cindex ON mytable(mycolumn)
DISTINCTROW
TOPORDER BY
SELECT TOP 25 FORM
&&&& &&&&WHERE =1994ORDER BY
ORDER BY TOPPERCENT
SELECT TOP 10 PERCENTFROM
&&&& WHERE=1994ORDER BYDESC
PartA....FROM Table
IN ""[dBASE IV;DATABASE=C:\DBASE\DATA\SALES;];
PartB....FROM Table
IN "C:\DBASE\DATA\SALES" "dBASE IV;"
HAVINGWHERE HAVINGGROUP
SELECT ,Sum()
HAVING Sum()&100 AND
SELECT...INTO
&&&&&& SELECT field1[,field2[,...]]INTO newtable[IN externaldatabase]
FROM source
INNER JOIN
&&&&&& SELECT fields
FROM table1 INNER JOIN table2
ON table1.field1 compopr table2.field2
UNIONUNION
[TABLE]query1 UNION [ALL][TABLE]query2 [UNION [ALL]
[TABLE]queryn [...]]
query1,query2,queryn
SQL1000UNION
UNION ALL ALL
WHERE &1000
DROP {TABLE table|INDEX index ON table}
DROP INDEX MyIndex ON E
datetimeinterval
datetimedatetime
DatetimeDATATIMETIMESTAMPDATA
DATA YYYY-MM-DDYMD
hh:mm:sshms
……
Universal Coordinated TimeUTC00:00:00.
TIMEhh:mm:ss.p
TIME WITH TIME ZONE
TIME () WITH TIME ZONE
TIMEZONEUTC00:00:00+hh:mm-12:5913:00
TIMEZONETIME14
TIMEZONETIME
YYYY-MM-DD hh:mm:ss.
UNIXYYYY-MM-DD hh:mm:ss.p
TIMESTAMP WITH TIME ZONE
TIMESTAMPWITH TIME ZONE
TIME WITH TIME ZONEUTC-12:59 +13:00
25YYYY-MM-DD hh:mm:ss.p
TIMESTAMP WITH TIME ZONE
interval——intervalSQL92intervalINTERVAL
interval“”YYYY-MM“”DD HH:MM:SS
——intervallead precision——interval“”“”
“”interval“”interval“”
OVERLAPSdatetimedatetimeintervalEXTRACTdatetimeintervalDATA
datetimeintervalOracleSQL
scale value1234.5662NUMERIC(6,2)
NUMERIC(,)
DECIMAL | DEC
DECIMAL | DEC
INTEGER | INT
bitimplementation-specificSMALLINT
DOUBLE PRECISION
DOUBLE PRECISION
DOUBLE PRECISION
在这一步中,需要指定游标的属性和根据要求产生的结果集。有两种方法可以指定一个游标。
形式1(ANSI 92)
DECLARE cursor_name [INSENSITIVE] [SCROLL] CURSOR
FOR select_statement
[FOR {READ ONLY | UPDATE ][OF column_list]}]
DECLARE cursor_name CURSOR
[LOCAL | GLOBAL]
[FORWARD_ONLY | SCROLL]
[STATIC | KEYSET | DYNAMIC]
[READ_ONLY | SCROLL_LOCKS | OPTIMISTIC]
FOR select_statement
[FOR {READ ONLY | UPDATE ][OF column_list]}]
INSENSITIVE关键字指明要为检索到的结果集建立一个临时拷贝,以后的数据从这个临时拷贝中获取。如果在后来游标处理的过程中,原有基表中数据发生了改变,那么它们对于该游标而言是不可见的。这种不敏感的游标不允许数据更改。
SCROLL关键字指明游标可以在任意方向上滚动。所有的fetch选项(first、last、next、relative、absolute)都可以在游标中使用。如果忽略该选项,则游标只能向前滚动(next)。
Select_statement指明SQL语句建立的结果集。Transact SQL语句COMPUTE、COMPUTE BY、FOR BROWSE和INTO在游标声明的选择语句中不允许使用。
READ ONLY指明在游标结果集中不允许进行数据修改。
UPDATE关键字指明游标的结果集可以修改。
OF column_list指明结果集中可以进行修改的列。缺省情况下(使用UPDATE关键字),所有的列都可进行修改。
LOCAL关键字指明游标是局部的,它只能在它所声明的过程中使用。
GLOBAL关键字使得游标对于整个连接全局可见。全局的游标在连接激活的任何时候都是可用的。只有当连接结束时,游标才不再可用。
FORWARD_ONLY指明游标只能向前滚动。
STATIC的游标与INSENSITIVE的游标是相同的。
KEYSET指明选取的行的顺序。SQL Server将从结果集中创建一个临时关键字集。如果对数据库的非关键字列进行了修改,则它们对游标是可见的。因为是固定的关键字集合,所以对关键字列进行修改或新插入列是不可见的。
DYNAMIC指明游标将反映所有对结果集的修改。
SCROLL_LOCK是为了保证游标操作的成功,而对修改或删除加锁。
OPTIMISTIC指明哪些通过游标进行的修改或者删除将不会成功。
· 如果在SELECT语句中使用了DISTINCT、UNION、GROUP BY语句,且在选择中包含了聚合表达式,则游标自动为INSENSITIVE的游标。
· 如果基表没有唯一的索引,则游标创建成INSENSITIVE的游标。
· 如果SELECT语句包含了ORDER BY,而被ORDER BY的列并非唯一的行标识,则DYNAMIC游标将转换成KEYSET游标。如果KEYSET游标不能打开,则将转换成INSENSITIVE游标。使用SQL ANSI-92语法定义的游标同样如此,只是没有INSENSITIVE关键字而已。
ii. 打开游标
打开游标就是创建结果集。游标通过DECLARE语句定义,但其实际的执行是通过OPEN语句。语法如下:
OPEN { { [GLOBAL] cursor_name } | cursor_variable_name}
GLOBAL指明一个全局游标。
Cursor_name是被打开的游标的名称。
Cursor_variable_name是所引用游标的变量名。该变量应该为游标类型。
在游标被打开之后,系统变量@@cursor_rows可以用来检测结果集的行数。@@cursor_rows为负数时,表示游标正在被异步迁移,其绝对值(如果@@cursor_rows为-5,则绝对值为5)为当前结果集的行数。异步游标使用户在游标被完全迁移时仍然能够访问游标的结果。
iii. 从游标中取值
在从游标中取值的过程中,可以在结果集中的每一行上来回移动和处理。如果游标定义成了可滚动的(在声明时使用SCROLL关键字),则任何时候都可取出结果集中的任意行。对于非滚动的游标,只能对当前行的下一行实施取操作。结果集可以取到局部变量中。Fetch命令的语法如下:
FETCH [NEXT | PRIOR| FIRST | LAST | ABSOLUTE {n | @nvar} | RELATIVE {n | @nvar}]
FROM [GLOBAL] cursor_name} | cursor_variable_name}
[INTO @variable_name ][,……n]]
NEXT指明从当前行的下一行取值。
PRIOR指明从当前行的前一行取值。
FIRST是结果集的第一行。
LAST是结果集的最后一行。
ABSOLUTE n表示结果集中的第n行,该行数同样可以通过一个局部变量传播。行号从0开始,所以n为0时不能得到任何行。
RELATIVE n表示要取出的行在当前行的前n行或后n行的位置上。如果该值为正数,则要取出的行在当前行前n行的位置上,如果该值为负数,则返回当前行的后n行。
INTO @cursor_variable_name表示游标列值存储的地方的变量列表。该列表中的变量数应该与DECLARE语句中选择语句所使用的变量数相同。变量的数据类型也应该与被选择列的数据类型相同。直到下一次使用FETCH语句之前,变量中的值都会一直保持。
每一次FETCH的执行都存储在系统变量@@fetch_status中。如果FETCH成功,则@@fetch_status被设置成0。@@fetch_status为-1表示已经到达了结果集的一部分(例如,在游标被打开之后,基表中的行被删除)。@@fetch_status可以用来构造游标处理的循环。
DECLARE @iname char(20), @fname char(20)
OPEN author_cur
FETCH FIRST FROM author_cur INTO @iname, @fname
WHILE @@fetch_status = 0
IF @fname = ‘Albert’
PRINT “Found Albert Ringer”
Print “Other Ringer”
FETCH NEXT FROM author_cur INTO @iname, @fname
iv. 关闭游标
CLOSE语句用来关闭游标并释放结果集。游标关闭之后,不能再执行FETCH操作。如果还需要使用FETCH语句,则要重新打开游标。语法如下:
CLOSE [GLOBAL] cursor_name | cursor_variable_name
v. 释放游标
游标使用不再需要之后,要释放游标。DEALLOCATE语句释放数据结构和游标所加的锁。语法如下:
DEALLOCATE [GLOBAL] cursor_name | cursor_variable_name
下面给出游标的一个完整的例子:
USE master
CREATE PROCEDURE sp_BuildIndexes
DECLARE @TableName sysname, @msg varchar(100), @cmd varchar(100)
DECLARE table_cur CURSOR FOR
SELECT name FROM sysobjects WHERE type=’u’
OPEN table_cur
FETCH NEXT FROM table_cur INTO @TableName
WHILE @@fetch_status = 0
IF @@fetch_status = -2
SELECT @msg = “Building indexes for +”…”
PRINT @msg
SELECT @cmd = “DBCC DBREINDEX ()”
EXEC (@cmd)
PRINT “ “
FETCH NEXT FROM table_cur INTO @TableName
DEALLOCATE table_cur
下面的脚本将为PUBS数据库执行sp_BuildIndexes
EXEC ap_BuildIndexes
注意:上面也是创建用户定义的系统存储过程的示例。
使用临时表
临时表是在TempDB中创建的表。临时表的名称都以“#”开头。临时表的范围为创建临时表的连接。因为,临时表不能在两个连接之间共享,一旦连接关闭,临时表就会被丢弃。如果临时表被创建于存储过程之中,则临时表的范围在存储过程之中,或者被该存储过程调用的任何存储过程之中。如果需要在连接之间共享临时表,则需要使用全局的临时表。全局的临时表以“##”符号开头,它将一直存在于数据库中,直到SQL Server重新启动。一旦这类临时表创建之后,所有的用户都可以访问到。在临时表上不能明确地指明权限。 临时表提供了存储中间结果的能力。有时候,临时表还能通过将一个复杂的查询分解成两个查询而获得性能的改善。这可以通过首先将第一个查询的结果存在临时表中,然后在第二个查询中使用临时表来实现。当一个大表中的某个子集在一个在座过程中使用多次时,建议使用临时表。在这种情况下,在临时表中保持数据的子集,以在随后的连接中使用,这样能大大改善性能。还可以在临时表中创建索引。
存储过程入门与提高
什么是存储过程呢?
&&&&& 将常用的或很复杂的工作,预先用SQL语句写好并用一个指定的名称存储起来, 那么以后要叫提供与已定义好的存储过程的功能相同的服务时,只需调用execute,即可自动完成命令。
讲到这里,可能有人要问:这么说存储过程就是一堆SQL语句而已啊?
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& Microsoft公司为什么还要添加这个技术呢?
那么存储过程与一般的SQL语句有什么区别呢?
存储过程的优点:
&&&&&&&&&&&&&&&&&&&&&& 1.存储过程只在创造时进行编译,以后每次执行存储过程都不需再重新编译,而一般SQL语句每执行一次就编译一次,所以使用存储过程可提高数据库执行速度。
&&&&&&&&&&&&&&&&&&&&&&& 2.当对数据库进行复杂操作时(如对多个表进行Update,Insert,Query,Delete时),可将此复杂操作用存储过程封装起来与数据库提供的事务处理结合一起使用。
&&&&&&&&&&&&&&&&&&&&&& 3.存储过程可以重复使用,可减少数据库开发人员的工作量
&&&&&&&&&&&&&&&&&&&&&& 4.安全性高,可设定只有某此用户才具有对指定存储过程的使用权
存储过程的种类:
&&& 1.系统存储过程:以sp_开头,用来进行系统的各项设定.取得信息.相关管理工作,
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 如 sp_help就是取得指定对象的相关信息
&& 2.扩展存储过程&& 以XP_开头,用来调用提供的功能
&&&&&&&&&&&&&&&&&&&&&&&&&&&&& exec master..xp_cmdshell 'ping <st1:chsdate w:st="on" isrocdate="False" islunardate="False" day="30" month="12" year=".16.1'
&& 3.用户自定义的存储过程,这是我们所指的存储过程
&& 常用格式
&& Create procedure procedue_name
&& [@parameter data_type][output]
&& [with]{recompile|encryption}
&&&&&&& sql_statement
output:表示此参数是可传回的
with {recompile|encryption}
recompile:表示每次执行此存储过程时都重新编译一次
encryption:所创建的存储过程的内容会被加密
&& 表book的内容如下
&& 编号&&& 书名&&&&&&&&&&&&&&&&&&&&&&&&&& 价格
&& 001&&&&& C语言入门&&&&&&&&&&&&&&&&&& $30
&& 002&&&&& PowerBuilder报表开发& $52
&实例1:查询表Book的内容的存储过程
&& create proc query_book
&&&&& select * from book
&& exec query_book
&实例2:加入一笔记录到表book,并查询此表中所有书籍的总金额
&& Create proc insert_book
&& @param1 char(10),@param2 varchar(20),@param3 money,@param4 money output
&& with encryption& ---------加密
&& insert book(编号,书名,价格) Values(@param1,@param2,@param3)
&& select @param4=sum(价格) from book
& 执行例子:
& declare @total_price money
& exec insert_book '003','Delphi 控件开发指南',$100,@total_price
& print '总金额为'+convert(varchar,@total_price)
存储过程的3种传回值:
&& 1.以Return传回整数
&& 2.以output格式传回参数
&& 3.Recordset
传回值的区别:
&&&&&& output和return都可在批次程式中用变量接收,而recordset则传回到执行批次的客户端中&
实例3:设有两个表为Product,Order,其表内容如下:
&&&&& Product
&&&&&&&&&& 产品编号&&&&&& 产品名称&&& 客户订数&&&&
&&&&&&&&&&& 001&&&&&&&&&&&& 钢笔&&&&&&&& 30&&&&&&&&
&&&&&&&&&&& 002&&&&&&&&&&&& 毛笔&&&&&&&& 50&&&&&&&&
&&&&&&&&&&& 003&&&&&&&&&&&& 铅笔&&&&&&&& 100&&&&&&&
&&&&& Order&
&&&&&&&&&& 产品编号&&&&&&&& 客户名&&&& 客户订金
&&&&&&&&&&& 001&&&&&&&&&&&&& 南山区&&&&& $30
&&&&&&&&&&& 002&&&&&&&&&&&&& 罗湖区&&&&& $50
&&&&&&&&&&& 003&&&&&&&&&&&&& 宝安区&&&&& $4
请实现按编号为连接条件,将两个表连接成一个临时表,该表只含编号.产品名.客户名.订金.总金额,
总金额=订金*订数,临时表放在存储过程中
&&&& Create proc temp_sale
&&&&&& select a.产品编号,a.产品名称,b.客户名,b.客户订金,a.客户订数* b.客户订金 as总金额
&&&&&& into #temptable from Product a inner join Order b on a.产品编号=b.产品编号
&&& if& @@error=0
&&&&&& print 'Good'
&&&&&& print 'Fail'
一、创建存储过程
  和数据表一样,在使用之前我们需要创建存储过程,它的简明语法是:
CREATE PROC 存储过程名称
&&& [参数列表(多个以&#8220;,&#8221;分隔)]
CREATE PROC upGetUserName
@intUserId&&&&&&& INT,
@ostrUserName NVARCHAR(20) OUTPUT&&&&&&&&&&&&&&& -- 要输出的参数
&&&&&&& -- 将uName的值赋给 @ostrUserName 变量,即要输出的参数
&&&&&&& SELECT @ostrUserName=uName FROM uUser WHERE uId=@intUserId
其中 CREATE PROC 语句(完整语句为CREATE PROCEDURE)的意思就是告诉,现在需要建立一个存储过程,upGetUserName 就是存储过程名称,@intUserId 和 @ostrUserName 分别是该存储过程的两个参数,注意,在SQL SERVER中,所有用户定义的变量都以&#8220;@&#8221;开头,OUTPUT关键字表示这个参数是用来输出的,AS之后就是存储过程内容了。只要将以上代码在&#8220;查询分析器&#8221;里执行一次,SQL SERVER就会在当前中创建一个名为&#8220;upGetUserName&#8221;的存储过程。你可以打开&#8220;企业管理器&#8221;,选择当前操作的数据库,然后在左边的树型列表中选择&#8220;存储过程&#8221;,此时就可以在右边的列表中看到你刚刚创建的存储过程了(如果没有,刷新一下即可)。
三、存储过程的实际应用
  用户登录在ASP项目中经常会使用到,相信很多朋友也都做过类似的系统,但使用存储过程来做验证朋友可能不多,那么我们就以它来做例子,写一个简单的用户登录验证的存储过程。
CREATE PROC upUserLogin
@strLoginName&&&&&&& NVARCHAR(20),
@strLoginPwd&&&&&&& NVARCHAR(20),
@blnReturn&&&&&&&&&&&&&&& BIT OUTPUT
-- 定义一个临时用来保存密码的变量
DECLARE @strPwd NVARCHAR(20)
&&&&&&& -- 从表中查询当前用户的密码,赋值给 @strPwd 变量,下面要对他进行比较
&&&&&&& SELECT @strPwd=uLoginPwd FROM uUser WHERE uLoginName=@strLoginName
&&&&&&& IF @strLoginPwd = @strPwd
&&&&&&&&&&&&&&& BEGIN
&&&&&&&&&&&&&&&&&&&&&&& SET @blnReturn = 1
&&&&&&&&&&&&&&&&&&&&&&& -- 更新用户最后登录时间
&&&&&&&&&&&&&&&&&&&&&&& UPDATE uUser SET uLastLogin=GETDATE() WHERE uLoginName=@strLoginName
&&&&&&&&&&&&&&& END
&&&&&&& ELSE
&&&&&&&&&&&&&&& SET @blnReturn = 0
一.前言:
存储过程(Stored Procedure)是一组为了完成特定功能的SQL语句集,经编译后存储在中。用户通过指定存储过程的名字并给出参数(如果该存储过程带有参数)来执行它。存储过程是数据库中的一个重要对象,任何一个设计良好的数据库应用程序都应该用到存储过程。
总的来说,存储过程具有以下一些优点:
◆存储过程允许标准组件式编程
◆存储过程能够实现较快的执行速度
◆存储过程能够减少网络流量
◆存储过程可被作为一种机制来充分利用
本文作者将向大家介绍数据库应用程序中存储过程的应用,以及如何将它与ADO.NET中的SqlDataAdapter对象、DataSet对象等结合使用以提高.NET数据库应用程序的总体性能。
二.系统要求:
开发工具:Visual Studio.NET
数据库管理系统: 2000(其中包含了示例程序所用到的Pubs数据库)
三.创建一个简单的存储过程:
这里我将向大家介绍如何运用Visual Studio.NET IDE来创建存储过程。运用Visual Studio.NET IDE创建存储过程是非常容易和直观的,你只要在服务器资源管理器中导向到Pubs数据库并展开节点,就会发现包括存储过程在内的各种数据库对象,如图1所示。
在存储过程节点上点击右键便可弹出一个菜单,其中包含了&#8220;新建存储过程&#8221;的命令。新建一个存储过程后,IDE中的代码编辑窗口便出现如下所示的代码模板:
CREATE PROCEDURE dbo.StoredProcedure1
@parameter1 datatype = default value,
@parameter2 datatype OUTPUT )
/* SET NOCOUNT ON */
上面的代码模板符合简化的创建存储过程的语法规则,完整的语法规则如下:
CREATE PROC [ EDURE ] procedure_name [ ; number ]
[ { @parameter data_type }
[ VARYING ] [ = default ] [ OUTPUT ]
] [ ,...n ]
{ RECOMPILE | ENCRYPTION | RECOMPILE , ENCRYPTION } ]
[ FOR REPLICATION ]
AS sql_statement [ ...n ]
限于篇幅,各个参数的含义在此就不多作介绍了,有兴趣的读者可以参考有关SQL Server 2000数据库管理系统的资料。
下面我对该代码模板中的各个语法成分略作介绍。CREATE PROCEDURE声明创建一个存储过程,后面跟着该存储过程的名称。&#8220;/*&#8230;&#8230;*/&#8221;中的成分是该存储过程的参数,可包括输入参数和输出参数。AS关键字后面的内容是该存储过程的主体部分,其中是任何数量和类型的包含在存储过程中的SQL语句。RETURN关键字表明存储过程结束并能返回整型状态值给调用者。下面我们就来创建一个简单的不带参数的存储过程并运用之:
CREATE PROCEDURE dbo.up_GetPublisherInfo
SELECT pub_id, pub_name, city, state, country
FROM publishers
创建以上存储过程后,保存之。保存完毕,与该存储过程相对应的节点就会出现在服务器资源管理器中。同时请注意代码编辑窗口中的CREATE关键字变为ALTER关键字了,该关键字是用于更改任何现有的存储过程的。要运行上述存储过程,只要点击其节点并在右键弹出菜单中选择&#8220;运行存储过程&#8221;,运行的结果图示如下:
四.创建一个带参数的存储过程:
以上我们创建了一个简单的不带参数的存储过程,而在实际的应用中往往会用到很多带有参数的存储过程。带有参数的存储过程一般是用于更新数据或是插入数据的。下面我们可以运用同样的操作方法创建一个带参数的存储过程:
CREATE PROCEDURE dbo.up_UpdatePublisherInfo
@pub_id char (4),
@pub_name varchar (40),
@city varchar (20),
@state char (2),
@country varchar (30)
UPDATE publishers
SET pub_name = @pub_name, city = @city, state = @state,
 country = @country
WHERE ( pub_id = @pub_id )
在上面的创建存储过程的代码中,我们通过在名称前添加一个&#8220;@&#8221;标志来声明存储过程的局部变量-参数,同时还声明了各个参数的类型,确定了各个参数的方向值,也即表明该参数是输入型的还是输出型的或者是输入输出型的或者是返回值型的。用户通过相应的存储过程名称以及正确有效的参数便可调用该存储过程了。还有,你可以通过运用OUTPUT关键字在参数中添加输出型的参数,具体方法请参考上面的语法规则。输出型的参数能返回给调用者相关的信息。
上面的存储过程能更新publishers表中相应出版商的信息。你可以通过点击该存储过程的节点,在右键弹出菜单中选择&#8220;运行存储过程&#8221;来执行它。一旦执行,IDE中便弹出一个输入出版商信息的对话框(如图3所示)。在该对话框中填入正确有效的更新信息,注意pub_id的值在原来的表中必须存在,然后点击&#8220;确定&#8221;按钮便可更新数据了。
五.创建简单存储过程的数据库应用程序:
下面我们就运用上述的不带参数的存储过程来一个数据库应用程序,其中还用到了ADO.NET中的SqlDataAdapter对象以及DataSet对象。其中的SqlDataAdapter对象作为SQL Server数据库和DataSet对象的桥梁将两者联系在一起。SqlDataAdapter对象包含了两个常用的方法:Fill()方法和Update()方法。其中的Fill()方法能从数据库中获取相应数据并填充到DataSet对象中,而Update()方法顾名思义就是更新数据集的意思了。在调用Fill()方法以前,我们必须设置好SqlDataAdapter对象的SelectCommand属性,该属性其实是一个SqlCommand对象。SelectCommand属性中包含有效的SQL语句,并能据此从数据库中获取相应数据并填充到DataSet对象中。
首先,我们创建一个Windows Forms应用程序,编程语言为C#。在Visual Studio.NET中创建一个新的项目后,给该项目添加一个新的类-Publishers类,该类封装了连接到后台数据库并获取数据集对象的业务逻辑。步骤如下:
1.添加必要的命名空间引用:using System.Data.SqlC
2.给该类添加如下一些必要的变量:
private SqlConnection cnP
private SqlCommand cmdP
private SqlDataAdapter daP
private DataSet dsP
3.在该类的构造函数中完成连接后台数据库,获取SqlDataAdapter对象等业务逻辑:
public Publishers()
// 创建一个对象
cnPubs = new SqlConnection( "server=integrated security=database=pubs" );
// 创建一个SqlCommand对象,并指明其命令类型为存储过程
cmdPubs = new SqlCommand();
cmdPubs.Connection = cnP
mandType = CommandType.StoredP
mandText = "up_GetPublisherInfo";
// 创建一个SqlDataAdapter对象,设定其SelectCommand属性为上面的SqlCommand对象
daPubs = new SqlDataAdapter();
daPubs.SelectCommand = cmdP
// 创建一个DataSet对象
dsPubs = new DataSet();
catch( Exception ) {}
4.最后为该类提供一个GetPublisherInfo()方法,该方法用SqlDataAdapter对象填充DataSet对象并返回填充后的DataSet对象,方法如下(值得注意的是:SqlDataAdapter对象会隐式地打开数据库连接并在获取数据后隐式地关闭连接,这就是说DataSet对象是工作在非连接模式下的。而当你显式地打开数据库连接并获取数据后,SqlDataAdapter对象并不会将该连接关闭):
public DataSet GetPublisherInfo()
// 调用SqlDataAdapter对象的Fill()方法并返回数据集对象
daPubs.Fill( dsPubs );
return dsP
完成Publishers类的设计后,我们给主窗体添加一个DataGrid控件并用它来显示DataSet对象中的数据。首先给主窗体类添加如下成员变量:
private DataS
之后,修改主窗体类的构造函数如下:
public Form1()
// Windows 窗体设计器支持所必需的
InitializeComponent();
// TODO: 在 InitializeComponent 调用后添加任何构造函数代码
// pubs = new Publishers();
ds = pubs.GetPublisherInfo();
dataGrid1.DataSource = ds.Tables[0];
这样该应用程序一启动主窗体的DataGrid控件中便显示了运用上述不带参数的存储过程从Pubs数据库中获取的相应数据,程序运行图示如下:
六.创建带参数的存储过程的数据库应用程序:
上面我们创建了一个不带参数的存储过程的应用程序,下面我们就来创建一个更加复杂的数据库应用程序。在实际的数据库应用中,我们往往需要获取数据并更新、插入或删除数据,这时我们就需要用到带有参数的存储过程了,同时在运用SqlDataAdapter对象时,我们会调用它的Update()方法。该Update()方法会自动根据DataSet对象中的DataTable对象内各条记录的变化情况完成相应操作。SqlDataAdapter对象还包含了UpdateCommand、InsertCommand、DeleteCommand等属性,这些属性其实都是SqlCommand对象。Update()方法会根据操作的类型选用相应的属性。
在运用带有参数的存储过程建立数据库应用程序时,我们一般都要用到SqlParameter类,该类封装了各种与Sql参数相关的属性和方法。其中的属性包括了ParameterName,SqlDBType,Direction,Size,Value,SourceColumn以及SourceVersion等。其中ParameterName,SqlDBType,Direction,Size等属性是用于匹配存储过程中定义的参数的。比如下面定义的SqlParameter对象就是用来匹配前面定义的up_UpdatePublisherInfo存储过程中的&#8220;@pub_id&#8221;参数的。
SqlParameter updParam = new SqlParameter( "@pub_id", SqlDbType.Char, 4 );
在上面的定义中,虽然Direction属性没有明确地给出,但是它的默认值为Input,所以也就满足了我们的需要。而如果一个SqlParameter对象的Direction属性为InputOutput或Output或ReturnValue,那么其Direction属性就必须明确地说明了,比如下面的代码就明确地声明了一个SqlParameter对象的Direction属性为Output。
oParam.Direction = ParameterDirection.O
其中的SourceColumn属性是用于匹配一个DataTable对象中的DataColumn对象的,这种匹配能在调用Update()方法更新DataTable对象时隐式地导入所需的SqlParameter对象。如果在定义时没有声明该属性,那么你必须在代码中显式地说明SqlParameter对象的SourceColumn属性。
其中的SourceVersion属性的默认值是DataRow对象相应字段中的当前值,也就是要更新到数据库中的值。当然,SourceVersion属性也可以指向DataRow对象相应字段中的原始值,也即从数据库中获取的初始值。在数据库事务处理系统中,数据的同步性问题非常重要,下面我们来建立一个能检测数据同步性的存储过程。
CREATE PROCEDURE dbo.up_UpdatePublisherName
@pub_id char(4),
@pub_name varchar(40),
@Original_pub_name varchar(40)
if exists(select pub_id
 from publishers
where (pub_id = @pub_id) AND (pub_name = @Original_pub_name))
 UPDATE publishers SET pub_name = @pub_name
 WHERE (pub_id = @pub_id)
接着,我们在上面的应用程序中调用该存储过程以更新发行商的名称。首先,在原有应用程序的基础上完善其业务逻辑类-Publishers类:
1.添加一个新的SqlCommand对象,该对象能作为SqlDataAdapter对象的 UpdateCommand属性被使用:
private SqlCommand cmdUpdP
2.更新该类的构造函数Publishers()函数,添加以下内容:
// 创建另一个SqlCommand对象,该对象引用更新发行商名称的存储过程
cmdUpdPubs = new SqlCommand();
cmdUpdPubs.Connection = cnP
mandType = CommandType.StoredP
mandText = "up_UpdatePublisherName";
// 为上面的SqlCommand对象添加必要的参数
cmdUpdPubs.Parameters.Add( "@pub_id", SqlDbType.Char, 4, "pub_id" );
cmdUpdPubs.Parameters.Add( "@pub_name", SqlDbType.VarChar, 40, "pub_name" );
SqlParameter updParam = new SqlParameter
( "@Original_pub_name", SqlDbType.VarChar, 40, "pub_name" );
updParam.SourceVersion = DataRowVersion.O
cmdUpdPubs.Parameters.Add( updParam );
3.指定SqlDataAdapter对象的UpdateCommand属性为上面定义的SqlCommand对象:
daPubs.UpdateCommand = cmdUpdP
4.添加方法UpdatePublisherName():
public void UpdatePublisherName( DataSet dsChanges )
// 更新所有改动
daPubs.Update( dsChanges );
应用程序的业务逻辑类完善之后,在主窗体上添加一个名为&#8220;更新数据集&#8221;的按钮,并添加该按钮的事件响应函数如下:
private void button1_Click(object sender, System.EventArgs e) { if( ds.HasChanges() ) { pubs.UpdatePublisherName( ds.GetChanges() ); ds.Clear(); ds = pubs.GetPublisherInfo(); } }&
到此为止,应用程序的业务逻辑类和主窗体类都已经更新完毕,现在的应用程序能根据用户的改用更新数据库中的相关内容了。
七.总结:
本文向大家介绍了存储过程的基本知识以及在.NET数据库应用程序中如何结合SqlDataAdapter对象、DataSet对象等构建数据驱动的应用程序。在本文中,我们运用到了两类存储过程:一类为简单的不带参数的存储过程,其运用方法相对容易;另一类为带有参数的存储过程,在调用该类存储过程时还得运用到SqlParameter对象。同时,我们不难发现将数据更新业务逻辑封装在存储过程中是一种很好的设计方法,它能提高应用程序的可管理性、可扩展性以及数据库的安全性。类似的,插入数据以及删除数据的业务逻辑也可以封装在存储过程中并以相似的方法在应用程序中被运用。最后,希望本文对大家有不少帮助。
SPSPSPSPSPSP
1TableViewView&#8220;databse.dbo.table_name&#8221;sp_dependsSPtableview
2SPset showplan on
i.holdlock
v.where&#8220;=&#8221;
vi.existsselect count(1)countcount(1)count(*)
vii.&#8220;&=&#8221;&#8220;&&#8221;
viii.orunion
xi.insertupdate200400k
ii.index index_name
iii.table scan
i.distinctorder bygroup byhavingjoincumputetempdb
iii.select intocreate tablelogcreate tableinsert
v.truncate tabledrop table
SQLASE TuningSQL,,,ASEset statistics io on, set statistics time on , set showplan on
定义:&何为触发器?在sql&server里面也就是对某一个表的一定的操作,触发某种条件,从而执行的一段程序。触发器是一个特殊的存储过程。&
  常见的触发器有三种:分别应用于insert&,&update&,&delete&事件。(sql&server&2000定义了新的触发器,这里不提)&
  我为什么要使用触发器?比如,这么两个表:&
  用到的功能有:
&&&&& 1.如果我更改了学生的学号,我希望他的借书记录仍然与这个学生相关(也就是同时更改借书记录表的
&&&&&&& 学号);
&&&&&&2.如果该学生已经毕业,我希望删除他的学号的同时,也删除它的借书记录,等等。&
  这时候可以用到触发器。对于1,创建一个update触发器:&
&&&&&&&&& create&trigger&trustudent&on&student&& --在student表中创建触发器&
&&&&&&&&&&for&update&& --为什么事件触发
&&&&&&&&&&as&& --事件触发后所要做的事情
&&&&&&&& &if&update studentid)&
&&&&&&&&&&begin&
&&&&&&&&&&&&& update&borrowrecord set studentid=i.studentid&from&borrowrecord&br,
&&&&&&&&&&&&& deleted d&,inserted&i& -- deleted 和 inserted临时表
&&&&&&&&&&&&& where&br.studentid=d.studentid&
&&&&&&&&&&end
  理解触发器里面的两个临时的表:deleted&,&inserted&。注意deleted&与inserted分别表示触发事件的表&#8220;旧的一条记录&#8221;和&#8220;新的一条记录&#8221;。&
  一个数据库系统中有两个虚拟表用于存储在表中记录改动的信息,分别是:&
  虚拟表inserted        虚拟表deleted&
  在表记录新增时存放新增的记录 不存储记录
  修改时存放用来更新的新记录  存放更新前的记录
  删除时不存储记录&存放被删除的记录&
  一个update&的过程可以看作为:生成新的记录到inserted表,复制旧的记录到deleted表,然后删除student记录并写入新纪录。&
  对于2,创建一个delete触发器&
create&trigger&trdstudent&on&student&for&deleteas&delete&borrowrecord&from&borrowrecord&br&,&delted&d&where&br.studentid=d.studentid&
  从这两个例子我们可以看到了触发器的关键:a.2个临时的表;b.触发机制。
  这里我们只讲解最简单的触发器。复杂的容后说明。
  事实上,我不鼓励使用触发器。触发器的初始设计思想,已经被&#8220;级联&#8221;所替代。
下面是另一个触发器名为"UpdateUser"的例子,当表member中的"在线否"字段被更新时,将触发更新UPDATE事件,程序将更新本条记录的"最后进入网站日期"的内容:
  CREATE TRIGGER [UpdateUser] ON [dbo].[member]
  FOR UPDATE
  if (Update([在线否]))
    Update member set&最后进入网站日期=GETDATE() from member m,deleted d,inserted i& where m.id=d.id and m.在线否="在线"
在数据库设计中,有两种方法可设定自动化的资料处理规则,一种是条件约束,一种是触发器,一般而言,条件约束比触发器较容易设定及维护,且执行效率较好,但条件约束只能对资料进行简单的栏位检核,当涉及到多表操作等复杂操
作时,就要用到触发器了.
一个数据库系统中有两个虚拟表用于存储在表中记录改动的信息,分别是:虚拟表Inserted、虚拟表Deleted
在表记录新增时 存放新增的记录 不存储记录
修改时 存放用来更新的新记录 存放更新前的记录
删除时 不存储记录 存放被删除的记录
触发器的种类及触发时机
After触发器:触发时机在资料已变动完成后,它将对变动资料进行必要的善后与处理,若发现有错误,则用事务回滚(Rollback Transaction)将此次操作所更动的资料全部回复.
Istead of 触发器:触发时机在资料变动前发生,且资料如何变动取决于触发器.
现在介绍一下创建触发器的编写格式:
After类型:
Create Trigger 触发器名称
after 操作(insert,update)
Instead of类型:
Create Trigger 触发器名称
Instead of 操作(update,delete)
在订单(表orders)中的订购数量(列名为num)有变动时,触发器会先到客户(表Customer)中取得该用户的信用等级(列名为Level),然后再到信用额度(Creit)中取出该等级许可的订购数量上下限,最后比较订单中的订购数量是否符合限制。
Create Trigger num_check
after insert,update
if update(num)
&&&&&& if exists(select a.* from orders a join customer b on a.customerid=b.customerid
&&&&&&&join creit c on b.level=c.level
&&&&&&&where a.num between c.up and c.down)
&&&&&& begin
&&&&&&&& rollback transaction
&&&&&&&& exec master..xp_sendmail 'administrator','客户的订购数量不符合限制'
&&&&&& end
有工资管理系统中,当公司对某员工甲的月薪进行调整时,通常会先在表员工中修改薪资列,然后在表员工记录中修改薪资调整时间与薪资
Create trigger compensation
after update
if @@rowcount=0 return
if update(薪资)
insert 员工记录
select 员工遍号,薪资,getdate()
from inserted
CREATE TRIGGER trigger_name
ON { table | view }
[ WITH ENCRYPTION ]
&&&&{ { FOR | AFTER | INSTEAD OF } { [ INSERT ] [ , ] [ UPDATE ] }
&&&&&&&&[ WITH APPEND ]
&&&&&&&&[ NOT FOR REPLICATION ]
&&&&&&&&AS
&&&&&&&&[ { IF UPDATE ( column )
&&&&&&&&&&&&[ { AND | OR } UPDATE ( column ) ]
&&&&&&&&&&&&&&&&[ ...n ]
&&&&&&&&| IF ( COLUMNS_UPDATED ( ) { bitwise_operator } updated_bitmask )
&&&&&&&&&&&&&&&&{ comparison_operator } column_bitmask [ ...n ]
&&&&&&&&} ]
&&&&&&&&sql_statement [ ...n ]
trigger_name
是触发器的名称。触发器名称必须符合标识符规则,并且在数据库中必须唯一。可以选择是否指定触发器所有者名称。
Table | view
是在其上执行触发器的表或视图,有时称为触发器表或触发器视图。可以选择是否指定表或视图的所有者名称。
WITH ENCRYPTION
加密 syscomments 表中包含 CREATE TRIGGER 语句文本的条目。使用 WITH ENCRYPTION 可防止将触发器作为 SQL Server 复制的一部分发布。
指定触发器只有在触发 SQL 语句中指定的所有操作都已成功执行后才激发。所有的引用级联操作和约束检查也必须成功完成后,才能执行此触发器。
如果仅指定 FOR 关键字,则 AFTER 是默认设置。
不能在视图上定义 AFTER 触发器。
INSTEAD OF
指定执行触发器而不是执行触发 SQL 语句,从而替代触发语句的操作。
在表或视图上,每个 INSERT、UPDATE 或 DELETE 语句最多可以定义一个 INSTEAD OF 触发器。然而,可以在每个具有 INSTEAD OF 触发器的视图上定义视图。
INSTEAD OF 触发器不能在 WITH CHECK OPTION 的可更新视图上定义。如果向指定了 WITH CHECK OPTION 选项的可更新视图添加 INSTEAD OF 触发器,SQL Server 将产生一个错误。用户必须用 ALTER VIEW 删除该选项后才能定义 INSTEAD OF 触发器。
{ [DELETE] [,] [INSERT] [,] [UPDATE] }
是指定在表或视图上执行哪些数据修改语句时将激活触发器的关键字。必须至少指定一个选项。在触发器定义中允许使用以任意顺序组合的这些关键字。如果指定的选项多于一个,需用逗号分隔这些选项。
对于 INSTEAD OF 触发器,不允许在具有 ON DELETE 级联操作引用关系的表上使用 DELETE 选项。同样,也不允许在具有 ON UPDATE 级联操作引用关系的表上使用 UPDATE 选项。
WITH APPEND
指定应该添加现有类型的其它触发器。只有当兼容级别是 65 或更低时,才需要使用该可选子句。如果兼容级别是 70 或更高,则不必使用 WITH APPEND 子句添加现有类型的其它触发器(这是兼容级别设置为 70 或更高的 CREATE TRIGGER 的默认行为)。有关更多信息,请参见 。
WITH APPEND 不能与 INSTEAD OF 触发器一起使用,或者,如果显式声明 AFTER 触发器,也不能使用该子句。只有当出于向后兼容而指定 FOR 时(没有 INSTEAD OF 或 AFTER),才能使用 WITH APPEND。以后的版本将不支持 WITH APPEND 和 FOR(将被解释为 AFTER)。
NOT FOR REPLICATION
表示当复制进程更改触发器所涉及的表时,不应执行该触发器。
是触发器要执行的操作。
sql_statement
是触发器的条件和操作。触发器条件指定其它准则,以确定 DELETE、INSERT 或 UPDATE 语句是否导致执行触发器操作。
当尝试 DELETE、INSERT 或 UPDATE 操作时,Transact-SQL语句中指定的触发器操作将生效。
触发器可以包含任意数量和种类的 Transact-SQL 语句。触发器旨在根据数据修改语句检查或更改数据;它不应将数据返回给用户。触发器中的 Transact-SQL 语句常常包含控制流语言。CREATE TRIGGER 语句中使用几个特殊的表:
&#183;SELECT *
&#183;FROM deleted
70 DELETEINSERT
UPDATE SQL Server
deleted textntext
当兼容级别是 80 或更高时,SQL Server 允许在表或视图上通过 INSTEAD OF 触发器更新 text、ntext 或 image 列。
是表示触发器中可以包含多条 Transact-SQL 语句的占位符。对于 IF UPDATE (column) 语句,可以通过重复 UPDATE (column) 子句包含多列。
IF UPDATE (column)
测试在指定的列上进行的 INSERT 或 UPDATE 操作,不能用于 DELETE 操作。可以指定多列。因为在 ON 子句中指定了表名,所以在 IF UPDATE 子句中的列名前不要包含表名。若要测试在多个列上进行的 INSERT 或 UPDATE 操作,请在第一个操作后指定单独的 UPDATE(column) 子句。在 INSERT 操作中 IF UPDATE 将返回 TRUE 值,因为这些列插入了显式值或隐性 (NULL) 值。
说明&&IF UPDATE (column) 子句的功能等同于 IF、IF...ELSE 或 WHILE 语句,并且可以使用 BEGIN...END 语句块。有关更多信息,请参见。
可以在触发器主体中的任意位置使用 UPDATE (column)。
是要测试 INSERT 或 UPDATE 操作的列名。该列可以是 SQL Server 支持的任何数据类型。但是,计算列不能用于该环境中。有关更多信息,请参见。
IF (COLUMNS_UPDATED())
测试是否插入或更新了提及的列,仅用于 INSERT 或 UPDATE 触发器中。COLUMNS_UPDATED 返回 varbinary 位模式,表示插入或更新了表中的哪些列。
COLUMNS_UPDATED 函数以从左到右的顺序返回位,最左边的为最不重要的位。最左边的位表示表中的第一列;向右的下一位表示第二列,依此类推。如果在表上创建的触发器包含 8 列以上,则 COLUMNS_UPDATED 返回多个字节,最左边的为最不重要的字节。在 INSERT 操作中 COLUMNS_UPDATED 将对所有列返回 TRUE 值,因为这些列插入了显式值或隐性 (NULL) 值。
可以在触发器主体中的任意位置使用 COLUMNS_UPDATED。
bitwise_operator
是用于比较运算的位运算符。
updated_bitmask
是整型位掩码,表示实际更新或插入的列。例如,表 t1 包含列 C1、C2、C3、C4 和 C5。假定表 t1 上有 UPDATE 触发器,若要检查列 C2、C3 和 C4 是否都有更新,指定值 14;若要检查是否只有列 C2 有更新,指定值 2。
comparison_operator
是比较运算符。使用等号 (=) 检查 updated_bitmask 中指定的所有列是否都实际进行了更新。使用大于号 (&) 检查 updated_bitmask 中指定的任一列或某些列是否已更新。
column_bitmask
是要检查的列的整型位掩码,用来检查是否已更新或插入了这些列。
触发器常常用于强制业务规则和数据完整性。SQL Server 通过表创建语句(ALTER TABLE 和 CREATE TABLE)提供声明引用完整性 (DRI);但是 DRI 不提供数据库间的引用完整性。若要强制引用完整性(有关表的主键和外键之间关系的规则),请使用主键和外键约束(ALTER TABLE 和 CREATE TABLE 的 PRIMARY KEY 和 FOREIGN KEY 关键字)。如果触发器表存在约束,则在 INSTEAD OF 触发器执行之后和 AFTER 触发器执行之前检查这些约束。如果违反了约束,则回滚 INSTEAD OF 触发器操作且不执行(激发)AFTER 触发器。
可用 sp_settriggerorder 指定表上第一个和最后一个执行的 AFTER 触发器。在表上只能为每个 INSERT、UPDATE 和 DELETE 操作指定一个第一个执行和一个最后一个执行的 AFTER 触发器。如果同一表上还有其它 AFTER 触发器,则这些触发器将以随机顺序执行。
如果 ALTER TRIGGER 语句更改了第一个或最后一个触发器,则将除去已修改触发器上设置的第一个或最后一个特性,而且必须用 sp_settriggerorder 重置排序值。
只有当触发 SQL 语句(包括所有与更新或删除的对象关联的引用级联操作和约束检查)成功执行后,AFTER 触发器才会执行。AFTER 触发器检查触发语句的运行效果,以及所有由触发语句引起的 UPDATE 和 DELETE 引用级联操作的效果。
触发器限制
CREATE TRIGGER 必须是批处理中的第一条语句,并且只能应用到一个表中。
触发器只能在当前的数据库中创建,不过触发器可以引用当前数据库的外部对象。
如果指定触发器所有者名称以限定触发器,请以相同的方式限定表名。
在同一条 CREATE TRIGGER 语句中,可以为多种用户操作(如 INSERT 和 UPDATE)定义相同的触发器操作。
如果一个表的外键在 DELETE/UPDATE 操作上定义了级联,则不能在该表上定义 INSTEAD OF DELETE/UPDATE 触发器。
在触发器内可以指定任意的 SET 语句。所选择的 SET 选项在触发器执行期间有效,并在触发器执行完后恢复到以前的设置。
与使用存储过程一样,当触发器激发时,将向调用应用程序返回结果。若要避免由于触发器激发而向应用程序返回结果,请不要包含返回结果的 SELECT 语句,也不要包含在触发器中进行变量赋值的语句。包含向用户返回结果的 SELECT 语句或进行变量赋值的语句的触发器需要特殊处理;这些返回的结果必须写入允许修改触发器表的每个应用程序中。如果必须在触发器中进行变量赋值,则应该在触发器的开头使用 SET NOCOUNT 语句以避免返回任何结果集。
DELETE 触发器不能捕获 TRUNCATE TABLE 语句。尽管 TRUNCATE TABLE 语句实际上是没有 WHERE 子句的 DELETE(它删除所有行),但它是无日志记录的,因而不能执行触发器。因为 TRUNCATE TABLE 语句的权限默认授予表所有者且不可转让,所以只有表所有者才需要考虑无意中用 TRUNCATE TABLE 语句规避 DELETE 触发器的问题。
无论有日志记录还是无日志记录,WRITETEXT 语句都不激活触发器。
触发器中不允许以下 Transact-SQL 语句:
ALTER DATABASE
CREATE DATABASE
DISK RESIZE
DROP DATABASE
LOAD DATABASE
RECONFIGURE
RESTORE DATABASE
RESTORE LOG
说明&&由于 SQL Server 不支持系统表中的用户定义触发器,因此建议不要在系统表中创建用户定义触发器。
多个触发器
SQL Server 允许为每个数据修改事件(DELETE、INSERT 或 UPDATE)创建多个触发器。例如,如果对已有 UPDATE 触发器的表执行 CREATE TRIGGER FOR UPDATE,则将创建另一个更新触发器。在早期版本中,在每个表上,每个数据修改事件(INSERT、UPDATE 或 DELETE)只允许有一个触发器。
说明&&如果触发器名称不同,则 CREATE TRIGGER(兼容级别为 70)的默认行为是在现有的触发器中添加其它触发器。如果触发器名称相同,则 SQL Server 返回一条错误信息。但是,如果兼容级别等于或小于 65,则使用 CREATE TRIGGER 语句创建的新触发器将替换同一类型的任何现有触发器,即使触发器名称不同。有关更多信息,请参见 。
递归触发器
当在 sp_dboption 中启用 recursive triggers 设置时,SQL Server 还允许触发器的递归调用。
递归触发器允许发生两种类型的递归:
使用间接递归时,应用程序更新表 T1,从而激发触发器 TR1,该触发器更新表 T2。在这种情况下,触发器 T2 将激发并更新 T1。
使用直接递归时,应用程序更新表 T1,从而激发触发器 TR1,该触发器更新表 T1。由于表 T1 被更新,触发器 TR1 再次激发,依此类推。
下例既使用了间接触发器递归,又使用了直接触发器递归。假定在表 T1 中定义了两个更新触发器 TR1 和 TR2。触发器 TR1 递归地更新表 T1。UPDATE 语句使 TR1 和 TR2 各执行一次。而 TR1 的执行将触发 TR1(递归)和 TR2 的执行。给定触发器的 inserted 和 deleted 表只包含与唤醒调用触发器的 UPDATE 语句相对应的行。
说明&&只有启用 sp_dboption 的 recursive triggers 设置,才会发生上述行为。对于为给定事件定义的多个触发器,并没有确定的执行顺序。每个触发器都应是自包含的。
禁用 recursive triggers 设置只能禁止直接递归。若要也禁用间接递归,请使用 sp_configure 将 nested triggers 服务器选项设置为 0。
如果任一触发器执行了 ROLLBACK TRANSACTION 语句,则无论嵌套级是多少,都不会进一步执行其它触发器。
嵌套触发器
触发器最多可以嵌套 32 层。如果一个触发器更改了包含另一个触发器的表,则第二个触发器将激活,然后该触发器可以再调用第三个触发器,依此类推。如果链中任意一个触发器引发了无限循环,则会超出嵌套级限制,从而导致取消触发器。若要禁用嵌套触发器,请用 sp_configure 将 nested triggers 选项设置为 0(关闭)。默认配置允许嵌套触发器。如果嵌套触发器是关闭的,则也将禁用递归触发器,与 sp_dboption 的 recursive triggers 设置无关。
延迟名称解析
SQL Server 允许 Transact-SQL 存储过程、触发器和批处理引用编译时不存在的表。这种能力称为延迟名称解析。但是,如果 Transact-SQL 存储过程、触发器或批处理引用在存储过程或触发器中定义的表,则只有当兼容级别设置(通过执行 sp_dbcmptlevel 设置)等于 65 时,才会在创建时发出警告。如果使用批处理,则在编译时发出警告。如果引用的表不存在,将在运行时返回错误信息。有关更多信息,请参见延迟名称解析和编译。
CREATE TRIGGER 权限默认授予定义触发器的表所有者、sysadmin 固定服务器角色成员以及 db_owner 和 db_ddladmin 固定数据库角色成员,并且不可转让。
若要检索表或视图中的数据,用户必须在表或视图中拥有 SELECT 语句权限。若要更新表或视图的内容,用户必须在表或视图中拥有 INSERT、DELETE 和 UPDATE 语句权限。
如果视图中存在 INSTEAD OF 触发器,用户必须在该视图中有 INSERT、DELETE 和 UPDATE 特权,以对该视图发出 INSERT、DELETE 和 UPDATE 语句,而不管实际上是否在视图上执行了这样的操作。
A. 使用带有提醒消息的触发器
当有人试图在 titles 表中添加或更改数据时,下例将向客户端显示一条消息。
说明&&消息 50009 是 sysmessages 中的用户定义消息。有关创建用户定义消息的更多信息,请参见 。
IF EXISTS (SELECT name FROM sysobjects
&&&&& WHERE name = 'reminder' AND type = 'TR')
&& DROP TRIGGER reminder
CREATE TRIGGER reminder
FOR INSERT, UPDATE
AS RAISERROR (5)
B. 使用带有提醒电子邮件的触发器
当 titles 表更改时,下例将电子邮件发送给指定的人员 (MaryM)。
IF EXISTS (SELECT name FROM sysobjects
&&&&& WHERE name = 'reminder' AND type = 'TR')
&& DROP TRIGGER reminder
CREATE TRIGGER reminder
FOR INSERT, UPDATE, DELETE
&& EXEC master..xp_sendmail 'MaryM',
&&&&&&'Don''t forget to print a report for the distributors.'
C. 在 employee 和 jobs 表之间使用触发器业务规则
由于 CHECK 约束只能引用定义了列级或表级约束的列,表间的任何约束(在下例中是指业务规则)都必须定义为触发器。
下例创建一个触发器,当插入或更新雇员工作级别 (job_lvls) 时,该触发器检查指定雇员的工作级别(由此决定薪水)是否处于为该工作定义的范围内。若要获得适当的范围,必须引用 jobs 表。
IF EXISTS (SELECT name FROM sysobjects
&&&&& WHERE name = 'employee_insupd' AND type = 'TR')
&& DROP TRIGGER employee_insupd
CREATE TRIGGER employee_insupd
ON employee
FOR INSERT, UPDATE
/* 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)
&& RAISERROR ('Job id 1 expects the default level of 10.', 16, 1)
&& ROLLBACK TRANSACTION
IF NOT (@emp_lvl BETWEEN @min_lvl AND @max_lvl)
&& RAISERROR ('The level for job_id:%d should be between %d and %d.',
&&&&& 16, 1, @job_id, @min_lvl, @max_lvl)
&& ROLLBACK TRANSACTION
D. 使用延迟名称解析
下例创建两个触发器以说明延迟名称解析。
IF EXISTS (SELECT name FROM sysobjects
&&&&& WHERE name = 'trig1' AND type = 'TR')
&& DROP TRIGGER trig1
-- Creating a trigger on a nonexistent table.
CREATE TRIGGER trig1
on authors
FOR INSERT, UPDATE, DELETE
&&&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
-- 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.
IF EXISTS (SELECT name FROM sysobjects
&&&&& WHERE name = 'trig2' AND type = 'TR')
&& DROP TRIGGER trig2
CREATE TRIGGER trig2
ON authors
FOR INSERT, UPDATE
&&&DECLARE @fax varchar(12)
&& SELECT @fax = phone&&
&&&FROM authors
-- 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'
E. 使用 COLUMNS_UPDATED
下例创建两个表:一个 employeeData 表和一个 auditEmployeeData 表。人力资源部的成员可以修改 employeeData 表,该表包含敏感的雇员薪水信息。如果更改了雇员的社会保险号码 (SSN)、年薪或银行帐户,则生成审核记录并插入到 auditEmployeeData 审核表。
通过使用 COLUMNS_UPDATED() 功能,可以快速测试对这些包含敏感雇员信息的列所做的更改。只有在试图检测对表中的前 8 列所做的更改时,COLUMNS_UPDATED() 才起作用。
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
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
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()
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 &0 (below).*/
&& IF (COLUMNS_UPDATED() & 14) & 0
/*Use IF (COLUMNS_UPDATED() & 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
/*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)
/*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
SELECT * FROM auditEmployeeData
/*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'
&& WHERE emp_id = 101
SELECT * FROM auditEmployeeData
F. 使用 COLUMNS_UPDATED 测试 8 列以上
如果必须测试影响到表中前 8 列以外的列的更新时,必须使用 UBSTRING 函数测试由 COLUMNS_UPDATED 返回的适当的位。下例测试影响 Northwind.dbo.Customers 表中的第 3、第 5 或第 9 列的更新。
USE Northwind
DROP TRIGGER& tr1
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'
UPDATE Customers
&&&SET ContactName=ContactName,
&&&&& Address=Address,
&&&&& Country=Country
创建一个简单的触发器
触发器是一种特殊的存储过程,类似于事件函数,SQL Server&#8482; 允许为 INSERT、UPDATE、DELETE 创建触发器,即当在表中插入、更新、删除记录时,触发一个或一系列 T-SQL语句。
触发器可以在查询分析器里创建,也可以在表名上点右键-&&#8220;所有任务&#8221;-&&#8220;管理触发器&#8221;来创建,不过都是要写 T-SQL 语句的,只是在查询分析器里要先确定当前操作的数据库
创建触发器用 CREATE TRIGGER
CREATE TRIGGER 触发器名称
FOR INSERT、UPDATE 或 DELETE
&&& T-SQL 语句
注意:触发器名称是不加引号的。
如下是联机丛书上的一个示例,当在 titles 表上更改记录时,发送邮件通知 MaryM。
CREATE TRIGGER reminder
FOR INSERT, UPDATE, DELETE
&& EXEC master..xp_sendmail 'MaryM',
&&&&& 'Don''t forget to print a report for the distributors.'
删除触发器
用查询分析器删除
在查询分析器中使用 drop trigger 触发器名称 来删除触发器。
也可以同时删除多个触发器:drop trigger 触发器名称,触发器名称...
注意:触发器名称是不加引号的。在删除触发器之前可以先看一下触发器是否存在:
if Exists(select name from sysobjects where name=触发器名称 and xtype='TR')
用企业管理器删除
在企业管理器中,在表上点右键-&&#8220;所有任务&#8221;-&&#8220;管理触发器&#8221;,选中所要删除的触发器,然后点击&#8220;删除&#8221;。
重命名触发器
用查询分析器重命名
exec sp_rename 原名称, 新名称
sp_rename 是 SQL Server&#8482; 自带的一个存储过程,用于更改当前数据库中用户创建的对象的名称,如表名、列表、索引名等。
用企业管理器重命名
在表上点右键-&&#8220;所有任务&#8221;-&&#8220;管理触发器&#8221;,选中所要重命名的触发器,修改触发器语句中的触发器名称,点击&#8220;确定&#8221;。
触发器更多语法
INSTEAD OF
执行触发器语句,但不执行触发触发器的 SQL 语句,比如试图删除一条记录时,将执行触发器指定的语句,此时不再执行 delete 语句。例:
create trigger f
instead of delete
&&& insert into Logs...
IF UPDATE(列名)
检查是否更新了某一列,用于 insert 或 update,不能用于 delete。例:
create trigger f
for update
&&& if update(status) or update(title)
&&&&&&& sql_statement --更新了 status 或 title 列
inserted、deleted
这是两个虚拟表,inserted 保存的是 insert 或 update 之后所影响的记录形成的表,deleted 保存的是 delete 或 update 之前所影响的记录形成的表。例:
create trigger tbl_delete
for delete
&&&& declare @title varchar(200)
&&&& select @title=title from deleted
&&& insert into Logs(logContent) values('删除了 title 为:' + title + '的记录')
说明:如果向 inserted 或 deleted 虚拟表中取字段类型为 text、image 的字段值时,所取得的值将会是 null。
如何查看当前数据库中有哪些触发器
用sysobjects表 xtype='TR'
如何查看某个触发器的内容
用查询分析器查看
use 数据库名
exec sp_helptext '触发器名称'
将会以表的样式显示触发器内容。
除了触发器外,sp_helptext 还可以显示 规则、默认值、未加密的存储过程、用户定义函数、视图的文本
用企业管理器查看
在表上点右键-&&#8220;所有任务&#8221;-&&#8220;管理触发器&#8221;,选择所要查看的触发器
sp_helptrigger
存储过程 sp_helptrigger 用于查看触发器的属性。
sp_helptrigger 有两个参数:第一个参数为表名;第二个为触发器类型,为 char(6) 类型,可以是 INSERT、UPDATE、DELETE,如果省略则显示指定表中所有类型触发器的属性。
use 数据库名
exec sp_helptrigger tbl
多个触发器
触发器的名称不同,触发事件相同(INSERT、UPDATE、DELETE),我们称为多个触发器。
多个触发器之间的执行顺序并不确定。
我个人认为应该避免使用多个触发器,因为它不利于维护。
递归、嵌套触发器
递归触发器
递归分两种,间接递归和直接递归。我们举例解释如下,假如有表1、表2名称分别为 T1、T2,在 T1、T2 上分别有触发器 G1、G2。
间接递归:对 T1 操作从而触发 G1,G1 对 T2 操作从而触发 G2,G2 对 T1 操作从而再次触发 G1...
直接递归:对 T1 操作从而触发 G1,G1 对 T1 操作从而再次触发 G1...
嵌套触发器
类似于间接递归,间接递归必然要形成一个环,而嵌套触发器不一定要形成一个环,它可以 T1-&T2-&T3...这样一直触发下去,最多允许嵌套 32 层。
设置直接递归
默认情况下是禁止直接递归的,要设置为允许有两种方法:
T-SQL:exec sp_dboption 'dbName', 'recursive triggers', true
EM:数据库上点右键-&属性-&选项。
设置间接递归、嵌套默认情况下是允许间接递归、嵌套的,要设置为禁止有两种方法:
T-SQL:exec sp_configure 'nested triggers', 0 --第二个参数为 1 则为允许
EM:注册上点右键-&属性-&服务器设置。
--------------------------------------------------------------------------------------------------------------------------------
在视图上创建触发器
在视图上创建普通触发器可能会出现&#8220;对象无效&#8221;的错误,实际上,我们不能在视图上创建 FOR 触发器,而应该创建 INSTEAD OF 触发器.
深刻理解 FOR CREATE TRIGGER 语句的 FOR 关键字之后可以跟 INSERT、UPDATE、DELETE 中的一个或多个,也就是说在其它情况下是不会触发触发器的,包括 SELECT、TRUNCATE、WRITETEXT、UPDATETEXT。
TRUNCATE TABLE和DELETE
TRUNCATE TABLE 和不带 WHERE 的 DELETE 功能是一样的,都是删除表中的所有数据,不过 TRUNCATE TABLE 速度更快,占用的日志更少,这是因为 TRUNCATE TABLE 直接释放数据页并且在事务日志中也只记录数据页的释放,而 DELETE 是一行一行地删除,在事务日志中要记录每一条记录的删除。
那么可不可以用 TRUNCATE TABLE 代替不带 WHERE 的 DELETE 呢?在以下情况是不行的:
1、要保留标识的情况下不能用 TRUNCATE TABLE,因为 TRUNCATE TABLE 会重置标识。
2、需要使用触发器的情况下不能使用 TRUNCATE TABLE ,它不会激发触发器。
3、对于由 FOREIGN KEY 约束引用的表(即主键所在的表,不是外键所在的表)不能使用 TRUNCATE TABLE。
4、对于参与了索引视图的表不能使用 TRUNCATE TABLE ,注意指索引视图,并非普通视图。
一个有趣的应用
我们看到许多注册系统在注册后都不能更改用户名,但这多半是由应用程序决定的,如果直接打开数据库表进行更改,同样可以更改其用户名,在触发器中利用回滚就可以巧妙地实现无法更改用户名。
use 数据库名
create trigger tr
for update
&&&&if update(userName)
&&&&&&&&rollback tran
关键在最后两句,其解释为:如果更新了 userName 列,就回滚事务。
触发器内部语句出错时&#8230;&#8230; 这种情况下,前面对数据更改操作将会无效。举个例子,在表中插入数据时触发触发器,而触发器内部此时发生了运行时错误,那么将返回一个错误值,并且拒绝刚才的数据插入。
不能在触发器中使用的语句 触发器中可以使用大多数 T-SQL 语句,但如下一些语句是不能在触发器中使用的。
CREATE 语句,如:CREATE DATABASE、CREATE TABLE、CREATE INDEX 等。
ALTER 语句,如:ALTER DATABASE、ALTER TABLE、ALTER INDEX 等。
DROP 语句,如:DROP DATABASE、DROP TABLE、DROP INDEX 等。
DISK 语句,如:DISK INIT、DISK RESIZE。
LOAD 语句,如:LOAD DATABASE、LOAD LOG。
RESTORE 语句,如:RESTORE DATABASE、RESTORE LOG。
RECONFIGURE
说明:有人说不能用 TRUNCATE TABLE 语句,其实是可以的。
慎用触发器
触发器功能强大,轻松可靠地实现许多复杂的功能,为什么又要慎用呢。触发器本身没有过错,但由于我们的滥用会造成数据库及应用程序的维护困难。
在数据库操作中,我们可以通过关系、触发器、存储过程、应用程序等来实现数据操作,比如删除 T1 表记录时期望删除 T2 表相关的记录,此时可以建立级联删除的关系,也可以为 T1 表建立触发器使同时删除 T2 表相关记录,也可以自定义存储过程删除 T1 和 T2 表的记录,也可以在应用程序中使用两个 SQL 语句来删除&#8230;&#8230;到底用哪一种好呢,应该说我们建立通过建立关系来实现级联删除是最好的,除非更有高的需求。
触发器还有一个用途可以用来保障数据的完整性,但同时规则、约束、默认值也可以保障数据完整性,到底哪一个好呢,一般说来,较为简单的完整性要求我们不应该使用触发器。两者在运行机制上也是有区别的,像规则、约束、默认值这些是在数据更改之前进行数据验证,而触发器是在数据更改之后进行验证(如果事务回滚,该表将不会产生变化)。
总之,如果我们对触发器过分的依赖,就会造成遍地是程序的情况,因为触发器本身就需要别的程序给它一个触发条件,也就是说至少在两个地方存在着程序,同时我们抛弃了约束、默认值等而选用触发器,势必影响数据库的结构。
阅读(...) 评论() &

我要回帖

更多关于 无法找到dsetup.dll 的文章

 

随机推荐