SQLServer 操作详解(没事多翻翻,发觉开发中用到的就是这些)
来自:http://www.voidcn.com/article/p-fvqxvkey-qb.html ? --################################################################################### /* 缩写: DDL(Database Definition Language): 数据库定义语言 DML(Database Manipulation Language): 数据库操作语言 DCL(Database Control Language): 数据库控制语言 DTM(Database Trasaction Management): 数据库事物管理 知识概要: |---1.查询Select | |---2.数据库定义语言DDL: 对表,视图等的操作,包括create,drop,alter,rename,truncate | 数据库操作--|---3.数据库操作语言DML: 对记录进行的操作,包括insert,delete,update | |---2.数据库控制语言DCL: 对访问权限等的操作,包括grant,revoke | |---2.数据库事物管理DTM: 对事物的操作,包括commit,rollback,savepoint 事物的是由DML(insert,update)开启的; 而引起事物的提交或结束原因有: 1.DTM操作: commit,savepoint 2.系统崩溃宕机: 隐式rollback 3.正常: 隐式commit 4.DDL和DCL操作: DDL(create,truncate) DCL(grant,revoke) 注意MS-Sql的特殊处: MS-Sql中的事物: 自动事物(commit)和手动事物(begin transaction). 在Sql中DML(insert,update)会引起自动事物commit,而Oracle中不会 MS-Sql的参数: 只能返回0或非0(如: 1,2)数字 MS-Sql的存储过程: 一定会有返回值(return value),默认返回0(成功). 在程序获取改值,需要创建return参数对象(添加到参数列表),并设定其方向. MSSqlServer的登录过程分两种: 1. Windows集成验证: windows用户名和口令 -> SqlServer登录列表 -> 映射到用户列表 -> 登录成功 2. SqlServer验证: Sql用户名和口令 -> 映射到用户列表 -> 登录成功 两种登录方式的连接串: string connectionStr = "data source=.;database=Test;user id=sa;password=sa"; string connectiongStr ="data source=.sqlexpress;database=Test;integrated security=true"; 数据库设计原则: 1. 数据库设计指导原则(关系数据库中的数据通过表来体现): 先确定表后确定业务字段. 每个业务对象在数据库中对应一张表(若业务对象复杂则可对应多张表),业务对象间每有一个关系也要对应一张表. 注意: 业务字段需要跟项目结合,例如: 学生的健康情况可以用一个字段(优、良等)表示,但针对健康普查,学生的健康情况需进一步划分为身高、体重、血压等 如: 学校中的学生对象: 学院表(学院ID,专业ID); 专业表: 专业表(专业ID,专业名);学生表(学生ID,学院ID,专业ID) 2. 数据库设三大计原则: a. 完整性: 设计方案能够保存项目中的各种信息(要全) b. 低冗余: 通过主键保证记录的不重复、通过表间关系减少冗余字段 c. 尽可能满足3范式(NormalForm): 1NF: 1个字段只能包含一个业务信息片(即项目中的业务字段表示的信息不可再分) 2NF: 能找到1个或多个字段的组合,用来唯一的确定表中的记录(即必须有主键). 3NF: 主键唯一且直接确定表中的其他字段(即无传递依赖,如: 教师id,办公室id,办公室电话关系中存在传递依赖) 注意事项: 尽可能不用业务字段做主键,通常的手段为自增列当主键,并且末尾添加默认时间字段(getdate()). 尽量采用SQL92代码,保证可移植性. 如: 在Sql2000中,top函数只能跟常量(Sql2005可跟变量). 通用的解法为拼Query串,用exec(query串)取结果 备注: Sql中使用+拼接字符串,Oracle中使用||拼接字符串. C#数据类型: 整数: sbyte,byte,short,ushort,int,uint,long,ulong 实数: float,double,decimal 字符: char,string 布尔: boolean 日期: datetime 对象: object 全局唯一标识: GUID Sql数据类型: 整数: bit(0,1),tinyint(8),smallint(16),int(32),bigint(64) 实数: float,real,numeric 字符: char(8000),nchar(4000),varchar(8000),nvarchar(4000),ntext 日期: smalldatetime(1900,1,1-2079,6,6),datetime(1753,1-9999,12,31) 货比: money 二进制: binary 图像: image 标识: uniqueidentity */ --################################################################################### --创建数据库: if exists(select * from sysdatabases where [name]='TestStudent') drop database TestStudent go create database TestStudent on ( name = 'TestStudent_DB',--数据库逻辑名 filename = 'D:WorkCodeDBTestStudent.mdf',size = 3,filegrowth = 10,maxsize = 100 ) log on ( name = 'TestStudent_Log',filename = 'D:WorkCodeDBTestStudent.log',maxsize = 100 ) go --################################################################################### use TestStudent go --创建表,约束类型: 主键、外键、唯一、check约束、非空约束 if exists( select * from sysobjects where [name] = 'T_Student' and [type] = 'U') drop table T_Student go create table T_Student ( Sno int identity(100,1) primary key,--可以使用scope_identity获得刚生成的id Sname nvarchar(50) not null,Sgender nchar(1),Sage tinyint check(Sage >= 20 and Sage <= 30),home nvarchar(100) default('北京'),idcard nvarchar(18) unique ) go if exists( select * from sysobjects where [name] = 'T_Score' and [type] = 'U') drop table T_Score go create table T_Score ( id int primary key,Sno int identity(100,1) foreign key references T_Student(Sno),Score tinyint ) go --修改表结构 alter table T_Student add Education nchar(3) go alter table T_Student drop column Education go --修改表约束 alter table T_Student add constraint PK_Sno primary key(Sno),constraint CK_gender check(gender in('男','女')),constraint DF_home default('北京') for home,constraint UQ_idcard unique(idcard) go alter table T_Score add constraint FK_Sno foreign key references T_Student(Sno) go --创建联合主键 alter table T_SC with nocheck add constraint [PK_T_SC] primary key nonclustered( studentId,courseId ) go --################################################################################### --新增(插入)数据 insert into T_Student(Sname,Sgender,Sage) values('张三','男',23) go insert into T_Student(Sname,Sage) select '李四',25 union select '王五','女',26 union select '赵六',28 go --删除数据 truncate table T_Student --只删除表的数据 delete from T_Student where sgender = '男' --修改数据 update T_Student set sgender = '女' where sgender='男' --################################################################################### --查询数据 select * from T_Student where sgender = '女' select top 3 * from T_Student --选择前三项,top 3是用来筛选记录的,所以得紧跟select,不用考虑字段顺序 select top 40 percent * from T_Student --选择前百分之几的记录 select sname as '姓名','年龄' = sage from T_Student --起别名的两种方式as和= select * from T_Student where sage >= 20 and age <= 30 select * from T_Student where sage between 20 and 30 select * from T_Student where sage is null select sname into T_Stu from T_StuInfo --用T_StuInfo中的sname字段创建新表 --模糊查询呢: 通配符: %(0-任意字符),_(1个字符),[abc](选择含a或b或c),[^abc](选择不含a或b或c) select * from T_Student where sname like '张%' select * from T_Student where sname like '王_' select * from T_Student where sname like '[张李]%' select * from T_Student where sname like '[^张李]%' --################################################################################### --排序: order by子句的结果将额外生成一个新表(2字段: 原位置索引字段和排好序的字段) select * from T_Student order by Sage desc --默认是升序asc --################################################################################### --聚合函数: 若有聚合函数,即使不写group by 子句,也会默认将表分为一个大组 select sum([sid]) from T_StuScore select count([sid]) from T_StuScore --count(*)表示记录数,而count(字段)忽略掉null值 select avg([sid]) from T_StuScore select max([sid]) from T_StuScore select min([sid]) from T_StuScore select distinct([sid]) from T_StuScore --################################################################################### --分组函数,where用于对记录的筛选,having用于对组的筛选 select gender,Counter = count(*) from T_Stuinfo group by gender select gender,Counter = count(*) from T_Stuinfo group by gender having count(*) >= 2 --################################################################################### --表连接: 笛卡尔积(m*n条记录),内连接,外连接(左外连接、右外连接、全外连接) --笛卡尔积 select sname,[sid],cid,score from T_StuInfo s cross join T_StuScore c on s.[sid] = c.[sid] --内连接: 先从m和n中选择,然后再连接 select sname,score from T_StuInfo s inner join T_StuScore c on s.[sid] = c.[sid] --左外连接(左连接): 内连接 + 左表剩余记录(右表记录置null) select sname,score from T_StuInfo s left join T_StuScore c on s.[sid] = c.[sid] --右外连接(右连接): 内连接 + 右表剩余记录(左表记录置null) select sname,score from T_StuInfo s right join T_StuScore c on s.[sid] = c.[sid] --全外连接(全连接): 内连接 + 左表剩余记录(右表记录置null) + 右表剩余记录(左表记录置null) select sname,score from T_StuInfo s full outer join T_StuScore c on s.[sid] = c.[sid] --################################################################################### --函数: 字符串函数、日期函数、数学函数、系统函数 --字符串函数 print charindex('1','ab1cd') --Sql中下表从1开始,类似于C#中indexof、lastindexof,返回包含'1'的下表位置 print len('abcd') --C#中的length print lower('ABCD') --ToLower(),ToUpper() print upper('abcd') --ToUpper() print ltrim(' abcd') --LTrim() print rtrim('abcd ') --RTrim() print rtrim(ltrim(' abcd ')) --Trim() print right('abcd',2) --从右截取,C#中的SubString(length-3,2)截取下表从0开始的2个 print left('abcd',2) --SubString(0,2) print replace('abcdef','cd','1234') --Replace(),用1234替换cd update [card] set [passWord] = Replace(Replace([PassWord],'O','0'),'i','1') from T_UserInfo print stuff('abcdef',2,3,'#') --填充替换,从第2个开始的3个字符替换成# print cast('2010-11-08' as datetime) --数据类型转换 print convert(datetime,'2010-11-08') --数据类型转换 print str(67) --数字变字符串 print newid() --新uniqueidentifier,它将会为记录临时添加一列,且内容是随机的 print getdate() --获得当前日期 --日期函数 print getdate() --DateTime.Now print dateadd(yyyy,10,'2010-1-2') --增加10年 print dateadd(mm,getdate()) --增加月,其他可以查sql联机丛书 print datediff(yyyy,'1985-12-13','2010-10-10') --时间差距 print datediff(dd,'2010-10-10') --时间差距 print datename(mm,'2010-10-01') + '2' --返回月份+'2' print datepart(mm,'2010-10-01') + '2' --日期10+2,结果为12 --数学函数 print abs(-100) --求绝对值,Abs() print ceiling(10.05) --取整数,如果有小数则进1 print floor(10.05) --取整数,忽略小数 print round(10.567,2) --四舍五入 print power(10,2) --求幂运算 print sqrt(108) --求平方根 print rand(10) --只能选择0-1之间的数,Random.Ran(0,1) print rand(10)*10 --随机0-10之间的数 print sign(30) --只返回±1 --系统函数 print 'abcd' + convert(nvarchar(10),5) --ToString() print 'abcd' + cast(5 as nvarchar(10)) --同上 print datalength('1+1=2') --返回表达式的字节数 print current_user --返回当前登录的角色 print host_name() --返回当前计算机名 print system_user --返回当前用户id print user_name() --给定用户id返回其角色名 print isnull(filedname,0) --替换null为0 raiserror('抛出异常',16,1) --抛出异常,1-15被系统占用,对应C#中的throw select * from sysobjects where objectproperty(id,N'IsUserTable') = 1 --判断是否用户表(y=1,n=0),N表示后边的串为unicode字符串. select * from sysobjects where type= 'U' --等价于上一行 select databasepropertyex('Northwind','IsBrokerEnabled') --查询该库是否开启缓存技术中的通知机制,1为开启,0为关闭 alter database northwind set enable_broker --开启数据库中,缓存技术中的通知机制 --注意以下三个函数的用法 eg: 结果集(1,5,11,17,19,25) select row_number() over(order by [sid]) from T_StuInfo --1,4,6 select rank() over(order by [sid]) from T_StuInfo --1,6 select dense_rank() over(order by [sid]) from T_StuInfo --1,4 select ntile(2) over(order by [sid]) from T_StuInfo --1,5 11,17 19,25 select row_number() over(order by [sid]) as sequence,sname,age,(case gender when '0' then '男' else '女' end) gender from T_StuInfo s left join T_StuScore c on s.sid = c.sid go --################################################################################### --范式: 1NF: 原子性,2NF: 单主键,3NF: 去除传递依赖 --E-R模型(表间关系): 1对1: 任何一个表添加外键; 1对多: 在多的一方添加外键; 多对多: 需要第三个表,添加前两表的外键 --################################################################################### --变量 --系统变量: select * from T_StuInfo print @@identity; --获得结果集最大标识值 print @@error; --有错误,返回大于0的数; 无错误返回0 print @@rowcount; --返回结果集的行数 --自定义变量 declare @age tinyint declare @age2 tinyint declare @name nvarchar(20) declare @name2 nvarchar(20) set @age = 15 --一次只能对一个量变赋值 select @name = '张三',@name2 = '李四' --一次性给多个变量赋值 select @age2 = max(age) from T_StuInfo --可以用在查询语句中 print @age print @age2 print @name print @name2 --################################################################################### --条件表达式 declare @age int set @age = 1 if (@age <20) begin set @age = @age + 1 end else begin set @age = @age - 1 end --循环 declare @index int declare @sum int set @index = 1 set @sum = 0 while (@index < 11) begin set @sum = @sum + @index set @index = @index + 1 end print @sum --批处理Sql语句: 练习---打印三角形,即成批处理语句+go即可,只访问一次数据库 declare @row int declare @col int declare @n int --总行数 declare @result nvarchar(2000) set @row = 0 set @col = 0 set @n = 10 --可以修改n的值 set @result = '' while (@row < @n) begin set @col = 0 --复位 set @result = '' while (@col < @n + @row) begin if (@col < @n - @row - 1) begin set @result = @result + ' ' end else begin set @result = @result + '*' end set @col = @col + 1 end print @result set @row = @row + 1 end go --case when --搜索case when(用于一个范围) select '评语' = case when SqlServer>= 90 then '优秀' when SqlServer >= 80 and SqlServer < 90 then '良' when SqlServer >= 60 and SqlServer < 80 then '及格' else '不及格' end from T_StuInfo --简单case when(类似swtich,用于一个定值) declare @gender bit set @gender = 'true' print case @gender when 'true' then '男' else '女' end --################################################################################### --事物: 事物的特性ACID(一致性(Consistency)、原子性(Atomicity)、隔离性(Isolation)、持久性(Durability)) declare @errorcount int set @errorcount = 0 begin transaction --if控制事物的提交 begin update T_StuInfo set age = age + 1 where gender = '男' set @errorcount = @@error --@@error无错误返回0,有错误返回非0的数 update T_StuInfo set age = age - 1 where cardno = '女' set @errorcount = @errorcount + @@error if(@errorcount = 0) begin commit end else begin rollback end end begin transaction --异常控制事物提交,raiserror('XXXXX',1)用于抛出xxxx的异常 begin begin try update T_StuInfo set age = age + 1 where gender = '男' update T_StuInfo set age = age - 1 where cardno = '女' commit end try begin catch raiserror('性别字段输入了不合适的字符',1) --1-15级为系统使用 rollback end catch end --################################################################################### --索引: 聚集索引(Clustered Index)或称物理所引,非聚集索引(Nonclustered Index)或称逻辑索引,唯一索引(Unique Index),主键索引(PK Index) --优缺点: 查询快,但增删改慢. --何时用: 数据量特别大的情况适合建索引; 经常查找的字段建索引(聚集索引,此时要求取消主键索引) --注意事项: -- 使用索引时,需要注意查询时的where子句: 若有索引,先查索引,之后再根据索引查找原表记录位置,拼接结果; 若无索引,则不查索引,直接拼结果. -- 如此,针对索引字段,若从带where的查询结果中去掉前5项(不带where),则会出现错误(真分页有类似情况). -- 解决方案: 在子查询中也添加排序字段的永真where条件,如: where sortfield > -1 select top 20 sortfiled,filed1 from T_S where sortfiled not in (select top 5 sortfiled from T_S where sortfiled > -1) create clustered index idx_age on T_StuInfo(age) --创建聚集索引(每表仅一份),将对记录排序,而且索引将会和表保存在一起(采用二分查找) create nonclustered index idx_age on T_StuInfo(age) --创建非聚集索引(任意多个),不排序但会创建独立表(含2列: 原表中的位置索引,已排序的字段) --################################################################################### --视图: 将会创建一张虚拟表,且对视图的insert、delete和update操作会修改源数据,但工作中禁止通过视图修改源数据. -- 视图就是个Sql语句,也就是Select结果的虚表,视图相当于虚表的别名而已. -- 注意: 视图的别名的使用. --优点: 代码易读; 经过预编译(存储过程也是预编译的),效率高; 屏蔽了表结构,比较安全性; 缺点: 增加管理开销 if exists(select * from sysobjects where [name] = 'V_SnoName' and [type] = 'V') drop view V_SnoName go create view V_SnoName as select [sid],sname from T_StuInfo go select * from V_SnoName select * from T_StuInfo insert into V_SnoName(sname) values('候八') --################################################################################### --存储过程(Stored Procedure): sp_help查看SP以及sp参数的信息,sp_helptext查看SP内部代码 if exists(select * from sysobjects where [name] = 'P_Triangle' and [type] = 'P') drop procedure P_Triangle go create procedure P_Triangle( @n int ) with encryption --加密,不影响编译但将无法查看SP内部代码(sp_helptext) as --局部变量 declare @row int declare @col int declare @result nvarchar(2000) begin set @row = 0 set @col = 0 set @result = '' while (@row < @n) begin set @col = 0 --复位 set @result = '' while (@col < @n + @row) begin if (@col < @n - @row - 1) begin set @result = @result + ' ' end else begin set @result = @result + '*' end set @col = @col + 1 end print @result set @row = @row + 1 end end go exec P_Triangle 10 sp_help P_Triangle --查看SP及其参数的信息 sp_helptext P_Triangle --查看SP内部代码 declare @result int --以下代码证明,SP默认返回值为0 set @result = -1 exec @result = P_Triangle 15 print @result --存储过程 + 事物 + 输出参数 if exists(select * from sysobjects where [name] = 'P_InsertRecord' and [type] = 'P') drop procedure P_InsertRecord go create procedure P_InsertRecord( @sname nvarchar(20),@gender nchar(1) = '男',--等号后边是默认值 @age tinyint,@status nchar(2),@birdate datetime,@retrunsid int output --用以保存该记录的主键 ) as --局部变量 begin transaction begin begin try insert into T_StuInfo(sname,gender,[status],birdate) values(@sname,@gender,@age,@status,@birdate) set @retrunsid = @@identity commit return 0 end try begin catch raiserror('插入数据异常',1) rollback return 1 end catch end go declare @sid int --保存输出参数 declare @return int --保存返回值 exec P_InsertRecord '测试2',35,'毕业','1977-06-07',@sid output exec @return = P_InsertRecord '测试2',@sid output --用@return接受SP返回值 print @sid print @return --################################################################################### --触发器: 执行时将自动创建inserted或deleted临时表(update,同时创建两表),且均是只读的; 因为无调用痕迹,系统调试时增加困难 if exists(select * from sysobjects where [name] = 'TR_DelStu' and [type] = 'TR') drop trigger TR_DelStu go create trigger TR_DelStu --级联删除 on T_StuInfo instead of delete --(for,after,instead of),注意for和after效果是一样的 as declare @currentid int begin transaction begin begin try --set @currentid = (select [sid] from deleted) --insert和update会用到临时表inserted select @currentid = [sid] from deleted delete from T_StuScore where [sid] = @currentid delete from T_StuInfo where [sid] = @currentid commit end try begin catch raiserror('删除失败操作异常',1) rollback end catch end delete from T_StuInfo where [sid] = 3 --################################################################################### --用户定义函数(User Defined Function): 标量函数、内嵌表值函数、多语句表值函数 --标量函数(Scalar Functions) if exists(select * from sysobjects where [name]='GetCountByGender' and [type] = 'FN') drop function GetCountByGender go create function GetCountByGender ( @gender nchar(1) --函数的参数列表 ) returns int --函数的返回值类型 as begin declare @count int --返回值变量 set @count = ( select count([sid]) from T_StuInfo where gender = @gender ) return @count --执行返回 end go select dbo.GetCountByGender('男') as 数量 --调用函数时,必须加上所有者 --内嵌表值函数(Inline Table-valued Functions) if exists(select * from sysobjects where [name]='GetInfoByStatus' and [type] = 'IF') drop function GetInfoByStatus go create function GetInfoByStatus ( @status nchar(2) --参数列表 ) returns table --返回值为数据表 as return ( select * from T_StuInfo where [status] = @status ) go select * from dbo.GetInfoByStatus('毕业') --调用函数时,必须加上所有者 go --多语句表值函数(Multistatement Table-valued Functions) if exists(select * from sysobjects where [name]='GetNameBySegAge' and [type] = 'TF') drop function GetNameBySegAge go create function GetNameBySegAge ( @firstage int,--18岁 @secondage int,--18-30岁 @thirdage int --30岁以上 ) returns @infotable table --定义返回值变量(table类型),以及返回值表的字段 ( AgeSegment nvarchar(30),countnum int ) as begin --局部变量,用于填充返回值表 declare @currentcount int --当前年龄段的计数 declare @currentdesc nvarchar(30) --当前年龄段的描述 set @currentcount = (select count([sid]) from T_StuInfo where age<@firstage) set @currentdesc = '小于(含)-'+Convert(nvarchar(10),@firstage)+'岁' insert into @infotable(AgeSegment,countnum) values(@currentdesc,@currentcount) set @currentcount = (select count([sid]) from T_StuInfo where age>=@firstage and age<@secondage) set @currentdesc = Convert(nvarchar(10),@firstage)+'岁(含)-'+Convert(nvarchar(10),@secondage)+'岁' insert into @infotable(AgeSegment,@currentcount) set @currentcount = (select count([sid]) from T_StuInfo where age>=@secondage and age<@thirdage) set @currentdesc = Convert(nvarchar(10),@secondage)+'岁(含)-'+Convert(nvarchar(10),@thirdage)+'岁' insert into @infotable(AgeSegment,@currentcount) set @currentcount = (select count([sid]) from T_StuInfo where age>=@thirdage) set @currentdesc = Convert(nvarchar(10),@thirdage)+'岁(含)-不限' insert into @infotable(AgeSegment,@currentcount) return --执行已定义的返回值表的返回操作 end go select * from dbo.GetNameBySegAge(20,30,40) --调用函数时,必须加上所有者 --################################################################################### --游标: begin transaction MoveUserInfoTrans begin declare @errcount int set @errcount = 0 declare MoveUserInfoTwo cursor --声明游标 for select userid,userpwd from UserInfoTwo open MoveUserInfoTwo --打开游标,准备开始读取操作 declare @userid nvarchar(20),@userpwd nvarchar(30) fetch next from MoveUserInfoTwo into @userid,@userpwd --执行读取 while(@@fetch_status=0) begin insert into UserInfoOne(userid,userpwd) values (@userid,@userpwd) if(@@error!=0) --验证单次操作的是否成功 begin set @errcount = @errcount + 1 break end fetch next from MoveUserInfoTwo into @userid,@userpwd --取下一条 end close MoveUserInfoTwo --完成游标操作,关闭游标 deallocate MoveUserInfoTwo --释放游标 if(@errcount = 0) --用if验证事务的操作过程 begin commit transaction MoveUserInfoTrans print '事务已成功提交!' end else begin rollback transaction MoveUserInfoTrans print '执行过程出错,事务已回滚!' end end go
尊重原创 另外说明: row_number(),rank(),dense_rank(),ntile(num) 区别: 都是 常用于排序后新产生一列序号 例如有一数据表:gradeInfo 表中数据如下 1.row_number(): select gradeID,gradeName,row_number() over(order by gradeid asc) a from gradeInfo 结果是: 2.rank(): select gradeID,rank() over(order by gradeid asc) newNumber from gradeInfo 结果是: 3.dense_rank(): select gradeID,dense_rank() over(order by gradeid asc) newNumber from gradeInfo 结果是: 4.ntile(num)? [num:表示序号的的最大值] (为了能看的更明白,我们多插入一条数据到gradeInfo); select gradeID,ntile(3) over(order by gradeid asc) newNumber from gradeInfo? 最大值只能排到3 结果: 如果:select gradeID,ntile(4) over(order by gradeid asc) newNumber from gradeInfo? 最大值为4 那么结果: tips:ntile(num),其实是将表中的数据分为了num个组,没一组有多少个数据,至于每一组中数据条数是怎么分配的,请看 排名函数详解 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |