首页 > 代码库 > SQL Server的高级知识
SQL Server的高级知识
DataSet的内容介绍,两种单例模式(并发量的考虑),SQL高级中的case语句、连接操作、子查询、派生表
————————————————————————————————
1.Case的用法
使用方法一:(类似C#中的case的用法)
->语法:
Case 表达式
when 值1 then 返回值
when 值2 then 返回值
…
End
->案例:把用户表中数据,全部查询出来,要求把DelFlag=0的显示成未删除
select *,case DelFlag
when 0 then N‘未删除‘
when 1 then N‘删除‘
end as 删除状态
,UserName
from
UserInfo
使用方法二:(类似C#中的多个if else)
->语法
Case
when 表达式 then 返回值
when 表达式 then 返回值
else 值
->案例:把用户表中数据全部查询出来,要求把DelFlag=0的显示成删除
select *,case
when DelFlag=0 then N‘未删除‘
when DelFlag=1 then N‘删除‘
else N‘未知‘
end as 删除状态
,UserName
from
UserInfo
2.补充:ISNULL(表达式,替换的值)
select CreateDate,ISNULL(CreateDate,getdate()) from UserInfo
3.SQL控制语句
->SQL中的变量
->定义变量:
->declare @变量名 变量类型
->给变量赋值:
->set @参数名=值
->select @参数名=值
select @i=count(1) from UserInfo;
->打印 : Print @变量名
->IF ELSE
->语法格式:
if(表达式)
begin
SQL语句
end
else
begin
语句
end
->WHILE
->语法:
While(表达式)
begin
SQL语句
end
4.数据的连接
主键不参与业务逻辑
->交叉连接
一张表中有M条数据,另一张表中有N条记录,那么交叉连接后就是M*N
->内连接
一张表中有M条数据,另一张表中有N条记录,那么内连接后的数据不确定条数,要根据外键去讨论
->外连接
select * from 表1 as t1 right jion 表2 as t2 on t1.列名=t2.列名;
表示将表1和表2通过“列名”作为对应关系进行连接,如果有数据没有相关对应,就看中间的连接方式right/left/full,如果是right就以右侧的数据为主,如果是left就以左侧的数据为主,如果是full就全部都显示,没有对应就补null;
5.子查询
需要将一个一个查询的结果作为另一个查询的条件
通常,将里面的查询叫做子查询,外面的查询叫做外部查询
注意:子查询内部 必须查询一个数据
返回结果
单值(一个单元格) 标量子查询(常常放在where子句中作为条件,或者在select中作为一个值)
多值 多值子查询
行(略)
列 一般放在in中作为批量删除、修改等操作或查询
表 一般作为数据源进行再一次检索
6.表表达式
->派生表
select top 30
t1.stuId as 编号
, t1.stuName as 姓名
, case t1.stuSex when ‘m‘ then ‘男‘ else ‘女‘ end as 性别
, t3.final as 期末总评
, t2.className as 课程
, t1.stuPhone as 电话
, t1.stuAddress as 家庭住址
from
TestDataBase..Student as t1
inner join
TestDataBase..Course as t2
on t1.classId = t2.classId
inner join
(select
stuId
, avg(testBase+testBeyond+testPro) as final
from
TestDataBase..Score
group by
stuId) as t3
on t1.stuId = t3.stuId
order by
t3.final desc;
其中,将结果集作为一张表来使用的应用就叫做派生表
实际上这张表是不存在的,
汇编语言:机器型语言
高级语言:描述性语言
第三种语言:任务型语言
表表达式的意思是表参与运算得到的结果还是表,常用的表表达式:
->派生表
->分页(重要)
引出:淘宝京东购物网站,第一页显示不了 ,会分页显示
select * from Student;
一共有6万条数据,一次显示会崩溃,所以分页显示
问题:每页有M条记录,要显示第N页,如何实现?
其实不好写,凡是遇到这种有规律性问题的时候,你需要给自己设置具体的数字,按照数字的规律先写个三遍、四遍的结果,从中找到结果;
每页10条,第1页
select top 10 * from TestDabase..Student;
每页10条,第2页
select * from TestDatabase..Student where stuId between 11 and 20;
每页10条,第3页
select * from TestDatabase..Student where stuId between 21 and 30;
……
规律:
每页M条,第N页
select * from TestDatabase..Student
where stuId between M*(N-1) and M*N;
如果不连续,那么可以考虑加一个列,让其连续
--row_number() over(order by 字段)
->公用表表达式(CTE)
-- 公用表表达式(CTE, common table expression)
-- 语法
/*
with 别名
as
(
结果集
)
紧跟查询
*/
with t
as
(
select
row_number() over(order by stuid) as num
, stuId
, stuName
, stuSex
, stuBirthdate
, stuStudydate
, stuAddress
, stuEmail
, stuPhone
, stuIsDel
, stuInputtime
, classId
from
TestDataBase..Student
where
stuIsDel = 0
)
select -- 紧跟查询
num
, stuId
, stuName
, stuSex
, stuBirthdate
, stuStudydate
, stuAddress
, stuEmail
, stuPhone
, stuIsDel
, stuInputtime
, classId
from t
where t.num between (10-1)*9+1 and 9*10;
->视图(View)
可持久化的派生表
create view vw_FenYeable --视图名需要以vw开头
因为必须是批处理执行的语句 ,所以需要在首尾加go
go
create view vw_FenYeable
as
select
row_number() over(order by stuid) as num
, stuId
, stuName
, stuSex
, stuBirthdate
, stuStudydate
, stuAddress
, stuEmail
, stuPhone
, stuIsDel
, stuInputtime
, classId
from
TestDataBase..Student
where
stuIsDel = 0
go
select * from vw_FenYeable where vw_FenYeable.num between 9 * 10 + 1 and 10 * 10;
--视图无法存储数据,本质上还是查询,目的是更好的帮助我们进行查询。如果每次查询一个复杂操作的时候,选择使用视图
->内联表值函数(*不做要求)
-- 就是带有参数的“视图”
-- 语法
/*
create function fn_函数名
(@参数名 as 类型, @参数名 as 类型, ...)
returns table
as
return
结果集
*/
定义时:
go
create function fn_FenYe
(@pageCount as int, @pageIndex as int)
returns table
as
return
select
num
, stuId
, stuName
, stuSex
, stuBirthdate
, stuStudydate
, stuAddress
, stuEmail
, stuPhone
, stuIsDel
, stuInputtime
, classId
from vw_FenYeable
where
vw_FenYeable.num
between (@pageIndex - 1) * @pageCount + 1 and @pageCount * @pageIndex;
go
使用时:
select * from fn_FenYe(20, 9);
-- 将结果集进行运算,得到一个新的结果集,并将其作为数据源进行查询
7.表的透视变换
将原始给的横表显示成竖排方式:
select
学号
, sum(case when 课程=‘语文‘ then 成绩 else 0 end) as ‘语文‘
, sum(case when 课程=‘数学‘ then 成绩 else 0 end) as ‘数学‘
, sum(case when 课程=‘英语‘ then 成绩 else 0 end) as ‘英语‘
from
Score
group by 学号
8.事务
在SQL Server中,默认的每一条语句都是一个事务 。
begin transaction
事务内容
->生效
commit transaction
->不生效
rollback transaction
如何知道事务是否生效?
@@rowcount 记录上条语句受影响的行数
@@error 如果有错记录错误编码
事务:一个整体,要么全部执行成功,要么全部执行失败
严格的定义:如果一个操作满足原子性、持久性、隔离性与一致性,那么这个操作称为一个事务
原子性:不可划分,要么全部成功要么全部失败
持久性:一旦事务执行完成,不可销毁
隔离性:如果事务在执行操作时还未完成,另外一个事务需要执行这个操作相关时需要等待(阻塞)
一致性:一旦事务完成,不管在文件中、数据文件中、内存中的所有数据都是一样的
如果严格遵循事务的操作,会很影响性能但是提高安全性
->隔离性的实现机制:锁
数据文件分块存储 文件存储模型:文件块
颗粒度:是锁定一行数据还是锁定一页数据就叫做颗粒度
共享锁(读)、排他锁(增删改)
->事务隔离级别
未提交读、已提交读、可重复读、序列化读、快照
9.存储过程 procedure
将SQL语句封装起来,就像C#中的方法一样
意义:将一个执行的过程(可能会很复杂)封装成一个名字,然后使用这个名字就可以执行这个过程
语法:
create peoc[edure] 存储过程名字
参数 as 类型 [默认值|output] --此处的as可以省略
as
begin
代码
end
e.g.
go
create proc usp_test1
as
begin
begin transaction
declare @myError int;
update bank set balance=balance - 900 where cid=‘0002‘
set @myError = (select @@ERROR);
update bank set balance=balance + 900 where cid=‘0001‘
set @myError += (select @@ERROR);
if(@myError = 0)
begin
commit
end
else
begin
rollback
end
end
go
->执行存储过程
exec usp_test1;
->带参数的存储过程
go
create proc usp_test2
@from as char(4)
, @to as char(4)
, @money as money
as
begin
begin transaction
declare @myError int;
update bank set balance=balance - @money where cid=@from
set @myError = (select @@ERROR);
update bank set balance=balance + @money where cid=@to
set @myError += (select @@ERROR);
if(@myError = 0)
begin
commit
end
else
begin
rollback
end
end
go
->执行
exec usp_test2 ‘0001‘, ‘0002‘, -900;
exec usp_test2 @to=‘0002‘, @money=100, @from=‘0001‘;
->上述执行是否成功不能看到,需要有返回值的存储过程
go
create proc usp_test3
@from as char(4)
, @to as char(4)
, @money as money
, @isSuccess int output -- 与C#一模一样,在存储过程内部赋值即可
as
begin
begin transaction
declare @myError int;
update bank set balance=balance - @money where cid=@from
set @myError = (select @@ERROR);
update bank set balance=balance + @money where cid=@to
set @myError += (select @@ERROR);
if(@myError = 0)
begin
commit
set @isSuccess = 1;
end
else
begin
rollback
set @isSuccess = 0;
end
end
go
->执行
但是这里虽然能显示我们的结果,但是如果出错结果为0但是还是会报错,怎么才能让它不报错呢?
->在SQL Server中的Try-catch
go
create proc usp_test4
@from as char(4)
, @to as char(4)
, @money as money
, @isSuccess int outputas
begin
begin transaction
begin try
update bank set balance=balance - @money where cid=@from
update bank set balance=balance + @money where cid=@to
commit
set @isSuccess = 1;
end try
begin catch
rollback
set @isSuccess = 0;
end catch
end
go
存储过程是为了用一个名字代替一串Sql语句,传输会快,解析不需要也会快
所以不管是大公司还是小公司都会用存储过程,但是也有的公司会要求全部用C#的逻辑来写
->系统存储过程
exec sp_help;
->默认参数的存储过程
go
create proc usp_FenYe1
@pageIndex int = 1 -- 第几页
,@pageCount int = 10-- 每页条数
as
begin
select * from fn_FenYe(@pageCount, @pageIndex);
end
go
10.触发器
现在来讲,用触发器的机会还不多
触发器是什么?
是一个特殊的存储过程,如果用C#来比喻的话本质还是方法
不是自己调用,而是因为执行某一个错做二自动的触发,就像事件
使用
->监视操作
(可以记录操作的时间、人物……)
->补充约束
(约束只能在一张表里,如果跨越多张表还要对字段进行约束的话可以考虑进行约束)
->语法
create trigger tr_类型_触发器的名字 on 表名
触发类型:after(之前)|instead of(代替,一般不用)
操作类型:insert|delete|update
as
begin
代码
end
两张临时表
inserted和deleted
11.逻辑与流程控制
-- 逻辑处理与流程控制
-- 选择结构
/*
if 表达式
begin
end
else if 表达式
begin
end
else
begin
end
*/
-- 循环结构
/*
while 表达式
begin
end
*/
-- 定义变量
declare @num int;
-- 为变量赋值
set @num = 123;
-- 使用
select @num;
-- 求1到100的和
declare @sum int;
declare @i int;
set @sum=0;
set @i = 0;
while @i <= 100
begin
set @sum = @sum + @i;
set @i = @i + 1;
end
select @sum;
go
declare @sum int;
declare @i int;
set @sum=0;
set @i = 0;
while @i <= 100
begin
if @i % 2 = 0
begin
set @sum = @sum + @i;
end
set @i = @i + 1;
end
select @sum;
go
-- 系统变量常常使用@@来引导
select @@VERSION
select @@ERROR;
SQL Server的高级知识