ifyear(current_timestamp)<>year(dateadd(day,1,current_timestamp))beginprint'Today is the last day of the year.'endelsebeginprint'Today is not the last day of the year.'end
declare c cursor fast_forward forselect custid,ordermonth,qty
from sales.custorders
orderby custid,ordermonth;open c
fetchnextfrom c into@custid,@ordermonth,@qty;select@prvcustid=@custid,@runqty=0;while @@fetch_status=0beginif@custid<>@prvcustidselect@prvcustid=@custid,@runqty=0;set@runqty+=@qty;insertinto@resultvalues(@custid,@ordermonth,@qty,@runqty);fetchnextfrom c into@custid,@ordermonth,@qty;endclose c;deallocate c;
10.5 临时表
临时表都是在tempdb数据库中创建的
10.5.1 局部临时表
特点
只对创建它的会话在创建级和调用堆栈内部级是可见
会话断开会自动删除
使用场景
需要临时保存中间结果
需要多次访问某个开销昂贵的处理结果
声明方式
命名时以单个数字符号(#)作为前缀
if object_id('tempdb.dbo.#MyOrderTotalsByYear')isnotnulldroptable dbo.#MyOrderTotalsByYear;
go
selectyear(o.orderdate)as orderyear,sum(od.qty)as qty
into dbo.#MyOrderTotalsByYearfrom sales.orders as o
join sales.orderdetails as oo
on od.orderid = o.orderid
groupbyyear(orderdate);select cur.orderyear, cur.qty as curyearqty,
prv.qty as prvyearqty
from dbo.#MyOrderTotalsByYear as curleftouterjoin dbo.#MyOrderTotalsByYear as prvon cur.orderyear = prv.orderyear +1;
10.5.2 全局临时表
特点
对所有会话都是可见的
会话断开且没有活动在引用全局临时表时会自动删除
所有都可以操作全局临时表,也可以删除
使用场景
需要和所有人共享临时数据
声明方式
命名时以两个数字符号(##)作为前缀
createtable dbo.##globals(
id sysname notnullprimarykey,
val sql_variant notnull);
10.5.3 表变量
类似局部临时表
在tempdb中有对应的表作为其物理表示
只对创建它的会话可见,但只对当前批处理可见
类似变量
通过declare进行声明
对于少量的数据,使用表变量更有意义,否则应该使用临时表
declare@MyOrderTotalsByYeartable(
orderyear intnotnullprimarykey,,
qty intnotnull);insertinto@MyOrderTotalsByYear(orderyear,qty)selectyear(o.orderdate)as orderyear,sum(od.qty)as qty
from sales.orders as o
join sales.orderdetails as od
on o.orderid = od.orderid
groupbyyear(orderdate);select cur.orderyear, cur.qty as curyearqty,
prv.qty as prvyearqty
from@MyOrderTotalsByYearas cur
leftouterjoin@MyOrderTotalsByYearas prv
on cur.orderyear = prv.orderyear +1;
declare@sqlasvarchar(100);set@sql='print '' this message
was printed by a dynamic sql batch.'';';exec(@sql);
10.6.1.2 方式二:sp_executesql存储过程
sp_executesql
只支持unicode字符
支持参数化代码
有助于重用缓存过的执行计划
输入、输出参数
由两个输入参数部分和一个参数赋值部分构成
使用步骤
声明并构造参数化字符串
指定输入参数和输出参数
为输入参数和输出参数赋值
declare@sqlas nvarchar(100),@sumfreightasfloat;set@sql=N'set @totalfreight = (select SUM(freight)
from Sales.Orders
where custid = @custid)';exec sp_executesql
@stmt=@sql,@params= N'@custid as int,@totalfreight as float output',@custid=85,@totalfreight=@sumfreight output;select@sumfreightas sumfreight;
10.6.2 在pivot中使用动态SQL
declare@sqlas nvarchar(1000),@orderyearasint,@firstasint;declare c cursor fast_forward forselectdistinct(year(orderdate))as orderyear
from sales.orders
orderby orderyear;set@first=1;set@sql= N'select *
from (select shipperid,year(orderdate) as orderyear,freight
from sales.orders) as d
pivot(sum(freight) for orderyear in (';open c
fetchnextfrom c into@orderyear;while @@fetch_status=0beginif@first=0set@sql+= N','elseset@first=0;set@sql+= quotename(@orderyear);fetchnextfrom c into@orderyear;endclose c;deallocate c;set@sql+= N')) as p;';exec(@sql);
10.7 例程
例程
为了计算结果或执行任务而对代码进行封装的一种编程对象
10.7.1 用户定义函数
UDF
user-defined function
作用
封装计算的逻辑处理
要求
不允许对数据库中的任何架构或数据进行修改
分类
标量UDF
表值UDF
createfunction dbo.fn_age
(@birthdateasdatetime,@eventdateasdatetime)returnsintasbeginreturn
datediff(year,@birthdate,@eventdate)-casewhen100*month(@eventdate)+day(@eventdate)<100*month(@birthdate)+day(@birthdate)then1else0endend
go
10.7.2 存储过程
与UDF的区别
可以对数据库中的架构或数据进行修改
优点
逻辑封装
控制安全性
可授予存储过程的权限,而不是底层表的操作权限
可以做到有限制的操作
错误处理
提高执行性能
可重用缓存计划
减少网络通信流量
if object_id('sales.usp_getcustomerorders','P')isnotnulldropproc sales.usp_getcustomerorders;
go
createproc sales.usp_getcustomerorders
@custidasint,@fromdateasdatetime='19000101',@todateasdatetime='99991231',@numrowsasint output
asset nocount on;select orderid,custid,empid,orderdate
from sales.orders
where custid =@custidand orderdate >=@fromdateand orderdate <@todate;set@numrows= @@rowcount;
go
createtrigger trg_T1_insert_audit on dbo.T1 afterinsertasset nocount on;insertinto dbo.T1_audit(keycol, datacol)select keycol, datacol from inserted;
go
10.7.3.2 DDL触发器
SQL Server支持在两个作用域内创建DDL触发器
数据库作用域
create table
服务器作用域
create datebase
SQL Server只支持 after 类型的DDL触发器
createtrigger trg_audit_ddl_events
on datebase for ddl_datebase_level_events
as...
go
10.8 错误处理
error_number()
错误号
error_message()
错误的消息文本
error_severity()
错误严重级别
error_state()
错误状态号
error_line()
发生错误的行号
error_procedure()
发生错误的存储过程或触发器名称
begin try
insertinto dbo.employees(empid,empname,mgrid)values(1,'Emp1',null);end try
begin catch
if error_number()=2627begin...end...end catch