目录
一、数据库定义语言DDL
1、数据库的定义
(1)创建数据库
(2)管理数据库
2、基本表的定义
(1)创建基本表
(2)修改基本表
3、索引的定义
(1)创建索引
(2)管理索引
4、视图的定义
(1)创建视图
(2)修改删除视图
(3)视图的查询和更新
二、数据查询语言DQL
1、select语句介绍
2、简单查询
(1)查询表中的若干行
(2)对查询结果进行排序
(3)汇总查询
3、连接查询
(1)内连接 inner join
(2)外连接 outer join
(3)自连接 self join
(4)交叉连接 cross join
4、嵌套查询
(1)非相关子查询
(2)相关子查询
5、组合查询
(1)union并操作
(2)intersect交操作
(3)except差操作
三、数据更新语言DML
1、插入数据
(1)插入单个元组
(2)插入多个元组
2、修改数据
(1)修改数据语法格式
(2)修改单个元组
(3)修改多个元组
3、删除数据
(1)delete
(2)truncate
4、数据更新操作检查完整性
(1)插入数据时需要分别检查实体完整性与参照完整性
(2)删除数据时需要检查参照完整性
(3)修改数据时需要检查实体完整性与参照完整性
四、数据控制语言DML
1、数据控制方法
2、sql server数据库操作权限
3、对象权限设置
4、语句权限设置
五、数据库初步编程
1、变量
(1)变量的声明
(2)变量的赋值
2、流程控制
(1)begin end
(2)if else
(3)while
(4)case
(5)waitfor
(6)goto
(7)注释
3、存储过程编程
(1)创建存储过程
(2)执行存储过程
(3)存储过程应用实例
(4)删除和修改存储过程
4、触发器编程
(1)DML触发器
(2)DDL触发器
(3)删除和修改触发器
六、嵌入式SQL
一、数据库定义语言DDL
1、数据库的定义
(1)创建数据库
create database 数据库名称 [ on --定义数据库的数据文件 [primary] --设置主文件组 <数据文件描述符>[,...n] --设置数据文件的属性 [,filegroup 文件组名 <数据文件描述符>[,...n]] --设置次文件组及数据文件属性 ] [ log on --定义数据库的日志文件 {<日志文件描述符>}[,...n] --设置日志文件的属性 ]
其中<数据文件描述符>和<日志文件描述符>为以下属性组合:
( name=逻辑文件名, --设置在SQL Server引用的名称 filename='物理文件名' --设置文件中磁盘存放的路径和名称 [,size=文件初始容量] [,maxsize=文件最大容量|unlimited] [,filegrowth=文件增长率%] --设置文件自动增量 )
例:在E盘的data文件夹下建立名为高校图书管理的数据库,主数据文件为高校图书管理_data,初始容量10MB,最大容量为50MB,增幅为10MB。日志文件为高校图管理_log,初始容量大小5MB,最大容量为20MB,增幅为5MB。
create database 高校图书管理 on primary --设置主文件组 ( name=高校图书管理_data filename='E:\data\高校图书管理_data.mdf', size=10MB maxsize=50MB filegrowth=10% ) --设置数据文件的属性 log on --定义数据库的日志文件 ( name=高校图书管理_log, size=5MB, maxsize=20MB, filegrowth=5% ) --设置日志文件的属性 go
注:如果创建时没有指定日志文件,系统自动创建一个初始容量为0.75MB的日志文件,且无最大容量限制
(2)管理数据库
alter database 数据库名称 --修改数据库 drop database 数据库名称 --删除数据库 sp_helpdb 数据库名称 --查看数据库属性
2、基本表的定义
(1)创建基本表
create table 表名 ( 列名,数据类型[(长度)] [default 常量表达式] --定义默认值 | [ indetity[(初值,增量)] ] --定义标识列 [constraint 约束名] { [NULL|NOT NULL] --设置空或非空约束 |[default] --设置默认值约束 |[ [primary key|unique] --设置主键或唯一性约束 [clustered|nonclustered] --设置聚集或非聚集索引 ] |[foreign key(外键列)] references 被参照表名(外键列) --设置外键约束 |check(逻辑表达式) --设置检查约束 } )
主键约束格式:
[constraint<约束名>]primary key(<列名>...) eg: constraint c3 primary key(dzkh,tsbh) --设置主键单独列出 dzkh nvarchar(20) primary key --直接在列后设置主键
外键约束格式:
[constraint<约束名>]foreign key(<列名>) references<主键表名>(<列名>) eg: constraint c1 foreign key(类别编号) references 读者类别(类别编号) --单独列出
- 外键设在哪个表? 哪个表的某属性需要被约束,则该表的该属性设为外键
- eg:选修表中学号需要被约束,则外键表为选修表,主键表为学生表
唯一性约束格式:
constraint 约束名 unique(<列名>)
主键与唯一性约束区别 主键 唯一性约束 唯一地标识表中的某一条记录,可以定义一列或多列为主键 限制不受主键约束的列上的数据的唯一性,用于作为访问某行的可选手段,一个表上可以放置多个唯一性约束 没有重复值,不允许空 没有重复值,允许空 可作外键 不可作外键
【例1】创建读者表
create table 读者 ( 读者卡号 nvarchar(10) primary key, 姓名 nvarchar(16) NOT NULL, 性别 nvarchar(1) NOT NULL default'男', 单位 nvarchar(30) NOT NULL, 办卡日期 date NOT NULL, 卡状态 nvarchar(5) NOT NULL, 类别编号 nvarchar(2), constraint c1 check(性别 in('男','女')), constraint c2 foreign key(类别编号) references 读者类别(类别编号) ) go
【例2】创建借阅表
create table 读者 ( 读者卡号 nvarchar(10), 图书编号 nvarchar(8), 借书日期 date NOT NULL, 还书日期 date, constraint c3 primary key(读者卡号,图书编号), constraint c4 foreign key(读者卡号) references 读者(读者卡号), constraint c5 foreign key(图书编号) references 图书(图书编号) ) go
(2)修改基本表
【例1】在读者表新增整型列id,该列作为每行数据的标识,并自动增加,初始值为1;在读者姓名一列增加唯一性约束,约束名为uk_dz_dzxm
use 高校图书管理 go alter table 读者 add id int indetity(1,1) go alter table 读者 whith nocheck --禁用约束检查 add constraint uk_dz_dzxm unique(姓名) go
注:
- identity是“自增标识列”,每个表只能创建一个标识列,数据类型只能是数值型,不能对标识列绑定默认值或default约束
- 如果想忽略对原有数据的约束检查,使用with nocheck——使增加的约束只对以后更新或插入的数据起作用,不能将with check/with nocheck用于主键和唯一性约束
- 新增列不能定义为NOT NULL
【例2】将读者类别表中类别编号设为主键
alter table 读者 add primary key(类别编号)
【例3】将读者表的id列和姓名列删除
alter table 读者 drop column id go alter table 读者 drop constraint uk_dz_dzxm go alter table 读者 drop column 姓名 go
注意:如果某列有约束或默认值,该列无法删除。必须先删约束,再删该列
3、索引的定义
索引机制:执行查询操作时,先查询索引文件得出元组在数据表的地址,再根据这个地址在数据表中直接取出该元组(跟map类似)
聚集索引与非聚集索引的区别
- 聚集索引一个表只能有一个,非聚集索引一个表可以存在多个
- 聚集索引存储记录是物理上连续存在,非聚集索引是逻辑上的连续,物理存储并不连续
- 聚集索引:物理存储按照索引排序,索引的键值逻辑顺序决定了表数据行的物理存储顺序。
- 非聚集索引:物理存储不按照索引排序,仅仅只是对数据列创建相应的索引,不影响整个表的物理存储顺序
- 聚集索引插入数据时速度要慢,查询数据比非聚集数据的速度快。
(1)创建索引
create [unique][clustered][nonclustered] index 索引名 on {表名|视图名}(列名1[ASC|DESC]……)
- unique 唯一性索引:不允许具有索引值相同的行,省略unique时,创建的是非唯一索引
- clustered 聚集性索引:省略clustered时,创建的是非聚集性索引
【例1】在图书表的书名列上,创建一个名称为idx_sm的唯一索引,并依据书名升序排序
use 高校图书管理 go create unique index idx_sm on 图书(书名) go
【例2】在读者表的姓名和性别列上,创建一个名称为idx_xmxb的唯一非聚集性复合索引,依据姓名升序、性别降序排序
create unique nonclustered index idx_xmxb on 读者(姓名,性别DESC)
(2)管理索引
① 修改索引
alter index 索引名 on 表名|视图名 {rebuild|reorganize|disable}
- rebuild:指定将相同的列、索引类型、唯一性属性、排列顺序重新生成索引,可以重新启动已禁用的索引
- reorganize:重新组织索引
- disable:将索引标志为禁用
② 删除索引
drop index 索引名
- 如果要删除为了实现主键或唯一约束而创建的索引,必须删除约束
- 在删除聚集索引时,表中所有非聚集性索引都被删除
③ 查看索引
exec sp_helpindex '表名'
【例1】查看读者表定义的索引信息
exec sp_helpindex '读者'
4、视图的定义
(1)创建视图
create view <视图名>(列名……) as<select 语句> [with check option]
- select语句中不允许含有compute (by)子句、into子句、distinct子句
- with check option:强制视图中执行所有的数据插入、删除和更新操作必须满足select的where表达式
① 行列子集视图
行列子集视图是指只删除基本表的某些行和某些列,但保留了码的视图
【例1】创建一个名为view_dw的视图,通过该视图,可以查询读者单位为"数计学院"的所有读者信息。该视图可以查询读者单位为"数计学院"的所有读者信息,要求透过该视图进行的更新操作只涉及数计学院的读者
create view view_dw as select * from 读者 where 单位='数计学院' with check option
select * from view_dw
② 连接视图
连接视图指多个数据表连接起来的视图
【例2】创建一个名为view_dzjy的视图,通过该视图可以查询每一个读者的姓名、性别、单位、图书编号、书名、借书日期、还书日期
create view view_dzjy as select xm,xb,dw,图书.tsbh,sm,jsrq,hsrq from 读者,图书,借阅 where 图书.tsbh=借阅.tsbh and 读者.dzkh=借阅.dzkh
③ 分组统计视图
通过使用聚集函数生成指定字段所需的统计数据
【例3】在高校图书管理数据库中,创建一个名为 view_dzjytj 的视图,要求通过该视图,可以查询每一位读者的借书册数
create view view_dzjytj(读者卡号,借书册数) as select 读者卡号,count(*) from 借阅 group by 读者卡号 --根据读者卡号分组
create view view_dzjytj as select 读者卡号,count(*) as 借书册数 from 借阅 group by 读者卡号 --根据读者卡号分组
【例4】创建一个名为view_dzjyxx的视图,要求通过该视图,可以查询每一位读者的详细信息和借书册数
可以将例3中的视图和读者表连接
create view view_dzjyxx as select 读者.*,借阅册数 from view_dzjytj,读者 where 读者.读者卡号=view_dzjytj.读者卡号
(2)修改删除视图
① 修改视图
alter view 视图名(列名……) as<select 语句> [with check option]
alter view 无法更改视图名称
② 删除视图
drop view 视图名
一个视图被删除后,由该视图导出的其他视图也将失效
(3)视图的查询和更新
① 视图更新的限制
- 视图的字段来自字段表达式或常数,则不允许对视图执行INSERT 和UPDATE操作,但允许执行DELETE操作
- 如果视图的字段来自聚合函数,则视图不允许更新
- 创建视图的SELECT语句中使用分组子句GROUP BY,以及TOP、DISTINCT、UNION关键字时,视图不允许更新
- 不能删除依赖于多个数据表的视图
- 视图定义中如果有嵌套查询,且内层查询中涉及了与外层一样的导出该视图的基本表,则视图不能更新
- 因为视图是从基本表导出的,所以对视图的插入、删除和更新操作必须遵守源基本表的完整性约束条件
② 使用视图更新数据
- 由于视图不是实际存储的,是虚表,因此对视图的更新最终要转换为对基本表的更新
- 为了防止用户通过视图对数据进行修改,无意或故意操作不属于视图范围内的基本数据,可在定义视图时加上with check option语句,这样在视图上进行数据更新时,DBMS会进一步检查视图定义中的条件,如果不满足,则拒绝执行该操作
二、数据查询语言DQL
1、select语句介绍
select [all|distinct] [top n[percent]] 目标列 [into 新表名] from 表组 [where 筛选条件] [group by<分组列名> [with{cube|rollup}]] [having <组选择条件>] [order by[all] <排序列名>[asc|desc]]
- select:写什么查询结果就显示什么。all是默认值,distinct去重,top n表示返回前n行,加上percent就是返回结果的前百分之n行
- into:用于创建一个新表,将查询结果插入该新表中。如果select有计算数值的字段,必须指定别名
- group by:当select后有统计函数,如果有group by,则统计为分组统计,否则是对整个结果集进行统计。group by后还可以跟having子句表达组选择条件,缩小查询范围
- order by:对结果集进行排序。ASC升序排序,DESC降序排序
2、简单查询
(1)查询表中的若干行
① top和distinct
【例1】查询借阅表的前4条记录信息
select top 4 * from 借阅
【例2】查询借阅了图书的读者卡号(去重)
select distinct 读者卡号 from 借阅
② 查询满足条件的元组
【例3】查询清华大学出版社出版的计算机类或管理类图书信息
select * from 图书 where 出版社='清华大学出版社' and 类别 in('计算机类','管理类') select * from 图书 where 出版社='清华大学出版社' and (类别='计算机类' or 类别='管理类')
③ 模糊查询
[not] like'<匹配串>'[escape '<换码字符>']
查找指定的属性列值与匹配串相匹配的记录。匹配串可以是一个完整的字符串,也可以是含有通配符的字符串
通配符包含:
- %:表示任意长度的字符串(长度可以为0)
- _:表示单个字符
- [ ]:表示方括号内字符列表的任意一个字符
- [^]:表示不在方括号内字符列表的任意一个字符
- [-]:表示方括号内-范围内的任何一个字符,例如[A-D]
- 如果用户要查询的字符串本身就有通配符,则用escape'换码字符'对通配符进行转义
- 例:查询"DB_1",查询like 'DB/_1' escape' / ',说明跟在' / '后的'_'不再是通配符,而是普通字符
【例4】查询书名中包含"数据库"的图书信息
select * from 图书 where 书名 like'%数据库%'
(2)对查询结果进行排序
- asc:空值最先显示
- desc:空值最后显示
【例1】查询出版日期在2014-05-01之后,书名第二个字为"理"的图书信息,按照出版日期降序、单价升序排序
select * from 图书 where 书名 like '_理%' and 出版日期>'2014-05-01' order by 出版日期 desc,单价
(3)汇总查询
① 聚合函数
常用聚合函数 函数名 功能 count(*) 返回表中所有总的数据记录个数,不管某列是否有空值 count([distinct | all] 列名) 返回表中不是空值数据记录的个数 avg([distinct | all] 列名) 返回列平均值 sum([distinct | all] 列名) 返回列的总和 max(列名) 返回列中最大值 min(列名) 返回列中最小值 【例1】查询图书表中计算机类图书的总册数
select count(*) as 总册数 from 图书 where 类别='计算机'
② group by分类汇总
group by 分组列 [with{cube|rollup}] [having 筛选条件表达式]
- cube:执行各分组查询字段的小计或加总
- rollup:执行针对第一个字段的加总
- having:对生成的组筛选中后,再对满足条件的组进行统计,必须和group by配合使用
【例2】查询借阅图书超过1本的读者卡号和借书册数,要求只统计2014-11-01以后的借书情况
select 读者卡号,count(*) as 借书册数 from 借阅 where 借书日期>'2014-11-01' group by 读者卡号 having count(*)>1
执行顺序:
- where先筛选掉不符合条件的记录
- group by将剩余记录按指定列分组
- having排除不符合条件的组
having和where的区别 where having 作用于基本表或视图,在分组前选择满足条件的元组 作用于组,在分组后对组进行筛选 不可用聚合函数 可以用聚合函数 可以使用from所含表内所有属性 只能使用select所含属性 【例3】在例2的情况基础上,对借书册数进行汇总
select 读者卡号,count(*) as 借书册数 from 借阅 where 借书日期>'2014-11-01' group by 读者卡号 with cube having count(*)>1
【例4】按年份统计每个读者的借书册数,并进行汇总
select 读者卡号,year(借书日期) as 年份,count(*) as 借书册数 from 借阅 group by 读者卡号,year(借书日期) with cube
group by子句可以是表名,也可以是表名表达式(year(借书日期)),但是不能是聚合函数
3、连接查询
(1)内连接 inner join
- 定义:内连接是将多个表中共同列的值进行比较,把满足条件的记录横向连接起来作为查询结果,为默认连接方式
- 实质:通过各表之间的共同列的关联来查询数据,连接条件通常是外键
① 等值连接
等值连接包含两个表的全部属性,存在重复情况,就是单纯地将两个表连接起来
【例1】查询每个读者的情况和借阅情况
select 读者.*,借阅.* from 读者 inner join 借阅 on 借阅.读者卡号=读者.读者卡号
- 当查询引用多个表时,如果某列是多个表的相同列,则列名必须使用表名限定,无相同列名则省略表名限定。eg:借阅.读者卡号、读者.读者卡号
② 自然连接
在等值连接基础上做到去重,则为自然连接
【例2】查询所有读者的姓名、性别、单位和读者所属的类别名称
select 姓名,性别,单位,读者.类别编号,类别名称 from 读者 inner join 读者类别 on 读者.类别编号=读者类别.类别编号
【例3】查询借阅了图书《微信公众号平台应用开发实战(第二版)》的读者姓名和所在单位
select 姓名,单位 from (图书 inner join 借阅 on 借阅.图书编号=图书.图书编号) inner join 读者 on 读者.读者卡号=借阅.读者卡号 where 书名='微信公众号平台应用开发实战(第二版)'
将第一个表和第二个表连接生成一个临时表,然后再连接第三个表
(2)外连接 outer join
- 定义:如果要求查询结果集中保留非匹配的元组,外连接中不匹配的结果称为NULL
① 左外连接 left join
查询结果集中保留连接表达式的左表中的非匹配记录,以及右表中符合条件的记录
【例1】查询读者姓名、单位、卡状态、借书日期和还书日期,要求查询结果也需要显示出没有借书的读者姓名、单位和卡状态
select 姓名,单位,卡状态,图书编号,借书日期 from 读者 left join 借阅 on 借阅.读者卡号=读者.读者卡号
左表:读者,右表:借阅
王武不满足连接条件,但作为左表,仍然输出
② 右外连接 right join
查询结果集中保留连接表达式的右表中的非匹配记录,以及左表中符合条件的记录
③ 全外连接 full join
两个表在连接过程中除了返回满足连接条件的行以外,还返回左表和右表中不满足条件的行
(3)自连接 self join
- 定义:自连接可以理解为自己连接自己,在一张表上面所进行的操作
【例1】查询在同一单位的读者卡号,读者姓名和单位
select distinct A.读者卡号,A.姓名,A.单位 from 读者 A inner join 读者 B on (A.读者卡号!=B.读者卡号 and A.单位=B.单位)
(4)交叉连接 cross join
- 定义:交叉连接查询是笛卡尔积运算,查询结果的字段数=两表字段数总和
- 去掉内连接的on后语句
select 读者.*,借阅.* from 读者 inner join 借阅
4、嵌套查询
(1)非相关子查询
- 非相关子查询:子查询不依赖父查询,每个子查询只进行一次
- 执行方式:先执行子查询,将子查询得到的结果集作为父查询的条件,进行父查询
① 用比较运算符的嵌套查询
【例1】查询办理借书卡比李丽晚的读者信息
select * from 读者 where 办卡日期 >(select 办卡日期 from 读者 where 姓名='李丽')
② 用all、any运算符的嵌套查询
all、any与比较运算符结合的含义 操作符 语义 >all 大于子查询结果中的所有值(大于最大值) >any 大于子查询结果中的某个值(大于最小值)
<all 小于子查询结果中的所有值(小于最小值) <any 小于子查询结果中的某个值(小于最大值) >=all 大于等于子查询结果中的所有值(≥ 最大值) >=any 大于等于子查询结果中的某个值(≥ 最小值) <=all 小于等于子查询结果中的所有值(≤ 最小值) <=any 小于等于子查询结果中的某一值(≤ 最大值) =all 通常无意义 =any 相当于in !=(或<>)all 相当于 not in !=(或<>)any 不等于子查询结果中的某一值 【例2】查询计算机类图书单价比管理类所有图书单价高的图书信息
select * from 图书 where 单价>all(select 单价 from 图书 where 类别='管理') and 类别='计算机'
③ 用in运算符的嵌套查询
【例3】查询借阅了图书《SQL入门》的读者姓名和单位
select 姓名,单位 from 读者 where 读者卡号 in(select 读者卡号 from 借阅 where 图书编号 in(select 图书编号 from 图书 where 书名='SQL入门'))
(2)相关子查询
- 相关子查询:子查询的查询条件要依赖于父查询的元组值
- 带有exist运算符的嵌套查询,子查询不返回任何数据,作用是用来检查子查询中是否有结果,如果有结果,父查询的where条件为true,否则为false
- 相关子查询处理过程:
- 先取父查询的第一个元组
- 根据该元组值,执行子查询
- 根据子查询返回的值判断该元组是否加入结果集
- 直到父查询的表全部处理完
- 将结果集输出
【例1】查询借阅了图书编号为"GL0002"的图书的读者姓名
select 读者卡号,姓名 from 读者 where exists(select * from 借阅 where 借阅.读者卡号=读者.读者卡号 and 图书编号='GL0002')
输出那些读者的读者卡号和姓名,当借阅表中存在他借阅了图书编号为GL0002图书的记录
由于子查询结果只返回true或false,因此子查询中select后通常是*
【例2】查询没有借阅图书编号为"GL0002"图书的读者姓名
select 读者卡号,姓名 from 读者 where not exists(select * from 借阅 where 借阅.读者卡号=读者.读者卡号 and 图书编号='GL0002')
select Reader.dzkh,xm from Reader inner join Borrow on Reader.dzkh=Borrow.dzkh where tsbh!='GL0002'
【例3】查询借阅了全部图书的读者姓名
查找这样的读者:不存在他没有借阅的书
select dzkh,xm from Reader where not exists(select * from Book where not exists(select * from Borrow where Borrow.dzkh=Reader.dzkh and Book.tsbh=Borrow.tsbh))
【例4】查询至少借阅了读者卡号为"1100001"读者所借阅的全部图书的读者姓名
转换:查询这样的读者:凡是1100001借阅的书,他都借阅了
P(y)表示1100001借阅的图书y
q(x,y)表示读者x借阅的图书y
查找这样的读者:不存在1100001借阅的书,他没有借阅
select xm from Reader where not exists(select * from Borrow A where A.dzkh='1100001' and not exists (select * from Borrow B where B.dzkh=Reader.dzkh and B.tsbh=A.tsbh))
5、组合查询
(1)union并操作
【例1】查询借阅了图书编号为"JSJ001"或"GL0003"的读者卡号
select 读者卡号 from 借阅 where 图书编号='JSJ001' union select 读者卡号 from 借阅 where 图书编号='GL0003'
(2)intersect交操作
【例2】查询既借阅了图书编号为"JSJ001",又借阅了图书编号为"GL0003"的读者卡号,姓名,单位
select 读者.读者卡号,姓名,单位 from 读者 inner join 借阅 on 读者.读者卡号=借阅.读者卡号 and 图书编号='JSJ001' intersect select 读者.读者卡号,姓名,单位 from 读者 inner join 借阅 on 读者.读者卡号=借阅.读者卡号 and 图书编号='GL0003'
select 读者.读者卡号,姓名,单位 from 读者 inner join 借阅 on 读者.读者卡号=借阅.读者卡号 and 图书编号='JSJ001' and 读者.读者卡号 in(select 读者卡号 from 借阅 where 图书编号='GL0003')
select 读者卡号,姓名,单位 from 读者 where 读者卡号 in(select 读者卡号 from 借阅 A where 图书编号='JSJ001' and exists(select * from 借阅 B where A.读者卡号=B.读者卡号 and 图书编号='GL0003'))
(3)except差操作
【例3】查询没有借阅图书编号为"GL0002"图书的读者姓名
select 读者卡号 from 读者 except select 读者卡号 from 借阅 where 图书编号='GL0002'
三、数据更新语言DML
1、插入数据
(1)插入单个元组
insert into 表名(属性列名1,属性列名2…) values(常量1,常量2…)
- into关键字和属性列表可有可无,且属性列不需要包含全部字段。
- 可以省略列名,但必须保证values后的各数据项位置与表定义时的顺序一致,否则报错。
- 如果给出表名后的属性列名,则表名后的属性名清单不需要和表结构的字段数目或顺序相同,但要和values子句的属性值顺序一致。
- 如果只给出部分属性列名,则对于not null属性的字段必须列出属性列名,因为没有出现在子句中的属性将取空值,假如这些属性已定义为not null,将会出错。
- 数据插入时,value子句中的属性值、字符与日期时间型数据需要使用单引号括起。如果对应属性列是默认值,可以使用default关键字;如果对应属性值是空值,直接使用null。
【例1】 向高校图书管理数据库的读者类别表中分别插入两条记录。
insert into 读者类别(类别编号,类别名称,可借阅天数,可借阅数量,超期罚款额) values('01','教师',90,6,0.2) insert into 读者类别 values('02','博士研究生',80,6,0.4)
【例2】向读者表插入三条记录
【例3】向图书表插入三条数据
(2)插入多个元组
① 行构造器
insert into 图书 values ('JSJ003','SQL Server 2012数据库应用与开发教程','计算机','卫琳','清华大学出版社','2014-08- 01 ',42,5), ('JSJ004','疯狂Java 讲义(第3版)','计算机','李刚','电子工业出版社','2014-07-01',109.00,5)
② insert/select命令
insert into 表名(属性列名) <子查询>
- 要求子查询的目标列和插入表的列数据类型要相同
- 如果属性列的顺序、个数都相同,则可省略属性列名
【例1】把图书订购数据库中图书信息表(books)的数据记录插入高校图书管理数据库中的图书表中。
insert into 图书 select * from 图书订购.dbo.books
因为要访问的数据库对象图书信息表(books)与正在使用的数据库高校图书管理在同一台服务器上,但不在同一个数据库中,因此,引用图书信息表(books)时可以省略服务器名,但必须指定数据库名图书订购和架构名dbo。
【例2】 求每一类别图书的总册数,并把结果存入高校图书管理数据库中。
create table 图书类别库存量(类别nvarchar(40),总册数tinyint) go insert into 图书类别库存量 select 类别,SUM(库存数量) from 图书 group by 类别 go
2、修改数据
(1)修改数据语法格式
update 表名 set 列名1=表达式,列名2=表达式… [from 数据源] [where 条件表达式]
- where子句:指定要修改的记录需要满足的条件。如果没有where,则表示要修改指定表中的所有元组。where可以嵌入查询语句
(2)修改单个元组
修改单个元组,可以同时修改数据表中的多个属性列值
【例1】 在读者表中,修改读者卡号为“1100004”的记录,将卡状态的值改为挂失,将单位的值修改为全称“数学与计算机科学学院”。update 读者 set 卡状态='挂失',单位='数学与计算机科学学院' where 读者卡号='1100004'
【例2】 在【例1】的结果上,将读者卡号为“1100004”的读者单位修改为与读者卡号为“1100001”相同的单位名称。update 读者 set 单位=(select 单位 from 读者 where 读者卡号='1100001') where 读者卡号='1100004'
(3)修改多个元组
在实际业务中,有时需要同时修改整个表的某些属性列的值,或者是复合条件的某些属性列的值
【例3】 将读者表的办卡日期属性列的值统一修改为“2015-05-10”。update 读者 set 办卡日期='2015-05-10'
【例4】 将读者赵亮所借图书的还书日期统一修改为“2015-03-10”。update 借阅 set 还书日期='2015-03-10' where 读者卡号 in(select 读者卡号 from 读者 where 姓名='赵亮')
此例也可以用下面的代码实现:
update 借阅 set 还书日期='2015-03-10' from 借阅 inner join 读者 on 借阅.读者卡号=读者.读者卡号 where 姓名='赵亮'
update命令不仅可以更新同一个数据表的记录数据,使用JOIN命令就可以进行不同数据表的合并更新
3、删除数据
(1)delete
delete [from] 表名 [where 条件] delete 表名 from 表名 连接另一表 [where 条件]
where子句:指定要删除的记录需要满足的条件。如果没有where,则表示要删除指定表中的所有元组。where可以嵌入查询语句
【例1】 删除书名为《请不要辜负这个时代》的图书信息。
delete from 图书 where 书名='请不要辜负这个时代'
【例2】 删除读者杨少华的所有借阅记录。delete 借阅 from 借阅 inner join 读者 on 借阅.读者卡号=读者.读者卡号 where 姓名='杨少华'
或使用以下的表示形式:
delete 借阅 from 借阅,读者 where 借阅.读者卡号=读者.读者卡号 and 姓名='杨少华'
或使用带有子嵌套查询的DELETE语句:
delete 借阅 where 读者卡号 in(select 读者卡号 from 读者 where 姓名='杨少华')
如果删除涉及多个表,则DELETE子句之后必须列出要删除数据所属的表名。
注意:DELETE语句删除的是表中的数据,而不是表的结构,即使表中的数据全部被删除,表的结构仍在数据库中。
(2)truncate
如果删除整个数据表,T-SQL语言中还提供了一个truncate table命令,用于删除数据表的所有记录。其语法格式如下:
truncate table 表名称
truncate table和delete的不同点:
- truncate table删除记录的数据快,且不会将删除记录的操作写入事务日志,delete会写入事务日志。
- truncate table删除完记录后,标识列会重新开始记数,而用delete语句删除之后,从上次最后记录为开始点继续记数。
- 如果要删除记录的表是其他表外键指向的表,那么不能用runcate table语句删除,只能用delete。
4、数据更新操作检查完整性
(1)插入数据时需要分别检查实体完整性与参照完整性
- 检查实体完整性:若插入元组的主码值不为null,并且相应属性值在关系中不存在,则可执行插入操作,否则不执行
- 检查参照完整性:
- 若向参照关系中插入元组,则要检查外码值是否在被参照关系中存在对应的主码值,存在则插入,否则不执行插入操作
- 若要插入元组的外码允许为空,则外码是空值时插入
- 如果向被参照关系插入元组,则无法检查参照完整性
- 注:外码——被参照关系的主码 主码——参照关系的主码
(2)删除数据时需要检查参照完整性
- 如果删除的是参照关系的元组,则不需进行参照完整性检查,可执行删除操作
- 如果删除的是被参照关系的元组,则检查被删除元组主码的属性值是否被参照关系中某元组的外码引用,未引用则删除,否则:
- ① 不可执行删除操作,拒绝删除
- ② 可删除,但需同时将参照关系中引用了该元组的对应元组一起删除,即执行级联删除
- ③ 可删除,则需将参照关系的外码值置为null
(3)修改数据时需要检查实体完整性与参照完整性修改操作即先删除旧元组,再插入新元组,因此检查完整性同执行删除和插入操作
四、数据控制语言DML
1、数据控制方法
- 授权定义
- 存权处理
- 查权操作
2、sql server数据库操作权限
- 对象权限
- select、insert、update、delete、execute
- 系统权限
- create database、create table、create view、create default……
- 隐含权限
3、对象权限设置
grant/revoke/deny all|对象权限组 on 对象名 to|from 用户组|public [with grant option]
- grant:授予
- revoke:回收、撤销
- deny:取消
- all:指所有的对象权限
- to:在授予、拒绝访问权限时使用
- from:在收回权限时使用
- public:指数据库的所有用户
- with grant option:指获得权限的用户可以把该权限再授予其他用户
【例1】授予用户user1图书表上insert、select权限,并允许他将得到的权限授予其他用户
grant select,insert on 图书 to user1 with grant option
4、语句权限设置
grant/revoke/deny 系统权限组 to|from 用户组|public [with grant option]
【例2】把授予用户user2创建表的权限收回,授予除user3以外的其他所有用户创建视图的权限
revoke create table from user2 grant create view to public deny create view to user3
五、数据库初步编程
1、变量
- 全局变量:@@变量名,只能预先定义,不能修改
- 局部变量:@变量名
(1)变量的声明
declare @变量名1 数据类型[=初值],@变量名2 数据类型[=初值]……
【例1】创建3个局部变量
declare @name varchar(10),@address char(30),@total int =1
(2)变量的赋值
① set赋值
set @变量名=表达式
【例2】将计算机类图书的库存总量赋给变量total,并使用print显示变量值
declare @total tinyint set @total=(select sum(库存数量) from 图书 where 类别='计算机') print'库存总量:'+cast(@total as char)
② select赋值
【例3】将读者表中女读者的姓名和单位的域值存入局部变量中
declare @myname nvarchar(16),@mydepat nvarchar(30) select @myname=姓名,@mydepat=单位 from 读者 where 性别='女' select @myname as 姓名,@mydepat as 单位
③类型转换函数
cast(表达式 as 数据类型) convert(数据类型[长度],表达式)
2、流程控制
(1)begin end
相当于c语言中的{}
(2)if else
【例1】统计读者卡号为“1100002”的读者的借书数目,如果不少于两本就显示“你借阅了×本图书,很好,祝你阅读愉快!”,否则显示“你借阅了×本图书,借书有点少,请多提宝贵建议!”(其中×表示借书数目)
DECLARE @cn smallint,@text varchar(100) SET @cn=(SELECT COUNT(图书编号) FROM 借阅 WHERE 读者卡号='1100002') IF @cn>=2 BEGIN SET @text='你借阅了'+CAST(@cn AS char(2)) SET @text=@text+'本图书,很好,祝你阅读愉快!' END ELSE BEGIN SET @text='你借阅了'+CAST(@cn AS char(2)) SET @text=@text+'本图书,借书有点少,请多提宝贵建议!' END SELECT @text AS 借书提示信息
(3)while
(4)case
【例2】 使用CASE 函数根据读者“类别编号”判断读者所属的读者类别名称。
SELECT 读者卡号,姓名, CASE类别编号 WHEN '01'THEN '教师' WHEN '02'THEN '博士研究生' WHEN '03'THEN '硕士研究生' WHEN '04'THEN '本科生' ELSE '没有录入' END AS 读者所属类别 FROM 读者
【例3】 统计不同类别图书的平均单价及评价信息,评价信息用贵、稍贵、可以接受、便宜、非常便宜表示,要求统计结果以平均单价的降序进行排序。当平均单价大于等于50,评价为“贵”;当平均单价大于等于40 小于50,评价为“稍贵”;当平均单价大于等于30小于40,评价为“可以接受”;当平均单价大于20 小于30,评价为“便宜”;否则评价为“非常便宜”。
SELECT 类别,STR(AVG(单价),4,1) AS '平均单价', CASE WHEN AVG(单价)>=50 THEN '贵' WHEN AVG(单价)>=40 THEN '稍贵' WHEN AVG(单价)>=30 THEN '可以接受' WHEN AVG(单价)>=20 THEN '便宜' ELSE '非常便宜' END AS '评价信息' FROM 图书 GROUP BY 类别 ORDER BY 平均单价 DESC
将数值转换为字符str函数
str(表达式,字符串总数,小数保留位)
- 表达式是一个带小数点的近似数字数据类型的表达式
- length表示总长度(总长度包括小数点、符号、数字以及空格)
- decimal 指定小数点后的位数
(5)waitfor
waitfor delay|time
- DELAY:指定必须延迟的一段时间,例如10s,最长可为24h
- TIME:指定运行程序的时间点,例如上午10点
用来暂时停止批处理、存储过程或事务等程序的执行,直到所设定的等待时间已过或所设定的时刻快到,才继续往下执行。延迟时间和时刻的格式为“HH:MM:SS”
【例4】延迟10s后查询图书表
waitfor delay '00:00:10' select * from 图书
(6)goto
GOTO关键字可以更改执行流程到指定的标签处,跳过GOTO后面的T-SQL语句,并从标签位置继续处理。GOTO可在程序中的任何位置使用。其语法格式如下:
标签名称: <语句组> GOTO 标签名称
- 使用GOTO 跳转时,需要指定跳转标签的名称。标签的定义可在GOTO之前或之后,标签名称可以是数字和字符的组合,但必须以“:”结尾。
- GOTO关键字最常用在跳出嵌套循环,因为BREAK 只能跳出本层WHILE 循环,如果需要跳出整个嵌套循环,则需使用GO关键字。
(7)注释
- 多行注释格式:/*注释语句*/
- 单行注释格式:--注释语句
3、存储过程编程
存储过程是为了实现特定任务,而将一些需要多次使用的、能够完成特定功能的操作语句编写成SQL语句的集合,该集合编译后放在数据库服务器上,用户应用程序通过指定存储过程的名称来调用并执行存储过程中的语句。
也就是说,存储过程是被存储在数据库中的可以接受和返回用户提供参数的SQL程序,在创建时被编译和优化,创建后可被程序调用。
(1)创建存储过程
create procedure <存储过程名> [{@参数名数据类型}][=默认值][output] [with {recompile|encryption}] as T-SQL语句块 go
- @参数名:在创建存储过程时可以声明一个或多个参数。参数包括输入和输出参数,输入参数提供执行存储过程所必需的变量值,输出参数用于返回执行存储过程后的一些结果值。参数实质是局部变量,只在声明的存储过程内有效。
- output:输出参数,在存储过程退出后,output值将返回调用程序,以便在调用该存储过程的程序中获得并使用该参数值。
- with recompile:每次执行时都对其进行重新编译。有时修改了存储过程中使用的数据对象,若直接运行之前已编译好的存储过程,可能会出现运行错误,为了避免此类情况的发生,使用recompile,但降低了执行速度。
- with encryption:对创建的存储过程文本进行加密,防止他人查看或修改。
- as:指定该存储过程要执行的操作。
- T-SQL语句块:用于定义存储过程执行的各种类型的T-SQL语句。
(2)执行存储过程
execute 存储过程名 [@参数名=]参量值|@变量
(3)存储过程应用实例
【例1】创建一个名为 pro_dzjy 的存储过程,查询每个读者借阅图书的信息,包括读者卡号、图书编号、书名、借书日期。
create procedure pro_dzjy AS select 读者卡号,借阅.图书编号,书名,借书日期 from 图书 inner join 借阅 on 借阅.图书编号=图书.图书编号 go
【例2】创建一个名为proc_tsxx的存储过程,用于根据出版社和图书类别查询图书信息,并利用该存储过程查询清华大学出版社出版的计算机类的图书信息。
create procedure proc_tsxx @cbs nvarchar(20), --输入参数 @tslb nvarchar(16) as select * from 图书 where 出版社=@cbs and 类别=@tslb go exec proc_tsxx '清华大学出版社','计算机'
- 调用存储过程时,可以不给出参数名,但参数值必须和定义参数的顺序一致。
- 如果不按照定义的参数顺序传递参数,则要指定参数名。
exec proc_tsxx @tslb='计算机类',@cbs='清华大学出版社'
【例3】输入某个读者的读者卡号,统计该读者的借书册数,并返回该读者的姓名和借书册数。
create procedure proc_dzjstj @dzkh char(10), --输入参数 @dzxm char(16) output, --输出参数 @jscs tinyint output as begin select @dzxm=xm from Reader where dzkh=@dzkh select @jscs=count(*) from Borrow where dzkh=@dzkh group by dzkh end go declare @xm varchar(16),@cs tinyint --output参数必须指定变量来接收 exec proc_dzjstj '110002',@xm output,@cs output --dzkh是输入变量,xm和cs都是输出变量 print'姓名为'+@xm+',借书册数为'+str(@cs)
(4)删除和修改存储过程
drop procedure 存储过程名 alter procedure 存储过程名
4、触发器编程
(1)DML触发器
DML触发器是当执行insert、update、delete时,一种自动执行的触发器。
① 创建DML触发器
create trigger 触发器名称 on 表名|视图名 [with encryption] for|after|instead of [insert,][update,][delete] as if update(列) T-SQL语句
- 表 | 视图:指定操作的对象为表或视图,视图只能被instead of触发器引用。
- with encryption:对创建的触发器文本进行加密。
- for | after:指定触发器只有在触发事件包含的所有操作都已成功执行后才被激活。如果仅指定for关键字,则after是默认设置。
- instead of:即用触发器程序取代相应的操作,当对表或视图执行instead of后面指定的操作时,仅执行触发器程序。此功能主要用于使不能更新的视图支持更新。
- [insert][,][update][,][delete]:指定在表或视图用于激活触发器的操作类型,必须至少指定一个选项。在触发器定义中允许使用以任意顺序组合的多个选项。如果指定的选项多于一个,需要用逗号分隔。
- update(列):在触发器程序中被用来判断哪些列被修改(INSERT和UPDATE)。
- T-SQL语句块:用于定义触发器执行的各种类型的T-SQL语句。
② DML触发器工作原理
一个触发器只适用于一个表,每个表最多只能有3个触发器,分别是insert、delete、update
在创建DML 触发器的语句中可以使用两个特殊的表:
- inserted 表(插入的表)
- deleted 表(删除的表)
SQL Server会自动创建和管理这两种表,这两张表是逻辑表,是只读的,驻留在内存而不是存储在数据库中,并且由系统管理的。不允许对其修改,两个表的结构总是与被该触发器作用的表的结构相同,当触发器工作完成,两个表也被删除。
- inserted 表用于存储insert和update语句所影响的行的副本
- deleted 表用于存储delete和update语句所影响的行的副本
INSERT 触发器:当执行insert操作时,先向inserted 表中插入一个新行的副本,然后检查inserted 表中的新行是否有效,确定是否要阻止该插入操作。如果所插入的行中的值是有效的,则将该行插入到触发器表。
UPDATE触发器:当执行update操作时,先将旧数据行保存到deleted表中,然后将新数据插入inserted 表中,最后计算deleted 表和inserted表中的值以确定是否进行干预。
DELETE触发器:当执行delete操作时,将旧数据行移到deleted 表中,计算deleted表中的值决定是否进行干预,如果不进行干预,则删除数据行。
③ DML触发器应用实例
【例1】创建触发器,保证读者表中性别只能为男或女
分析:因为破坏约束"性别只能为男或女"的操作是插入和修改操作,因此需要建立insert和update两个触发器,如果在inserted表中有性别取值不为男或女的记录,取消本次操作
create trigger tri_t1 on 读者 for insert,update as if exist (select * from 读者 where 性别 not in('男','女')) begin print '读者性别必须为男or女' rollback end
【例2】利用触发器保证删除读者记录的同时,也要把相应的借阅记录删除。
create trigger tri_t2 on 读者 for delete as if(select count(*) from 借阅,deleted where 借阅.读者卡号=deleted.读者卡号)>0 begin delete 借阅 from 借阅 inner join deleted on 借阅.读者卡号=deleted.读者卡号 end
只有在成功执行触发T-SQL语句之后,才会激活AFTER触发器
以删除表中记录为例,整个执行分为如下步骤。
- 当系统接收到一个要执行删除读者表中的记录的T-SQL语句时,系统将要删除的记录存放在deleted表中。
- 把读者表中的相应记录删除。
- 删除操作激活了事先编制的AFTER触发器,系统执行AFTER触发器中AS定义后的T-SQL语句。
- 触发器执行完毕后,删除内存中的deleted表,退出整个操作。若触发器语句执行失败,则整个过程回滚,恢复到初始状。
【例3】 图书表中的图书编号是唯一且不可改变的,创建触发器,实现更新图书编号的不可改变性。CREATE TRIGGER tri_t3 ON 图书 FOR UPDATE AS IF UPDATE(图书编号) BEGIN PRINT'每一个图书编号是唯一的,不能改变' ROLLBACK TRANSACTION END
【例4】 使用触发器实现当向借阅表中新增一条借阅记录时,更新图书表中对应图书的库存量的值。CREATE TRIGGER tri_t4 ON 借阅 FOR INSERT AS BEGIN UPDATE 图书 SET 库存数量=库存数量-1 WHERE 图书编号=(SELECT 图书编号 FROM inserted) END
【例5】 为读者类别表创建一个INSERT操作类型的触发器。当插入的新行中“类别名称”的值是“专科生”时,就撤销该插入操作,并使用RAISERROR 语句返回一个错误信息。CREATE TRIGGER tri t5 ON 读者类别 FOR INSERT AS DECLARE @lbmc char(10) --声明变量 SELECT @lbmc=类别名称 FROM inserted --获取新数据行类别名称的值 IF @lbmc='专科生' BEGIN ROLLBACK TRANSACTION --撤销插入操作 RAISERROR('不能插入专科生的读者信息!',16,10) --RAISERROR语句返回错误信息 END
(2)DDL触发器
DDL 触发器是在执行DDL 命令(create、alter、drop、grant、revoke 等)后触发执行的,所以不能创建类似于DML触发器的INSTEAD OF 触发器
① 创建DDL触发器
create trigger 触发器名 on {all 服务器|数据库}{for|after} {事件类型} as T-SQL语句
- ALL服务器:是指将DDL触发器作用到整个当前的服务器上。如果指定了这个参数,在当前服务器上的任何一个数据库都能激活该触发器。
- 数据库:数据库表示将DDL 触发器作用域应用于当前数据库,只能在当前数据库上激活该触发器。
- FOR或AFTER:是同一个意思,可以指定FOR,也可以指定AFTER,DDL触发器不能指定INSTEAD OF触发器。
- 事件类型:指可以激发DDL触发器的事件,主要是以create、alter、drop开头的T-SQL语句。
使用DDL触发器的时机如下所示:
- 保护数据库模式不会改变。
- 记录数据库模式的改变或相关事件。
- 希望在更改数据库模式时,有一些响应来进行额外处理。
② DDL触发器实例
【例1】创建一个名为tri t6的触发器,当创建数据库时,系统返回提示信息“DATABASE CREATED”。
CREATE TRIGGER tri_t6 ON ALL SERVER FOR CREATE_DATABASE AS PRINT 'DATABASE CREATED'
成功运行创建触发器后,执行如下测试语句:
CREATE DATABASE xx
消息栏内会出现设计的“DATABASE CREATED”。
注意:系统不会为DDL 触发器创建inserted 表和deleted 表。
(3)删除和修改触发器
delete trigger 触发器名称