SqlServer资料
SQL SERVER介绍
数据库:数据库是“按照数据结构来组织、存储和管理数据的仓库”。是一个长期存储在计算机内的、有组织的、可共享的、统一管理的大量数据的集合。
应用场景:在软件系统中无处不在,几乎所有的软件系统背后都有数据库,例如(淘宝,QQ,游戏等)。
数据表展现形式: (二维表)
(images\001.PNG)
主流关系型数据库: SQL SERVER,MySQL,Oracle等。
数据库的安装:
(1)百度搜索”I tell you”,或者访问 https://msdn.itellyou.cn/
(2)选择合适的版本,下载安装。
(images\002.PNG)
打开数据库:
(1)启动服务:
【1】命令行启动;【2】SQL SERVER配置管理器;【3】Windows服务;
(2)打开SQL SERVER Management Studio,使用工具连接到数据库。
【1】Windows身份验证; 【2】SQL SERVER身份验证;
数据库基本操作:
(1)建库。
(2)建表。
(3)数据维护。
数据库的迁移:
(1)数据库的分离、附加;(分离和删除的区别在于硬盘上是否还留存有数据库文件)
(2)数据库的备份,还原;
(3)数据库脚本的保存;
建库建表
一、检查数据库名是否存在
如果需要创建数据库,可能会出现数据库名字重名的现象,我们可以使用如下代码查询数据库名是否存在,存在则删除此数据库。
--删除数据库 |
此代码检查数据库中是否存在”DBTEST”数据库,如果存在则删除此数据库,此处理方式最好只在学习阶段使用,在正式生产环境中慎用,操作不当可能会删除重要数据。
二、创建数据库
--创建数据库 |
以上代码创建”DBTEST”数据库,并且分别使用on和log on规定了数据文件和日志文件的信息。
创建数据库也可以按照如下简单语法来创建:
create database DBTEST |
如果按照上述方式创建数据库,数据库的数据文件和日志文件的相关信息,全部采取默认值。
三、建表
使用数据库和删除数据表:
use DBTEST --切换当前数据库为DBTEST |
创建数据表语法:
create table 表名 |
其中数据类型,我们在后面用到什么类型,在介绍什么类型,有的类型可以不填写长度。
创建数据表示例(部门表,职级表,员工信息表):
--创建部门表 |
字符串类型比较:
char:定长,例如 char(10),不论你存储的数据是否达到了10个字节,都要占去10个字节的空间 。
varchar:变长,例如varchar(10),并不代表一定占用10个字节,而代表最多占用10个字节。最大长度8000,也可以使用varchar(max)表示2G以内的数据,但存储机制会和text一样,效率会降低。
text:长文本, 最大长度为2^31-1(2,147,483,647)个字符 。
nchar,nvarchar,ntext:名字前面多了一个n, 它表示存储的是Unicode数据类型的字符,区别varchar(100)可以存储100个英文字母或者50个汉字,而nvarchar(100)可以存储100个英文字母,也可以存储100个汉字。
--创建职级表,rank为系统关键字,此处使用[]代表自定义名字,而非系统关键字 |
--创建员工信息表 |
四、修改表结构
(1)如需在表中添加列,请使用下面的语法:
ALTER TABLE table_name |
例如该员工表添加一列员工邮箱:
alter table People |
(2)如需在表中删除列,请使用下面的语法:
ALTER TABLE table_name |
例如删除员工表中的邮箱这一列
alter table People |
(3)如需改变表中列的数据类型,请使用下列语法:
ALTER TABLE table_name |
例如需要改变邮箱列的数据类型为varchar(100)
alter table People |
五、删除添加约束
删除约束语法:
if exists(select * from sysobjects where name=约束名) |
添加约束语法:
--添加主键约束 |
插入数据
一、向部门表插入数据
标准语法:
insert into Department(DepartmentName,DepartmentRemark) |
简写语法:(省略字段名称)
insert into Department values('行政部','公司主管行政工作的部门') |
此写法在给字段赋值的时候,必须保证顺序和数据表结构中字段顺序完全一致,不推荐使用此种写法,因为数据表结构变化的时候,数据会出错或产生错误数据。
一次插入多行数据:
insert into Department(DepartmentName,DepartmentRemark) |
二、向职级表插入数据
insert into [Rank](RankName,RankRemark) |
三、向员工表插入数据
insert into People(DepartmentId,RankId,PeopleName,PeopleSex,PeopleBirth, |
其中DepartmentId,RankId,PeopleSalary均为数字类型,在赋值的时候不需要添加单引号,而其它类型需要添加单引号。
四、查询数据是否插入成功
select * from Department |
修改和删除数据
一、修改数据示例
工资普调,为每个员工+500 元工资(批量修改)
update People set PeopleSalary = PeopleSalary + 500 |
将员工编号为8的工资+1000 元(根据条件修改)
update People set PeopleSalary = PeopleSalary + 1000 WHERE PeopleId = 8 |
将软件部(部门编号已知=1)所有员工工资低于1万的全部调整成1 万(根据多条件修改)
update People set PEOPLESALARY = 10000 WHERE DepartmentId=1 and PEOPLESALARY < 10000 |
修改刘备工资为以前的2 倍,并且修改其地址为北京(同时修改多个字段)
UPDATE People SET PEOPLESALARY = PEOPLESALARY*2,PEOPLEADDRESS='北京' WHERE PEOPLENAME = '刘备' |
二、删除数据示例
删除员工表中所有数据
DELETE FROM People |
删除市场部(已知部门编号=3)中工资大于15000 的所有员工
DELETE FROM People WHERE DepartmentId = 3 and PEOPLESALARY > 15000 |
三、drop、truncate、delete区别
drop table:删除表对象,表数据、表结构、表对象都进行了删除。
delete和truncate table:删除表数据,表对象及表结构依然存在。
delete与truncate table的区别如下:
delete:
(1)可以删除表所有数据,也可以根据条件删除数据。
(2)如果有自动编号,删除后继续编号,例如delete删除表所有数据后,之前数据的自动编号是1,2,3,那么之后新增数据的编号从4开始。
truncate table:
(1)只能清空整个表数据,不能根据条件删除数据。
(2)如果有自动编号,清空表数据后重新编号,例如truncate table清空表所有数据后,之前数据的自动编号是1,2,3,那么之后新增数据的编号仍然从1开始。
基础查询
(1)查询所有行所有列
--查询所有的部门 |
(2)指定列查询(姓名,性别,月薪,电话)
SELECT PeopleName,PeopleSex,PeopleSalary,PeoplePhone from People |
(3)指定列查询,并自定义中文列名(姓名,性别,月薪,电话)
SELECT PeopleName 姓名,PeopleSex 性别,PeopleSalary 工资,PeoplePhone 电话 from People |
(4)查询公司员工所在城市(不需要重复数据)
select distinct PeopleAddress from People |
(5)假设工资普调10%,查询原始工资和调整后的工资,显示(姓名,性别,月薪,加薪后的月薪)(添加列查询)。
SELECT PeopleName 姓名,PeopleSex 性别,PeopleSalary 月薪,PeopleSalary*1.1 加薪后月薪 from People |
条件查询
SQL中常用运算符
=:等于,比较是否相等及赋值 |
查询示例:
(1)根据指定列(姓名,性别,月薪,电话)查询性别为女的员工信息,并自定义中文列名
SELECT PeopleName 姓名,PeopleSex 性别,PeopleSalary 工资,PeoplePhone 电话 from People |
(2)查询月薪大于等于10000 的员工信息( 单条件 )
select * from People where PeopleSalary >= 10000 |
(3)查询月薪大于等于10000 的女员工信息(多条件)
select * from People where PeopleSalary >= 10000 and PeopleSex = '女' |
(4)显示出出身年月在1980-1-1之后,而且月薪大于等于10000的女员工信息。
select * from People where PeopleBirth >= '1980-1-1' and PeopleSalary >= 10000 and PeopleSex = '女' |
(5)显示出月薪大于等于15000 的员工,或者月薪大于等于8000的女员工信息。
select * from People where PeopleSalary >= 15000 or (PeopleSalary >= 8000 and PeoPleSex = '女') |
(6)查询月薪在10000-20000 之间员工信息( 多条件 )
--方案一: |
(7)查询出地址在北京或者上海的员工信息
--方案一: |
(8)查询所有员工信息(根据工资排序,降序排列)
--order by: 排序 asc: 正序 desc: 倒序 |
(9)显示所有的员工信息,按照名字的长度进行倒序排列
select * from People order by len(PeopleName) desc |
(10)查询工资最高的5个人的信息
select top 5 * from People order by PeopleSalary desc |
(11)查询工资最高的10%的员工信息
select top 10 percent * from People order by PeopleSalary desc |
(12)查询出地址没有填写的员工信息
select * from People where PeopleAddress is null |
(13)查询出地址已经填写的员工信息
select * from People where PeopleAddress is not null |
(14)查询所有的80后员工信息
--方案一: |
(15)查询年龄在30-40 之间,并且工资在15000-30000 之间的员工信息
--方案一: |
(16)查询出巨蟹 6.22–7.22 的员工信息
select * from People where |
(17)查询工资比赵云高的人
select * from People where PeopleSalary > |
(18)查询出和赵云在同一个城市的人
select * from People where PEOPLEADDRESS = |
(19)查询出生肖为鼠的人员信息
select * from People where year(PeopleBirth) % 12 = 4 |
(20)查询所有员工信息,添加一列显示属相(鼠,牛,虎,兔,龙,蛇,马,羊,猴,鸡,狗,猪)
--方案一: |
模糊查询
模糊查询使用like关键字和通配符结合来实现,通配符具体含义如下:
%:代表匹配0个字符、1个字符或多个字符。 |
(1)查询姓刘的员工信息
select * from People where PeopleName like '刘%' |
(2)查询名字中含有 “ 尚 “ 的员工信息
select * from People where PeopleName like '%尚%' |
(3)显示名字中含有“尚”或者“史”的员工信息
select * from People where PeopleName like '%尚%' or PeopleName like '%史%' |
(4)查询姓刘的员工,名字是2个字
--方案一: |
(5)查询出名字最后一个字是香,名字一共三个字的员工信息
--方案一: |
(6)查询出电话号码开头138的员工信息
select * from People where PeoplePhone like '138%' |
(7)查询出电话号码开头138的员工信息,第4位可能是7,可能8 ,最后一个号码是5
select * from People where PeoplePhone like '138[7,8]%5' |
(8)查询出电话号码开头133的员工信息,第4位是2-5之间的数字 ,最后一个号码不是2和3
--方案一: |
聚合函数
SQL SERVER中聚合函数主要有:
count:求数量 |
一、聚合函数举例应用
(1)求员工总人数
select COUNT(*) 数量 from People |
(2)求最大值,求最高工资
select MAX(PeopleSalary) 最高工资 from People |
(3)求最小时,求最小工资
select MIN(PeopleSalary) 最低工资 from People |
(4)求和,求所有员工的工资总和
select SUM(PeopleSalary) 工资总和 from People |
(5)求平均值,求所有员工的平均工资
--方案一: |
ROUND函数用法:
round(num,len,[type]) |
(6)求数量,最大值,最小值,总和,平均值,在一行显示
select COUNT(*) 数量,MAX(PeopleSalary) 最高工资,MIN(PeopleSalary) 最低工资,SUM(PeopleSalary) 工资总和,AVG(PeopleSalary) 平均工资 from People |
(7)查询出武汉地区的员工人数,总工资,最高工资,最低工资和平均工资
select '武汉' 地区,COUNT(*) 数量,MAX(PeopleSalary) 最高工资,MIN(PeopleSalary) 最低工资 |
(8)求出工资比平均工资高的人员信息
select * from People where PeopleSalary > (select AVG(PeopleSalary) 平均工资 from People) |
(9)求数量,年龄最大值,年龄最小值,年龄总和,年龄平均值,在一行显示
--方案一: |
(10)计算出月薪在10000 以上的男性员工的最大年龄,最小年龄和平均年龄
--方案一: |
(11)统计出所在地在“武汉或上海”的所有女员工数量以及最大年龄,最小年龄和平均年龄
--方案一: |
(12)求出年龄比平均年龄高的人员信息
--方案一: |
二、补充-SQL中常用时间处理函数
GETDATE() 返回当前的日期和时间
DATEPART() 返回日期/时间的单独部分
DATEADD() 返回日期中添加或减去指定的时间间隔
DATEDIFF() 返回两个日期直接的时间
DATENAME() 返回指定日期的指定日期部分的整数
CONVERT() 返回不同格式的时间
示例:
select DATEDIFF(day, '2019-08-20', getDate()); --获取指定时间单位的差值 |
时间格式控制字符串:
| 名称 | 日期单位 | 缩写 |
|---|---|---|
| 年 | year | yyyy 或yy |
| 季度 | quarter | qq,q |
| 月 | month | mm,m |
| 一年中第几天 | dayofyear | dy,y |
| 日 | day | dd,d |
| 一年中第几周 | week | wk,ww |
| 星期 | weekday | dw |
| 小时 | Hour | hh |
| 分钟 | minute | mi,n |
| 秒 | second | ss,s |
| 毫秒 | millisecond | ms |
分组查询
(1)根据员工所在地区分组统计员工人数 ,员工工资总和 ,平均工资,最高工资和最低工资
--方案一:使用union(此方案需要知道所有的地区,分别查询出所有地区的数据,然后使用union拼接起来。) |
(2)根据员工所在地区分组统计员工人数,员工工资总和,平均工资,最高工资和最低工资,1985 年及以后出身的员工不参与统计。
select PeopleAddress 地区,COUNT(*) 人数,SUM(PeopleSalary) 工资总和, |
(3)根据员工所在地区分组统计员工人数,员工工资总和,平均工资,最高工资和最低工资,要求筛选出员工人数至少在2人及以上的记录,并且1985年及以后出身的员工不参与统计。
select PeopleAddress 地区,COUNT(*) 人数,SUM(PeopleSalary) 工资总和, |
多表查询
一、笛卡尔乘积
select * from People,Department |
此查询结果会将People表的所有数据和Department表的所有数据进行依次排列组合形成新的记录。例如People表有10条记录,Department表有3条记录,则排列组合之后查询结果会有10*3=30条记录。
二、简单多表查询
此种查询不符合主外建关系的数据不会被显示
查询员工信息,同时显示部门名称
select * from People,Department where People.DepartmentId = Department.DepartmentId |
查询员工信息,同时显示职级名称
select * from People,Rank where People.RankId = Rank.RankId |
查询员工信息,同时显示部门名称,职位名称
select * from People,Department,Rank |
三、内连接
此种查询不符合主外建关系的数据不会被显示
查询员工信息,同时显示部门名称
select * from People inner join Department on People.DepartmentId = Department.DepartmentId |
查询员工信息,同时显示职级名称
select * from People inner join Rank on People.RankId = Rank.RankId |
查询员工信息,同时显示部门名称,职位名称
select * from People |
三、外连接
外连接分为左外连接、右外连接和全外连接。
左外联接:以左表为主表显示全部数据,主外键关系找不到数据的地方null取代。
以下是左外连接的语法示例:
查询员工信息,同时显示部门名称
select * from People left join Department on People.DepartmentId = Department.DepartmentId |
查询员工信息,同时显示职级名称
select * from People left join Rank on People.RankId = Rank.RankId |
查询员工信息,同时显示部门名称,职位名称
select * from People |
右外连接(right join):右外连接和左外连接类似,A left join B == B right join A
全外连接(full join):两张表的所有数据无论是否符合主外键关系必须全部显示,不符合主外键关系的地方null取代。
四、多表查询综合示例
(1)查询出武汉地区所有的员工信息,要求显示部门名称以及员工的详细资料
select PeopleName 姓名,People.DepartmentId 部门编号 ,DepartmentName 部门名称, |
(2)查询出武汉地区所有的员工信息,要求显示部门名称,职级名称以及员工的详细资料
select PeopleName 姓名,DepartmentName 部门名称,RankName 职位名称, |
(3)根据部门分组统计员工人数,员工工资总和,平均工资,最高工资和最低工资。
--提示:在进行分组统计查询的时候添加二表联合查询。 |
(4)根据部门分组统计员工人数,员工工资总和,平均工资,最高工资和最低工资,平均工资在10000 以下的不参与统计,并且根据平均工资降序排列。
select DepartmentName 部门名称,COUNT(*) 人数,SUM(PeopleSalary) 工资总和, |
(5)根据部门名称,然后根据职位名称,分组统计员工人数,员工工资总和,平均工资,最高工资和最低工资
select DepartmentName 部门名称,RANKNAME 职级名称,COUNT(*) 人数,SUM(PeopleSalary) 工资总和, |
五、自连接
自连接:自己连接自己。
例如有如下结构和数据:
create table Dept |
如果要查询出所有部门信息,并且查询出自己的上级部门,查询结果如下:
--部门编号 部门名称 上级部门 |
数据库设计
一、数据库结构设计三范式
第一范式:是对属性的原子性,要求属性具有原子性,不可再分解。
如有如下表结构设计:
create table Student --学生表 |
上述设计则不满足第一范式,联系方式这一列并不是不可再分的最小单元,应修改为如下结构
create table Student --学生表 |
第二范式:是对记录的惟一性,要求记录有惟一标识,即实体的惟一性,即不存在部分依赖;
如有如下表结构设计:
--选课成绩表 |
上述设计中有两个事物,一个学生信息,一个课程信息,很显然这两个事物都没有保证实体的唯一性,这里的姓名依赖学号,课程名称依赖课程编号,所以不符合二范式。
create table Course --课程 |
第三范式:要求任何字段不能由其他字段派生出来,它要求字段没有冗余,即不存在传递依赖 ;
如有如下表结构设计:
create table Student |
上述设计种专业名称字段和专业介绍字段,在数据库种会产生很多冗余数据,不满足第二范式,优化方案如下:
create table Professional |
二、表关系
(1)一对多关系(专业–学生)
create table Profession --专业 |
(2)一对一关系(学生基本信息–学生详情)
方案一:
create table StudentBasicInfo --学生基本信息 |
此方案要求两个表的主键相等关系确定一个学生,所以此设计必须保证主键是可以维护和编辑的,如果主键是自动增长,将很大程度增加了数据维护的难度。
方案二:
create table StudentBasicInfo --学生基本信息 |
此方案中实际上我们是使用一对多的模式来表示一对一,保证多的表中只有一条对应数据即可。
(3)多对多关系:(选课成绩–学生)
create table Course --课程 |
此方案中,一个学生可以有多门选课,一门课程也可以被多个学生选择,我们称之为多对多关系,在处理多对多关系的时候,我们需要建立一个中间关联表,该关联表中需要有另外两张表的主键字段。
三、数据库设计案例
业务需求说明:
模拟银行业务,设计简易版的银行数据库表结构,要求可以完成以下基本功能需求:
1.银行开户(注册个人信息)及开卡(办理银行卡)(一个人可以办理多张银行卡,但是最多只能办理3张)
2.存钱
3.查询余额
4.取钱
5.转账
6.查看交易记录
7.账户挂失
8.账户注销
表设计:
1.账户信息表:存储个人信息。
2.银行卡表:存储银行卡信息。
3.交易信息表(存储存钱和取钱的记录)
4.转账信息表(存储转账信息记录)
5.状态信息变化表(存储银行卡状态变化的记录,状态有1:正常,2:挂失,3:冻结,4:注销)
表结构设计:
--账户信息表:存储个人信息 |
添加测试数据:
--为刘备,关羽,张飞三个人进行开户开卡的操作 |
使用T-SQL编程
一、信息打印
--print:直接打印消息 |
二、变量
T-SQL中变量分为局部变量和全局变量
局部变量:(1)以@作为前缀(2)先声明,在赋值
declare @str varchar(20) |
备注:set赋值和select赋值区别:
set:赋给变量指定的值
select:一般用于从表中查询出的数据,查询记录如果有多条,将最后一条记录的值赋给变量,例如:
select @变量名 = 字段名 from 表名 |
在赋值过程中,如果是表中查询的数据,如果记录只有一条,使用set和select都可以,但是习惯上使用select。
全局变量:(1)以@@作为前缀(2)由系统进行定义和维护,只读
--@@ERROR:返回执行的上一个语句的错误号 |
变量示例:
(1)为赵云此人进行开户开卡操作,赵云身份证:420107199904054233
declare @AccountId int |
(2)需要求出张飞的银行卡卡号和余额,张飞身份证:420107199602034138
--方案一:连接查询 |
三、go语句
go语句:
(1)等待go语句前的代码执行完成后,再执行go后面的代码。
(2)批处理语句的结束标志。
--下面的@num变量作用域为全局 |
运算符
T-SQL中使用的运算符分为7种
算数运算符:加(+)、减(-)、乘(*)、除(/)、模(%) |
运算符示例
(1)已知长方形的长和宽,求长方形的周长和面积
declare @c int = 5 |
(2)查询银行卡状态为冻结,并且余额超过1000000的银行卡信息
select * from BankCard where CardState = 3 and CardMoney > 1000000 |
(3)查询出银行卡状态为冻结或者余额等于0的银行卡信息
select * from BankCard where CardState = 3 or CardMoney = 0 |
(4)查询出姓名中含有’刘’的账户信息以及银行卡信息
select * from AccountInfo left join BankCard on AccountInfo.AccountId = BankCard.AccountId where RealName like '%刘%' |
(5)查询出余额在2000-5000之间的银行卡信息
select * from BankCard where CardMoney between 2000 and 5000 |
(6)查询出银行卡状态为冻结或者注销的银行卡信息
select * from BankCard where CardState in(3,4) |
(7)关羽身份证:420107199507104133,关羽到银行来开户,查询身份证在账户表是否存在,不存在则进行开户开卡,存在则不开户直接开卡。
declare @AccountId int |
上述代码也可以使用not exists进行判断,表示不存在。
扩展:上面需求添加一个限制即一个人最多只能开3张银行卡。
declare @AccountId int |
(8)查询银行卡账户余额,是不是所有的账户余额都超过了3000。
if 3000 < ALL(select CardMoney from BankCard) |
(9)查询银行卡账户余额,是否含有账户余额超过30000000的信息
if 30000000 < ANY(select CardMoney from BankCard) |
流程控制
一、选择分之结构
(1)某用户银行卡号为“6225547854125656”,该用户执行取钱操作,取钱5000元,余额充足则进行取钱操作,并提示”取钱成功”,否则提示“余额不足”。
declare @balance money |
(2)查询银行卡信息,将银行卡状态1,2,3,4分别转换为汉字“正常,挂失,冻结,注销”,并且根据银行卡余额显示银行卡等级 30万以下为“普通用户”,30万及以上为”VIP用户”,显示列分别为卡号,身份证,姓名,余额,用户等级,银行卡状态。
select CardNo 卡号,AccountCode 身份证,RealName 姓名,CardMoney 余额, |
或如下写法:
select CardNo 卡号,AccountCode 身份证,RealName 姓名,CardMoney 余额, |
二、循环结构
(1)循环打印1-10。
declare @i int = 1 |
(2)循环打印九九乘法表
declare @i int = 1 |
备注:
(1)特殊字符:制表符 CHAR(9);换行符 CHAR(10);回车 CHAR(13);
(2)循环中若出现break和CONTINUE,作用与Java,C#等语言一致。
子查询
(1)关羽的银行卡号为”6225547858741263”,查询出余额比关羽多的银行卡信息,显示卡号,身份证,姓名,余额。
--方案一: |
--方案二: |
(2)从所有账户信息中查询出余额最高的交易明细(存钱取钱信息)。
--方案一: |
--方案二:(如果有多个银行卡余额相等并且最高,此方案只能求出其中一个人的明细) |
(3)查询有取款记录的银行卡及账户信息,显示卡号,身份证,姓名,余额。
select CardNo 卡号,AccountCode 身份证,RealName 姓名,CardMoney 余额 from BankCard |
(4)查询出没有存款记录的银行卡及账户信息,显示卡号,身份证,姓名,余额。
select CardNo 卡号,AccountCode 身份证,RealName 姓名,CardMoney 余额 from BankCard |
(5)关羽的银行卡号为”6225547858741263”,查询当天是否有收到转账。
if exists(select * from CardTransfer where CardNoIn = '6225547858741263' |
(6)查询出交易次数(存款取款操作)最多的银行卡账户信息,显示:卡号,身份证,姓名,余额,交易次数。
--方案一 |
--方案二(如果有多个人交易次数相同,都是交易次数最多,则使用以下方案) |
(7)查询出没有转账交易记录的银行卡账户信息,显示卡号,身份证,姓名,余额。
select CardNo 卡号,AccountCode 身份证,RealName 姓名,CardMoney 余额 from BankCard |
(8)分页。
--数据结构和数据如下: |
--方案一:使用row_number分页 |
--方案二:使用top分页 |
事务-索引-视图-游标
一、事务
人员信息如下:(第二列是身份证号,第三列是银行卡卡号)
–刘备 420107198905064135 6225125478544587
–关羽 420107199507104133 6225547858741263
–张飞 420107199602034138 6225547854125656
(1)假设刘备取款6000,(添加check约束,设置账户余额必须>=0),要求:使用事务实现,修改余额和添加取款记录两步操作使用事务
begin transaction |
(2)假设刘备向张飞转账1000元,(添加check约束,设置账户余额必须>=0);分析步骤有三步(1)张飞添加1000元,(2)刘备扣除1000元,(3)生成转账记录;使用事务解决此问题。
begin transaction |
二、索引
索引:提高检索查询效率。
SQL SERVER索引类型:按存储结构区分:“聚集索引(又称聚类索引,簇集索引)”,“非聚集索引(非聚类索引,非簇集索引)”;
聚集索引:根据数据行的键值在表或视图中的排序存储这些数据行,每个表只有一个聚集索引。聚集索引是一种对磁盘上实际数据重新组织以按指定的一列或多列值排序(类似字典中的拼音索引)(物理存储顺序)。
非聚集索引:具有独立于数据行的结构,包含非聚集索引键值,且每个键值项都有指向包含该键值的数据行的指针。(类似字典中的偏旁部首索引)(逻辑存储顺序)。
SQL SERVER索引其他分类:
按数据唯一性区分:“唯一索引”,“非唯一索引”;按键列个数区分:“单列索引”,“多列索引”。
创建索引的方式:
- 通过显式的CREATE INDEX命令
- 在创建约束时作为隐含的对象
- 主键约束(聚集索引)
- 唯一约束(唯一索引)
创建索引语法:
CREATE [UNIQUE] [CLUSTERED | NONCLUSTERED] |
索引基本示例语法:
--exp:创建一个非聚集索引 |
备注:索引信息存储在系统视图sys.indexes中。
按照指定索引进行查询
select * from AccountInfo with(index=indexAccount) where AccountCode='6225125478544587' |
三、视图
视图:可以理解成虚拟表。
(1)编写视图实现查询出所有银行卡账户信息,显示卡号,身份证,姓名,余额。
create view CardAndAccount as |
如果要进行相应信息的查询,不需要编写复杂的SQL语句,直接使用视图,如下:
select * from CardAndAccount |
四、游标
游标:定位到结果集中某一行。
游标分类:
(1)静态游标(Static):在操作游标的时候,数据发生变化,游标中数据不变
(2)动态游标(Dynamic):在操作游标的时候,数据发生变化,游标中数据改变,默认值。
(3)键集驱动游标(KeySet):在操作游标的时候,被标识的列发生改变,游标中数据改变,其他列改变,游标中数据不变。
假设有如下表结构和数据:
create table Member |
创建游标:
--1.创建游标(Scroll代表滚动游标,不加Scroll则是只进的,只能支持fetch next) |
打开游标:
open CURSORMember |
提取数据:
fetch first from CURSORMember --结果集的第一行 |
提取数据给变量以供它用(取出第3行用户名,查询该用户详细信息):
declare @MemberAccount varchar(30) |
利用游标提取所有的账户信息:
--方案一: |
利用游标修改和删除数据:
fetch absolute 3 from CURSORMember |
关闭游标:
close CURSORMember |
删除游标:
deallocate CURSORMember |
创建游标指向某行多列数据,并循环显示数据:
--此处如果指向所有数据,可以将for后面的语句修改成select * from Member |
函数-存储过程-触发器
一、函数
函数分为(1)系统函数,(2)自定义函数。
其中自定义函数又可以分为(1)标量值函数(返回单个值),(2)表值函数(返回查询结果)
本文主要介绍自定义函数的使用。
(1)编写一个函数求该银行的金额总和
create function GetSumCardMoney() |
函数调用
select dbo.GetSumCardMoney() |
上述函数没有参数,下面介绍有参数的函数的定义及使用
(2)传入账户编号,返回账户真实姓名
create function GetNameById(@AccountId int) |
函数调用
print dbo.GetNameById(2) |
(3)传递开始时间和结束时间,返回交易记录(存钱取钱),交易记录中包含 真实姓名,卡号,存钱金额,取钱金额,交易时间。
方案一(逻辑复杂,函数内容除了返回结果的sql语句还有其他内容,例如定义变量等):
create function GetExchangeByTime(@StartTime varchar(30),@EndTime varchar(30)) |
函数调用
select * from GetExchangeByTime('2018-6-1','2018-7-1') |
方案二(逻辑简单,函数内容直接是一条sql查询语句):
create function GetExchangeByTime(@StartTime varchar(30),@EndTime varchar(30)) |
函数调用:
select * from GetExchangeByTime('2018-6-19','2018-6-19') |
(4)查询银行卡信息,将银行卡状态1,2,3,4分别转换为汉字“正常,挂失,冻结,注销”,根据银行卡余额显示银行卡等级 30万以下为“普通用户”,30万及以上为”VIP用户”,分别显示卡号,身份证,姓名,余额,用户等级,银行卡状态。
方案一:直接在sql语句中使用case when
select * from AccountInfo |
方案二:将等级和状态用函数实现
create function GetGradeByMoney(@myMoney int) |
函数调用实现查询功能
select CardNo 卡号,AccountCode 身份证,RealName 姓名,CardMoney 余额, |
(5)编写函数,根据出生日期求年龄,年龄求实岁,例如:
生日为2000-5-5,当前为2018-5-4,年龄为17岁
生日为2000-5-5,当前为2018-5-6,年龄为18岁
测试数据如下:
create table Emp |
函数定义:
create function GetAgeByBirth(@birth smalldatetime) |
函数调用实现查询
select *,dbo.GetAgeByBirth(empBirth) 年龄 from Emp |
二、触发器
触发器分类:(1) “Instead of”触发器(2)“After”触发器
“Instead of”触发器:在执行操作之前被执行
“After”触发器:在执行操作之后被执行
触发器中后面的案例中需要用到的表及测试数据如下:
--部门 |
(1)假设有部门表和员工表,在添加员工的时候,该员工的部门编号如果在部门表中找不到,则自动添加部门信息,部门名称为”新部门”。
编写触发器:
create trigger tri_InsertPeople on People |
测试触发器:
insert People(DepartmentId,PeopleName,PeopleSex,PeoplePhone) |
我们会发现,当插入赵云这个员工的时候会自动向部门表中添加数据。
(2)触发器实现,删除一个部门的时候将部门下所有员工全部删除。
编写触发器:
create trigger tri_DeleteDept on Department |
测试触发器:
delete Department where DepartmentId = '001' |
我们会发现当我们删除此部门的时候,同时会删除该部门下的所有员工
(3)创建一个触发器,删除一个部门的时候判断该部门下是否有员工,有则不删除,没有则删除。
编写触发器:
drop trigger tri_DeleteDept --删除掉之前的触发器,因为当前触发器也叫这个名字 |
测试触发器:
delete Department where DepartmentId = '001' |
我们会发现,当部门下没有员工的部门信息可以成功删除,而部门下有员工的部门并没有被删除。
(4)修改一个部门编号之后,将该部门下所有员工的部门编号同步进行修改
编写触发器:
create trigger tri_UpdateDept on Department |
测试触发器:
update Department set DepartmentId = 'zjb001' where DepartmentId='001' |
我们会发现不但部门信息表中的部门编号进行了修改,员工信息表中部门编号为001的信息也被一起修改了。
三、存储过程
存储过程(Procedure)是SQL语句和流程控制语句的预编译集合。
(1)没有输入参数,没有输出参数的存储过程。
定义存储过程实现查询出账户余额最低的银行卡账户信息,显示银行卡号,姓名,账户余额
--方案一 |
执行存储过程:
exec proc_MinMoneyCard |
(2)有输入参数,没有输出参数的存储过程
模拟银行卡存钱操作,传入银行卡号,存钱金额,实现存钱操作
create proc proc_CunQian |
执行存储过程:
exec proc_CunQian '6225125478544587',3000 |
(3)有输入参数,没有输出参数,但是有返回值的存储过程(返回值必须整数)。
模拟银行卡取钱操作,传入银行卡号,取钱金额,实现取钱操作,取钱成功,返回1,取钱失败返回-1
create proc proc_QuQian |
执行存储过程:
declare @returnValue int |
(4)有输入参数,有输出参数的存储过程
查询出某时间段的银行存取款信息以及存款总金额,取款总金额,传入开始时间,结束时间,显示存取款交易信息的同时,返回存款总金额,取款总金额。
create proc proc_SelectExchange |
执行存储过程:
declare @SumIn money --存款总金额 |
(5)具有同时输入输出参数的存储过程
密码升级,传入用户名和密码,如果用户名密码正确,并且密码长度<8,自动升级成8位密码
--有输入输出参数(密码作为输入参数也作为输出参数) |
