下面就T-SQL的几个方面来分别讲解一下。sql
一、变量数据库
要动态的写sql语句,就不能没有变量。编程
声明变量并赋值:c#
1 declare @i as int;--定义一个 int 类型的 变量 (as能够省略) 2 print @i;--这注意:没有赋值以前,程序不会报错,并且输出一个 空 3 set @i=3; 4 print @i;
在sql server 2008以后就能够对变量 在声明的同时进行赋值缓存
1 declare @a int=3; 2 print @a;
在变量的使用过程当中,必定要注意nvarcahr 和nchar的区别。安全
1 declare @s nvarchar(20); 2 set @s='Hello'; 3 set @s=@s+' World!'; 4 print @s;--输出的是 Hello World! 5 6 declare @s2 nchar(20); 7 set @s2='Hello'; 8 set @s2=@s2+' World!'; 9 print @s2;--输出的是 Hello。
为何使用nchar倒是输出的 Hello,由于:nchar是固定长度,即便长度没有达到最大,可是其他长度用 空来代替了,因此 至关因而满的,因此在进行字符串的相加 是不会起做用的。服务器
在查询中赋值:架构
1 declare @now datetime; 2 select @now=GETDATE(); 3 print @now; 4 declare @orderNum int; 5 select @orderNum = COUNT(1) from [Sales.Orders];--这条查询语句只是用来 对 变量进行赋值的,不会返回查询结果的. 6 print @orderNum;
主要做用:是将查询结果保存在 变量里,为了下面的使用。ide
其实也可使用 set 赋值的方式 实现 上面的做用函数
1 set @orderNum =(select COUNT(1) from [Sales.Orders]) 2 print @orderNum
二、批处理
使用“go”,go前面的 全部的 语句处于在一个 批里面。不一样批 的变量是不能互相调用。
三、流程控制
1)条件控制
1 declare @minute int ; 2 set @minute =DATEPART(minute,getdate()); 3 if @minute>20 4 begin--一条语句能够将 begin end 省略 5 print '立刻睡觉'; 6 end 7 else 8 print '继续快乐玩耍';
2)循环控制
1 --高斯问题 2 declare @sum int,@i int; 3 set @sum=0; 4 set @i=0; 5 while @i<100 6 begin 7 set @i=@i+1; 8 set @sum=@sum+@i; 9 end 10 print @sum;
在sql server 里面的 continue 和 break 和c#里面的 使用是相同的。
四、游标
什么是游标:
不是基于集合的操做,而是将集合中的数据逐条取出来,逐条进行操做。
何时使用游标:
默认的状况下是使用集合的方式 查询,若是要使用 游标 必须是在 能有让人信服的 理由下才考虑使用。
通常不使用游标的缘由:
一、使用游标,严重违背了关系模型,关系模型是基于集合考虑的。
二、逐条对记录进行操做会带来性能上的开销。给定一个集合,对集合进行一系列的游标代码的操做,必定会带来性能上的开销,而且这种使用游标的 方式 性能比 集合 慢了好几倍。
三、使用游标 要写不少代码。可是使用集合的,就只 把须要的数据查询出来,不描述怎么获取他们。游标写不少代码,可读性差,维护差。
存在的意义:
说了这么多的 弊端,那是游标还有存在的意义吗?固然:当须要对查询出来的数据 逐条进行 处理的 时候就要使用游标.
使用游标的步骤:
一、在查询中声明游标
二、打开游标
三、从第一个值开始将值赋值到对应的变量里面。
四、循环遍历游标,将赋值变量拿过来进行操做。
五、关闭游标
六、释放游标
1 --一、首先在查询基础上声明游标 2 declare c cursor 3 for 4 select shipperid,companyname 5 from [Sales.Shippers]; 6 7 --二、打开游标 8 open c; 9 10 --三、从第一个游标开始把 值 赋值到 对应的变量里面 11 declare @id int,@name nvarchar(20); 12 13 --每次取出来一条数据, 添加到指定的 变量。 14 fetch next from c into @id,@name; 15 16 --四、循环遍历 游标,将赋值的变量 拿过来进行相应的操做 17 while @@FETCH_STATUS=0--等于0表明 游标没有超过最后一行 18 begin 19 --相应的操做处理 20 print @name; 21 22 --尝试 读取下一条 数据 23 fetch next from c into @id,@name; 24 end 25 26 --五、关闭游标 27 close c; 28 29 --六、释放游标 30 deallocate c;
总结:
一、使用游标,要很是谨慎,由于性能消耗很大,不肯定的时候绝对不能使用
二、游标的惟一 好处:就是能够对查询数据,进行 逐条操做。这也正是它适应的场合。
五、临时表
使用场景:
当要将一些 数据 放到表里面 保存,可是又不想 建立一张数据表(由于通常 公司只有DBA 才有权限建立表), 或者我指向让当前 数据 只有当前 会话可见 ,甚至 只要当前 批 可见。
临时表的种类:
sql server三种临时表:局部临时表、全局临时表、表变量
下面对三种临时表进行分别讲解:
局部临时表:
一、建立过程和使用方式 都普通表是 同样,加上“#” 就表明是临时表.
二、只对 建立 他的 会话 是可见的,而且存储 在 系统数据库的 tempdb 数据的 临时表 里面。当前会话(进程)结束后,临时表会自动被删除
三、在 sql server 里 系统建立的临时表 都会加 后缀名,就是为了防止 不一样进程之间 建立相同的表名的 表名,保证惟一性。
普通建立临时表的方法:
1 create table #partTable 2 ( 3 num int 4 ); 5 6 insert into #partTable (num) values (1),(2),(3); 7 go;--再也不 一个 批 里面 也能使用 ,只要是在同一个 进程里面 8 select * from #partTable;
在查询过程当中建立临时表,并将查询出的数据插入到临时表里面
1 select * into #table from [Sales.Shippers] 2 select * from #table;
全局临时表:
一、在 表名 前面 添加两个 “#” 表明是 全局临时表
二、注意:对全部的 会话(进程) 都是可见的, 可是 当前进程 若是关闭或者 全局临时表 长时间没有被使用,那么就会被删除
1 create table ##allTable 2 ( 3 num int 4 ); 5 insert into ##allTable (num) values(1),(2),(3),(4); 6 select * from ##allTable
表变量:
注意:它也会在 tempdb数据库 里面建立一个 对应物理 临时表,只不过,他只对当前操做他的"批"可见,并且 当前 批 执行完成 以后就会 删除 临时表.(因此必定要注意:表 变量 并非 存在内存中,他也会 建立一张物理数据表)
性能考虑:当 只有几行数据的时候,固然是 表变量 性能好;;可是若是是大量数据,应该使用临时表
1 declare @tableVariable table 2 ( 3 num int 4 ) 5 6 insert into @tableVariable (num) values (1),(2),(3); 7 select * from @tableVariable; 8 go; 9 --再也不同一 批 里面是不能访问到 表 变量 10 select * from @tableVariable;
最重要的一点:表变量同变量同样,当事务回滚以后,变量的值不会回滚。同理表变量也不会回滚。
1 --回滚中 变量的不会回滚的 特殊状况 2 declare @num int; 3 set @num =1; 4 begin transaction; 5 set @num=12; 6 print @num; 7 rollback; 8 --注意:事务回滚,若是变量在 事务里面 改变,回滚的时候 变量是不会回滚的. 9 print @num; 10 11 --同理:表变量也是如此的 12 declare @tableVariable2 table 13 ( 14 num int 15 ); 16 insert into @tableVariable2 (num) values(1),(2),(3); 17 begin transaction; 18 delete from @tableVariable2 where num =1; 19 rollback; 20 --表变量是不会 回滚的 21 select * from @tableVariable2;
六、动态sql
什么是动态sql
首先静态sql就是普通的静态查询语句。
动态sql:就是使用 exec来执行字符串sql语句。
1 declare @sql nvarchar(100); 2 set @sql ='select * from [Sales.Shippers]'; 3 exec(@sql)
缺点:不能防止sql注入漏洞攻击。(什么是sql注入漏洞你们应该都懂的吧。就不作介绍了)
为解决 上面的 sql注入漏洞攻击 因此又出现了 第二种动态sql :sp_executesql存储过程:
一、安全,由于他 支持 输入和输出参数的设置
二、性能比 exec 要好:由于它的 参数化 有助于 重用 缓存过 的执行计划. 执行计划:就是sql server 处理 sql 语句时 生成的指令. 若是 要想要 复用 缓存 中执行计划.必须保证 sql 字符串语句 相同.因此要 由于 使用 参数化 的sql语句 只要替换 参数就能够,因此 sql语句 不变化 能够复用.
1 declare @sql nvarchar(100); 2 set @sql='select * from [Sales.Shippers] where companyname=@name'; 3 declare @name nvarchar(20); 4 --set @name='顺丰'; 5 set @name='顺丰;select * from [Sales.Shippers]';--即便这样,想要进行sql 注入漏洞攻击,不可能,由于 在sql 语句 把整个 @name里面的 值 做为一个 字符串 来使用的,就是执行 companyname 和 整个字符串的对比 6 7 exec sp_executesql 8 --下面两个是很是重要的 9 @stmt=@sql,--动态执行的 sql语句 10 @params=N'@name as nvarchar(20)',--参数的类型 11 @name=@name;--参数赋值
七、例程
例程是什么:
为了 计算结果 或者 执行任务 而 封装的代码 的一种编程现象.提到例程,你们可能不知道,可是提到下面的他的三个种类,就全都知道了。
例程的种类:
用户自定义函数、存储过程、触发器
最经常使用的是存储过程,下面先对存储过程进行介绍。
存储过程:
建立存储过程:
1 --存储过程:最经常使用的方法 2 create procedure MyDemoPro 3 ( 4 --存储过程当中 要使用到 的参数 5 @orderid int 6 ) 7 as 8 --下面是执行的 sql 语句 9 select * from [Sales.Orders] where orderid=@orderid;
执行存储过程:
exec MyDemoPro @orderid=10; --能够简写成:exec MyDemoPro 10;
要搞懂存储过程,就必须搞懂他的三个参数类型:
传入参数、传出参数、return参数。
传入参数:
就是普通的参数;上面使用的那中就是 参入参数
传出参数:
output 定义的参数:能够 传出 供用户使用的
1 create procedure OrderCount 2 ( 3 @count int output 4 ) 5 as 6 select @count=COUNT(*) from [Sales.Orders]; 7 go; 8 9 --执行 ,必定以声明一个变量 ,赋值给 传出参数 10 declare @outCount int ; 11 exec OrderCount @count=@outCount output; 12 print @outCount;
return参数:
特殊的参数:和 c#里面的不同,这里只用来,表示 操做结果的正确或错误,只能返回数字
1 alter procedure ReturnProc 2 ( 3 @username nvarchar(100) 4 ) 5 as 6 declare @usernameLen int; 7 set @usernameLen=LEN(@username); 8 9 if @usernameLen>=5 10 return '1'; 11 else 12 return '0'; 13 14 15 declare @result int; 16 --如何为 return 参数 赋值 17 exec @result = ReturnProc @username='wanglihong'; 18 print @result;
若是将一个返回参数设置成'asd',就会报错以下:
自定义函数:
一、能够直接返回一个值
二、分两种:
标量函数(返回值为一个 值)
表函数(返回值是一张表)(存在与 可编程性 里面的函数里面)
三、实际开发中不多使用。
1 create function GetMinute 2 ( 3 @date datetime 4 ) 5 --设置返回值: 6 returns int 7 as 8 begin 9 declare @minute int; 10 set @minute =datepart(minute,@date); 11 return @minute; 12 end 13 14 --使用自定义函数 15 select dbo.GetMinute(GETDATE());
触发器:
特殊的存储过程。主要做用:检索。注意:触发器必须依附于事件,只有当事件发生时候,出发触发器,运行触发器代码。(sql server 中和 触发器相对应两个事件:数据操做事件和数据定义事件,从而对应下面的连个触发器)
种类分为两种:
DML触发器(修改触发器:对表的数据修改:如:update等)
DLL触发器(架构触发器:对数据库的架构进行修改:如建立表)
DML触发器:
分为两种:
after触发器(对表操做)
instead of 触发器(对视图进行操做)
注意:在触发器的代码里,只能访问到 inserted 和deleted 两张表.对数据进行更新的是 先删除而后在插入执行的。 inserted表包含 insert 和update操做 以后新数据的行。deleted表含有 delete和update 操做 以后旧数据的 行。
对于after触发器是常用,因此这里只对 after作介绍:
与之关联的事件执行完成以后才出发after触发器。
为shipper(货运公司)表建立一张日志表:
1 create table Ship_Log 2 ( 3 id int identity(1,1) primary key, 4 op_date datetime default getdate(), 5 op_uaer nvarchar(50), 6 op nvarchar(50), 7 shipname nvarchar(50), 8 shipphone nvarchar(50) 9 )
为表 dbo.Sales.Shipper建立触发器
1 create trigger ship_log_trigger 2 on [Sales.Shippers] after insert 3 as 4 --当对上面的表进行 增删改的时候执行 触发器的下面的代码 5 insert into Ship_Log (op_uaer,op,shipname,shipphone) 6 select user_name()--返回当前操做的用户名 7 ,'insert',companyname,phone from inserted;
向表 dbo.Sales.Shipper 中插入数据触发触发器:
1 insert into [Sales.Shippers] (companyname,phone) values('shits','12345678')
查询日志表:
select * from Ship_Log;
查询结果:
已经将日志插入进去了。
DLL触发器
通常用不到。
分为两种:
对数据库的触发(例如:建立表)
对服务器的触发(例如:建立数据库)
八、标识
标识:就是有时咱们会将主键设置为标识列(自动增长列),而后查询标识的时候就是查到最新增长的标识列的值。
分为两种:
一、全局范围的:@@identity
二、当前 表 范围的 :SCOPE_IDENTITY();最经常使用
注意:若是想一张表含有触发器的表中插入数据的话,查询到的结果就是不一样(由于向表中插入数据以后,触发器还会再向日志表中插入数据,因此全局标识查到的是日志表中的标识,而 SCOPE_IDENTITY()查到的 插入数据表里的 标识)
1 insert into [Sales.Shippers] (companyname,phone) values('asd','12345'); 2 select @@identity;--整个数据库中全部的 最新最新增长的 标识列 3 4 select SCOPE_IDENTITY();--得到 当前操做的表的最新增长的 标识列的值
若是想没有触发器的 表 插入数据,两个就查询的标识列的值相同