Sql Server专题二:数据库主要对象
存储过程
(1)减少网络通信量。调用一个行数不多的存储过程与直接调用sql语句的网络通信量可能不会有很大的差别,可是如果存储过程包含上百行sql语句,那么其性能绝对比一条一条的调用sql语句要高得多。
(2)执行速度更快。有两个原因:首先,在存储过程创建的时候,数据库已经对其进行了一次解析和优化。其次,存储过程一旦执行,在内存中就会保留一份这个存储过程,这样下次再执行同样的存储过程时,可以从内存中直接调用。
(3)更强的适应性:由于存储过程对数据库的访问是通过存储过程来进行的,因此数据库开发人员可以在不改动存储过程接口的情况下对数据库进行任何改动,而这些改动不会对应用程序造成影响。
(4) 布式工作:应用程序和数据库的编码工作可以分别独立进行,而不会相互压制。
存储过程可以用临时表,函数不能用临时表
存储过程可以使用Update,函数不能使用Update
存储过程可以用Getdate()等函数,函数不能使用Getdate()这些函数
一、使用存储过程要注意一些问题
1、在as后面立即跟上一个 set nocount on子句,这样会减少很大开销(相对于过程中存在DML操作的时候)
2、创建和引用过程的时候加上架构名
3、避免在返回许多行数据的 SELECT 语句中使用标量函数(getdate())。因为标量函数必须应用于每一行。
4、避免使用 SELECT *。而是应指定所需的列名称。
5、避免处理或返回”过多”的数据,一些没必要的数据就不要了
6、通过使用 BEGIN/END TRANSACTION 来使用显式事务并且保留尽可能短的事务。更长的事务意味着更长的记录锁定和更高的死锁风险
7、在 LIKE 子句中避免使用通配符作为前导字符,例如 LIKE ‘%a%’。因为第一个字符是不确定的,所以,查询处理器无法使用可用的索引。应改用 LIKE ‘a%’。
8、使用 Transact-SQL TRY…CATCH 功能进行过程内的错误处理
9、创建/修改Table时给列一个Default值
10、使用 IF EXISTS (SELECT TOP 1 FROM table_name) 来代替 IF EXISTS (SELECT * FROM table_name)。
11、使用 UNION ALL 运算符来代替 UNION 或 OR 运算符
12、创建存储过程时在其定义中指定 WITH RECOMPILE 选项,表明 SQL Server 将不对该存储过程计划进行高速缓存;该存储过程将在每次执行时都重新编译。
当存储过程的参数值在各次执行间都有较大差异(个人觉得应该是动态创建存储过程时,使得很可能有时候参数不同),导致每次均需创建不同的执行计划时,可使用 WITH RECOMPILE选项。此选项并不常用,因为每次执行存储过程时都必须对其进行重新编译,这样会使存储过程的执行变慢,
二、知识点:
1、SQL Server 启动时可以自动执行一个或多个过程。这些过程必须由系统管理员在 master 数据库中创建,并以 sysadmin 固定服务器角色作为后台进程执行。这些过程不能有任何输入或输出参数。
2、过程可以嵌套,并且最多可以嵌套 32 级。
3、过程可以引用尚不存在的表。在创建时,只进行语法检查。直到第一次执行该过程时才对其进行编译。只有在编译过程中才解析过程中引用的所有对象。
加密存储过程
Create procedure FirstP
WITH ENCRYPTION
as
set nocount on
select * from C
go
EXEC sp_helptext 'dbo.FirstP'
存储过程实现递归
select * from tb
order by case when ISNUMERIC(col) =1 then '' else left(col,1) end,
cast(case when ISNUMERIC(col) =0 then stuff(col,1,1,'') else col end as int)
drop table dbo.Example
create table Example(ID int,ParentID int)
insert Example
select 0,null union
select 1,0 union
select 2,0 union
select 3,1 union
select 4,6 union
select 5,2
--取其某个ID为父节点的树结构的SQL如下:
create procedure display(@ID int) as
begin
WITH Example_Table(ID,ParentID,lv)
AS
(
--取根节点放入临时表
SELECT ID,ParentID,0 FROM Example WHERE ID=@ID
--根据已取到的数据递归取其字节点的数据
UNION ALL
SELECT A.ID,A.ParentID,B.lv+1 FROM Example A INNER JOIN Example_Table B ON A.ParentID = B.ID
)
SELECT * FROM Example_Table
end
drop table tb
go
自定义函数
用户定义自定义函数像内置函数一样返回标量值,也可以将结果集用表格变量返回
用户自定义函数的类型:
标量函数:返回一个标量值
表格值函数{内联表格值函数、多表格值函数}:返回行集(即返回多个值)
1、标量函数
Create function 函数名(参数)
Returns 返回值数据类型
[with {Encryption | Schemabinding }]
[as]
begin
SQL语句(必须有return 变量或值)
End
Schemabinding :将函数绑定到它引用的对象上(注:函数一旦绑定,则不能删除、修改,除非删除绑定)
Create function AvgResult(@scode varchar(10))
Returns real
As
Begin
Declare @avg real
Declare @code varchar(11)
Set @code=@scode + ‘%’
Select @avg=avg(result) from LearnResult_baijiali
Where scode like @code
Return @avg
End
执行用户自定义函数
select 用户名。函数名 as 字段别名
select dbo.AvgResult(‘s0002’) as result
用户自定义函数返回值可放到局部变量中,用set ,select,exec赋值
declare @avg1 real ,@avg2 real ,@avg3 real
select @avg1= dbo.AvgResult(‘s0002’)
set @avg2= dbo.AvgResult(‘s0002’)
exec @avg3= dbo.AvgResult ‘s0002’
select @avg1 as avg1 ,@avg2 as avg2 ,@avg3 as avg3
函数引用
create function code(@scode varchar(10))
returns varchar(10)
as
begin
declare @ccode varchar(10)
set @scode = @scode + ‘%’
select @ccode=ccode from cmessage
where ccode like @scode
return @ccode
end
select name from class where ccode = dbo.code(‘c001’)
2、表格值函数
a、 内联表格值函数
格式:
create function 函数名(参数)
returns table
[with {Encryption | Schemabinding }]
as
return(一条SQL语句)
create function tabcmess(@code varchar(10))
returns table
as
return(select ccode,scode from cmessage where ccode like @ccode)
b、 多句表格值函数
create function 函数名(参数)
returns 表格变量名table (表格变量定义)
[with {Encryption | Schemabinding }]
as
begin
SQL语句
end
多句表格值函数包含多条SQL语句,至少有一条在表格变量中填上数据值
表格变量格式
returns @变量名 table (column 定义| 约束定义 [,…])
对表格变量中的行可执行select,insert,update,delete , 但select into 和 insert 语句的结果集是从存储过程插入。
Create function tabcmessalot (@code varchar(10))
Returns @ctable table(code varchar(10) null,cname varchar(100) null)
As
Begin
Insert @ctable
Select ccode,explain from cmessage
Where scode like @code
return
End
Select * from tabcmessalot(‘s0003’)
触发器
一: 触发器是一种特殊的存储过程﹐它不能被显式地调用﹐而是在往表中插入记录﹑更新记录或者删除记录时被自动地激活。所以触发器可以用来实现对表实施复杂的完整性约束。
二: SQL Server为每个触发器都创建了两个专用表:Inserted表和Deleted表。这两个表由系统来维护﹐它们存在于内存中而不是在数据库中。这两个表的结构总是与被该触发器作用的表的结构相同。触发器执行 完成后﹐与该触发器相关的这两个表也被删除。
Deleted表存放由于执行Delete或Update语句而要从表中删除的所有行。
Inserted表存放由于执行Insert或Update语句而要向表中插入的所有行。
三:Instead of 和 After触发器
SQL Server2000提供了两种触发器:Instead of 和After 触发器。这两种触发器的差别在于他们被激活的同:
Instead of触发器用于替代引起触发器执行的T-SQL语句。除表之外﹐Instead of 触发器也可以用于视图﹐用来扩展视图可以支持的更新操作。
After触发器在一个Insert,Update或Deleted语句之后执行﹐进行约束检查等动作都在After触发器被激活之前发生。After触发器只能用于表。
一个表或视图的每一个修改动作(insert,update和delete)都可以有一个instead of 触发器﹐一个表的每个修改动作都可以有多个After触发器。
四:触发器的执行过程
如果一个Insert﹑update或者delete语句违反了约束﹐那幺After触发器不会执行﹐因为对约束的检查是在After触发器被激动之前发生的。所以After触发器不能超越约束。
Instead of 触发器可以取代激发它的操作来执行。它在Inserted表和Deleted表刚刚建立﹐其它任何操作还没有发生时被执行。因为Instead of 触发器在约束之前执行﹐所以它可以对约束进行一些预处理。
五:使用T-SQL语句来创建触发器
基本语句如下:
create trigger trigger_name
on {table_name | view_name}
{for | After | Instead of }
[ insert, update,delete ]
as
sql_statement
六:删除触发器:
基本语句如下:
drop trigger trigger_name
七:查看数据库中已有触发器:
-- 查看数据库已有触发器
use jxcSoftware
go
select * from sysobjects where xtype='TR'
-- 查看单个触发器
exec sp_helptext '触发器名'
八:修改触发器:
基本语句如下:
alter trigger trigger_name
on {table_name | view_name}
{for | After | Instead of }
[ insert, update,delete ]
as
sql_statement
九:相关示例:
1:在Orders表中建立触发器﹐当向Orders表中插入一条订单记录时﹐检查goods表的货品状态status是否为1(正在整理)﹐是﹐则不能往Orders表加入该订单。
create trigger orderinsert
on orders
after insert
as
if (select status from goods,inserted
where goods.name=inserted.goodsname)=1
begin
print 'the goods is being processed'
print 'the order cannot be committed'
rollback transaction --回滚﹐避免加入
end
2:在Orders表建立一个插入触发器﹐在添加一条订单时﹐减少Goods表相应的货品记录中的库存。
create trigger orderinsert1
on orders
after insert
as
update goods set storage=storage-inserted.quantity
from goods,inserted
where
goods.name=inserted.goodsname
3:在Goods表建立删除触发器﹐实现Goods表和Orders表的级联删除。
create trigger goodsdelete
on goods
after delete
as
delete from orders
where goodsname in
(select name from deleted)
4:在Orders表建立一个更新触发器﹐监视Orders表的订单日期(OrderDate)列﹐使其不能手工修改.
create trigger orderdateupdate
on orders
after update
as
if update(orderdate)
begin
raiserror(' orderdate cannot be modified',10,1)
rollback transaction
end
5:在Orders表建立一个插入触发器﹐保证向Orders表插入的货品名必须要在Goods表中一定存在。
create trigger orderinsert3
on orders
after insert
as
if (select count(*) from goods,inserted where
goods.name=inserted.goodsname)=0
begin
print ' no entry in goods for this order'
rollback transaction
end
6:Orders表建立一个插入触发器,保证向Orders表插入的货品信息要在Order表中添加
alter trigger addOrder
on Orders
for insert
as
insert into Order
select inserted.Id, inserted.goodName,inserted.Number from inserted
游标
SQl Server的游标在你需要对记录集进行单条处理时很有用处。
select返回一个记录集,但是你想根据每条记录的不同情况进行单条处理,这时游标的用处就显出来了。
一个游标(cursor)可以被看作指向结果集(a set of rows)中一行的指针(pointer)。游标每个时间点只能指向一行,但是可以根据需要指向结果集中其他的行。
例如:SELECT * FROM employees WHERE sex='M'会返回所有性别为男的雇员,在初始的时候,游标被放置在结果集中第一行的前面。使游标指向第一行,要执行FETCH。当游标指向结果集中一行的时候,可以对这行数据进行加工处理,要想得到下一行数据,要继续执行FETCH。FETCH操作可以重复执行,直到完成结果集中的所有行
在存储过程中使用游标,有如下几个步骤:
声明游标、打开游标、根据需要一次一行,讲游标指向的数据取到本地变量(local variables)中、结束时关闭游标
创建游标分五个步骤:
CREATE PROCEDURE [dbo].[Proc_Syn_Data]
AS
BEGIN
SET NOCOUNT ON;
DECLARE @ID INT;
TRUNCATE TABLE Table2;
-- 1.声明游标
DECLARE CUR_MonthID CURSOR FOR
SELECT ID FROM [Table1]
-- 2.打开游标
OPEN CUR_MonthID
-- 3.从一个游标中查找信息,实现自己的数据处理。
FETCH CUR_MonthID INTO @ID
WHILE @@FETCH_STATUS=0
BEGIN
INSERT INTO [Table2]
SELECT * FROM [Table1] tm WHERE tm.ID=@ID
FETCH NEXT FROM CUR_MonthID INTO @ID
END;
-- 4.关闭游标
CLOSE CUR_MonthID;
-- 5.释放游标
DEALLOCATE CUR_MonthID;
END