转:SQL进阶之变量、事务、存储过程与触发器
一、变量那点事儿
1.1 局部变量
(1)声明局部变量
DECLARE @变量名 数据类型
DECLARE @name varchar(20)
DECLARE @id int
(2)为变量赋值
SET @变量名 =值 --set用于普通的赋值
SELECT @变量名 = 值 --用于从表中查询数据并赋值,,可以一次给多个变量赋值 SET @name=‘张三’
SET @id = 1
SELECT @name = sName FROM student WHERE sId=@id
(3)输出变量的值
SELECT 以表格的方式输出,可以同时输出多个变量;而PRINT 则是以文本的方式输出,一次只能输出一个变量的值。
SELECT @name,@id
PRINT @name
PRINT @id
print @name,@id --错误!!
1.2 全局变量
(1)关于全局变量与局部变量
局部变量:
①局部变量必须以标记@作为前缀 ,如@Age int;
②局部变量需要先声明,再赋值;
全局变量(系统变量):
①全局变量必须以标记@@作为前缀,如@@version;
②全局变量由系统定义和维护,我们只能读取,不能修改全局变量的值;
(2)有哪些全局变量?
补充:@@error变量,在每次执行完SQL语句后,都会为@@error变量赋值,如果上次执行的SQL语句有错,则将@@errro赋值为一个不为0的值,否则(执行没错),则将@@error赋值为0.
(3)怎么使用全局变量
select @@LANGUAGE as '当前使用语言'
select @@SERVERNAME as '当前服务器名称'
select @@TRANCOUNT as '当前连接打开的事务数'
select @@MAX_CONNECTIONS as '可以同时连接的最大数目'
select @@VERSION as '当前服务器版本'
select @@ERROR as '最后一个T-SQL错误的错误号'
二、选择与循环:if(小苹果) begin 一直听根本停不下来 end
2.1 无处不在的 IF ELSE
(1)条件选择语法
IF(条件表达式)
BEGIN --相当于C#里的{
语句1 ……
END --相当于C#里的}
ELSE
BEGIN
语句1
……
END
IF(条件表达式)
BEGIN --相当于C#里的{
语句1 ……
END --相当于C#里的}
ELSE
BEGIN
语句1
……
END
(2)假设我们有一张选课成绩表SC,其中包括三个字段{S#,C#,Score},其中S#为学号,C#为课程号,而Score则为成绩。S#为Student表的外键,C#为课程表的外键。那么,根据这三张表,我们有一个需求:
计算平均分数并输出:如果平均分数超过60分输出成绩最高的三个学生的成绩,否则输出后三名的学生;
declare @avgscore float = 0
select @avgscore = AVG(Score) from SC
if(@avgscore>60)
begin
print '前三名'
select top 3 s.Sname,sc.Score from Student s join SC sc on s.S#=sc.S#
order by Score desc
end
else
begin
print '后三名'
select top 3 s.Sname,sc.Score from Student s join SC sc on s.S#=sc.S#
order by Score asc
end
2.2 死了都要爱 WHILE
(1)循环语句语法
WHILE(条件表达式)
BEGIN --相当于C#里的{
语句
……
continue --退出本次循环
BREAK --退出整个循环
END --相当于C#里的}
WHILE(条件表达式)
BEGIN --相当于C#里的{
语句
……
continue --退出本次循环
BREAK --退出整个循环
END --相当于C#里的}
(2)经典案例:计算1-100之间所有奇数的和
declare @index int = 1
declare @sum int = 0
while(@index <= 100)
begin
if(@index%2!=0)
begin
set @sum=@sum+@index
end
set @index=@index+1
end
三、事务:锤锤,不是说好一起同生共死吗?
3.1 什么是事务?
(1)事务(Transaction)是并发控制的基本单位
所谓的事务,它是一个操作序列,这些操作要么都执行,要么都不执行,它是一个不可分割的工作单位。 例如,银行转账工作:从一个账号扣款并使另一个账号增款,这两个操作要么都执行,要么都不执行。所以,应该把它们看成一个事务。事务是数据库维护数据一致性的单位,在每个事务结束时,都能保持数据一致性。
事务具有以下4个基本特征:简称ACID
● Atomic(原子性):事务中的所有元素作为一个整体提交或回滚,事务的个元素是不可分的,事务是一个完整操作。
● Consistency(一致性):事物完成时,数据必须是一致的,也就是说,和事物开始之前,数据存储中的数据处于一致状态。
● Isolation(隔离性):对数据进行修改的多个事务是彼此隔离的。这表明事务必须是独立的,不应该以任何方式以来于或影响其他事务。
● Durability(持久性):事务完成之后,它对于系统的影响是永久的,该修改即使出现系统故障也将一直保留,真实的修改了数据库。
(2)事务的语法步凑
• 开始事务:BEGIN TRANSACTION 开启事务
• 事务提交:COMMIT TRANSACTION --提交操作
• 事务回滚:ROLLBACK TRANSACTION --取消操作
3.2 为什么需要事务?
以最经典的转账情形为例,我们要从A账户转一笔钱到B账户,需要进行两部操作:第一步,从A账户扣除指定的金额数目;第二部,将B账户增加指定的金额数目;
update bank set balance=balance-1000 where cid='0001'
update bank set balance=balance + 1000 where cid='0002'
这里假设A账户有1000元,B账户有10元,并且银行约束每个账户必须保留10元最低存款额。这时,如果我们要从A账户转1000元到B账户的话,会在第一步从A账户扣除1000元时违反约束条件,从而出现错误,阻止了此次转账操作;但是,这并没有影响到第二步操作,于是B账户得到了天上掉下来的1000元。该怎么解决?我们可以将这两步放到一个操作序列里边,如果任何一步出现错误,都不会执行下一步操作,于是我们就可以用到事务了。
3.3 使用事务完成同生共死
这里使用事务解决刚刚的那个转账的问题,注意这里使用到了系统变量@@ERROR,但是@@ERROR只能判断当前一条T-SQL语句执行是否有错,为了判断事务中所有T-SQL语句是否有错,我们需要对错误进行累计,于是我们可以定义一个局部变量来记录整个操作序列期间的错误数。
begin transaction
declare @sumerror int = 0
update bank set balance=balance-1000 where cid='0001'
set @sumerror = @sumerror + @@ERROR
update bank set balance=balance + 1000 where cid='0002'
set @sumerror = @sumerror + @@ERROR
if(@sumerror = 0)
begin
commit transaction
end
else
begin
rollback transaction
end
四、存储过程:别以为你藏在数据库里我就不用你
4.1 什么是存储过程?
存储过程(Procedure)是一组为了完成特定功能的SQL语句集合,经编译后存储在数据库中,用户通过指定存储过程的名称并给出参数来执行。
存储过程中可以包含逻辑控制语句和数据操纵语句,它可以接受参数、输出参数、返回单个或多个结果集以及返回值。因此,我们可以简单的理解为:使用存储过程就像在数据库中运行方法。
4.2 存储过程的优点
(1)执行速度更快 – 在数据库中保存的存储过程SQL语句都是编译过的
(2)允许模块化程序设计 – 类似方法的复用
(3)提高系统安全性 – 防止SQL注入
(4)减少网络流量 – 只需要传输存储过程的名称
4.3 使用存储过程
(1)系统存储过程
由系统定义,存放在master数据库中,名称以“sp_”开头或”xp_”开头:
(2)自定义存储过程
自定义的存储过程可以以usp_开头,由用户在自己的数据库中创建的存储过程。
这里我们可以创建一个Account表的分页存储过程,看看怎么使用的吧:
create proc usp_GetPagedAccountData
@pageIndex int = 1,
@pageSize int = 10
as
begin
select *
from
(select ROW_NUMBER() OVER(order by Id) as rownum,* from Account) as t
where
t.rownum between @pageSize*(@pageIndex-1)+1 and @pageIndex*@pageSize
Order by t.Id
end
如何来执行存储过程呢?
exec usp_GetPagedAccountData @pageIndex=2,@pageSize=10
(3)使用输出参数
如果希望在使用存储过程后,将用户传递的某个参数输出改变后的结果,可以使用输出参数关键字:OUTPUT
具体的使用语法为:
declare @a int
exec usp_pp @canshu= @a output
print @a
declare @a int
exec usp_pp @canshu= @a output
print @a
这里我们看一个实例,加入有以下的一个存储过程,它接收用户传递过来的一个年龄,在Student表找出所有大于这个年龄的学生信息,并返回大于这个年龄的学生人数。
create proc usp_GetInfoByAge
@page int = 10,
@pcount int output
as
begin
select @pcount=COUNT(*) from Student where Sage>@page
select * from Student where Sage>@page
end
-- 下面是调用的代码以及返回结果
declare @outputCount int=0
exec usp_GetInfoByAge @page=18,@pcount=@outputCount output
select @outputCount
五、触发器:If you jump,I will jump too.
5.1 什么是触发器?
触发器(Trigger)是一种特殊类型的存储过程,它不同于之前的我们介绍的存储过程。触发器主要是通过事件进行触发被自动调用执行的。而存储过程可以通过存储过程的名称被调用。
触发器对表进行插入、更新、删除的时候会自动执行的特殊存储过程,它一般用在比check约束更加复杂的约束上面。
触发器和普通的存储过程的区别是:触发器是当对某一个表进行操作,诸如:update、insert、delete这些操作的时候,系统会自动调用执行该表上对应的触发器。
5.2 触发器的类型
(1)after/for 触发器(之后触发):insert触发器、update触发器、delete触发器
(2)instead of 触发器 (之前触发)
两种类型的区别是:After和for都是在增删改执行的时候执行另外的SQL语句,而Instead of 是使用另外的SQL语句取代原来的操作;
5.3 使用触发器
(1)触发器语法
CREATE TRIGGER triggerName ON 表名
after(for)(for与after都表示after触发器) | instead of
UPDATE|INSERT|DELETE(insert,update,delete)
AS
begin
…
end
(2)after触发器实例:
假如我们有一张成绩表Score{sId,cId,grade}和学生表Student{sId,sName,sAge},其中Score中的sId是Student中的主键,即Scroe中的sId为外键。那么,现在我们有这样一个需求:在每次向成绩表中添加新数据的时候,首先判断插入的学生学号是否存在于Student表中,如果存在则显示“插入成功”,如果不存在(也就是操作人员输入有误)那么则此次新增操作作废。
CREATE TRIGGER tgForScoreOnInsert ON Score
after INSERT -- 后置的新增触发器
AS
Begin
declare @stuid int,@courseid int--定义两个变量
select @stuid = sId,@courseid = cId from inserted--获得新增行的数据
if exists(select * from Student where sId=@stuid)--判断分数学员是否存在
print ‘插入成功’
else --如果不存在,则把更新增成功的分数记录给删除掉
delete from Score where sId = @stuid and cId = @courseid
End
(3)instead of触发器实例:
由instead of触发器的定义可以知道,instead of触发器表示并不执行其定义的操作(insert、update、delete)而仅是执行触发器本身的内容。
因此,借助instead of触发器的这个特点,我们可以看看这个场景:假如我们有一张借书记录表,图书馆规定每个学生最多只能借5本书,因此我们需要在添加借书记录时首先判断该生是否已经达到了最大的借书数量,如果达到了则提示“已达到借书最大限制,无法再继续借阅”,如果没有达到才会添加到记录表中。
create trigger tgInsteadOfRecordOnInsert on Record
INSTEAD OF INSERT
as
begin
--首先在记录表中查找,判断是否还能借
if exists(select 1 from inserted a join Record b on a.cardNo=b.cardNo group by a.cardNo having count(*)>=5)
--给出提示
print '已达到借书最大限制,无法再继续借阅!'
else
insert into Record select * from inserted
end
5.4 触发器使用建议
(1)尽量避免在触发器中执行耗时操作,因为触发器会与SQL语句认为在同一个事务中。(事务不结束,就无法释放锁。)
(2)避免在触发器中做复杂操作,影响触发器性能的因素比较多(如:产品版本、所使用架构等等),要想编写高效的触发器考虑因素比较多(编写触发器容易,编写复杂的高性能触发器难!)。
出处:http://edisonchou.cnblogs.com
转:SQL进阶之变量、事务、存储过程与触发器的更多相关文章
- 走向面试之数据库基础:三、SQL进阶之变量、事务、存储过程与触发器
一.变量那点事儿 1.1 局部变量 (1)声明局部变量 DECLARE @变量名 数据类型 ) DECLARE @id int (2)为变量赋值 SET @变量名 =值 --set用于普通的赋值 SE ...
- MS SQL Server之光标、存储过程和触发器
光标 通常数据库操作被认为是以数据集为基础的操作,但是光标被用于以记录为单位来进行操作,来获取数据库中的数据的子集.光标一般用于过程化程序里的嵌入的SQL语句. 对光标的定义如下: DECLARE C ...
- SQL学习(时间,存储过程,触发器)
SQL学习 几个操作时间的函数 --datapart 获取时间中的年月日时分秒等部分 select DATEPART(year,current_timestamp); select DATEPART( ...
- SQL Server查询数据库所有存储过程、触发器、索引信息SQL分享
1. 查询所有存储过程 1 select Pr_Name as [存储过程], [参数]=stuff((select ','+[Parameter] 2 from ( 3 select Pr.Name ...
- SQL进阶17-变量的声明/使用(输出)--全局变量/会话变量--用户变量/局部变量
/*进阶17 变量 系统变量: 全局变量: 会话变量: 自定义变量: 用户变量: 局部变量: */ /* #一: 系统变量 #说明: 变量由系统提供,不是用户定义的,属于服务器层面 #使用的语法 #1 ...
- MSSQL 事务,视图,索引,存储过程,触发器
事务 事务是一种机制.是一种操作序列,它包含了一组数据库操作命令,这组命令要么全部执行,要么全部不执行. 在数据库系统上执行并发操作时事务是作为最小的控制单元来使用的.这特别适用于多用户同时操作的数据 ...
- SQL Server 表变量和临时表的区别
SQL Server 表变量和临时表的区别 一.表变量 表变量在SQL Server 2000中首次被引入.表变量的具体定义包括列定义,列名,数据类型和约束.而在表变量中可以使用的约束包括主键约束,唯 ...
- SQL数据库之变量
--学习SQL数据库,变量是必须要掌握的概念,系统变量就是变量中最重要的变量之一,下面是SQL中系统变量的应用实例 use AdventureWorksDW exec sp_addtype 'char ...
- [转]SQL Server 表变量和临时表的区别
一.表变量 表变量在SQL Server 2000中首次被引入.表变量的具体定义包括列定义,列名,数据类型和约束.而在表变量中可以使用的约束包括主键约束,唯一约束,NULL约束和CHECK约束(外键约 ...
随机推荐
- js跨域解决方案
1.参考该文档:http://blog.csdn.net/enter89/article/details/51205752 2. 参考网络:http://www.ruanyifeng.com/blog ...
- 响应式框架Bootstrap
概念:Bootstrap将会根据你的屏幕的大小来调整HTML元素的大小 -- 强调 响应式设计的概念. 通过响应式设计,你无需再为你的网站设计一个手机版的.它在任何尺寸的屏幕上看起来都会不错. Boo ...
- 使用VIM将文件的其中的连续几行注释删除或者给其中的连续几行添加注释
一.使用VIM将文件的其中的连续几行注释删除 1.用VIM打开一个文件,比如打开sshd_config文件,以该文件的下面几行为例: #vim sshd_config 2.此时,按ctrl+v键,使 ...
- Filezilla Server日志文件
Filezilla Server版本:0.9.41. Filezilla Server日志文件在软件安装目录下Logs目录下. 但Filezilla Server默认不开始日志记录,如何开启filez ...
- 前端回答从输入URL到页面展示都经历了些什么
浏览器和服务器涉及大量网络通信内容,此处做了弱化介绍,作为前端主要关注第四部分.一. 网络环境保障我们先假定我们访问的URL为www.abc.com并且地址不在局域网内:首先我们所处的局域网的总路由应 ...
- 大型互联网公司Java开发岗位面试题归类!
一.Java基础 1. String类为什么是final的. 2. HashMap的源码,实现原理,底层结构. 3. 说说你知道的几个Java集合类:list.set.queue.map实现类咯.. ...
- 通过实例介绍Android App自动化测试框架--Unittest
1.为什么需要使用框架实现自动化测试 作为测试工程师,可能在代码能力上相比开发工程师要弱一点,所以我们在写脚本的时候就会相对容易的碰到更多的问题,如果有一个成熟的框架供给我们使用的话,可以帮助我们避免 ...
- C/C++语言的特点
一.支持数据封装和数据隐藏 在C++中,类是支持数据封装的工具,对象则是数据封装的实现.C++通过建立用户定义类支持数据封装和数据隐藏. 在面向对象的程序设计中,将数据和对该数据进行合法操作的函数封装 ...
- 微信小程序初识
http://lib.csdn.net/article/wechat/46742 微信小程序的前途和定位有什么疑惑?点进去 简单先记几个印象名词:流量入口,线下是重点,“即用即走”适合低频工具类产品. ...
- SQLSERVER中分割字符串成多列
今天修改到之前的大佬的代码,居然把多个Id存在一个列里面,还是用的逗号分割...特么查询的时候怎么办??? 网上搜索了半天,终于找到了SqlServer里面有一个PARSENAME函数,可以按.(点) ...