SQL功能 | 谓词 |
---|---|
数据定义DDL | CREATE, DROP, ALTER |
数据查询DQL | SELECT |
数据操作DML | INSERT, UPDATE, DELETE |
数据控制DCL | GRANT, REVOKE, DENY |
数据类型 | 描述 | 存储 |
---|---|---|
tinyint | 0~255 | 1字节 |
smallint | -32768~32767 | 2字节 |
int | -21 4748 3648 ~ 21 4748 3647 | 4字节 |
bigint | -922 3372 0368 5477 5808 ~ 922 3372 0368 5477 5807 | 8字节 |
decimal(p, s)或numeric(p, s) | -1038+1 ~ 1038 , p是最大位数【1~38之间】默认是18;s:小数点右侧存储的最大位数,s必须是0~p之间的值,默认是0 | 5-17字节 |
smallmoney | -21 4748.3648 ~ 21 4748.3647 | 4字节 |
momey | -922 3372 0368 5477.5808 ~ 922 3372 0368 5477.5807 | 8字节 |
smalint, decimal会用即可
数据类型 | 描述 | 存储 |
---|---|---|
float(n) | -1.79E+308~1.79E+308。n:24保存4字节;53:就保存8字节 | 4/8字节 |
real | -3.04E+38~3.4E+38 | 4字节 |
一般使用float
数据类型 | 描述 | 存储 |
---|---|---|
datetime | 1753.1.1~9999.12.31, 精度3.33ms | 8字节 |
datetime2 | 1753.1.1~9999.12.31, 精度100ns | 6~8字节 |
smalldatetime | 1900.1.1~2079.6.6, 精度1min | 4字节 |
date | 进存储日期,0001.1.1~9999.12.31 | 3字节 |
time | 仅存储时间, 精度100ns | 3~5字节 |
datatimeoffset | 与datetime2相同,外加时区偏移 | 8~10字节 |
tiemstamp | 存储唯一的数字, 每当创建或修改某行时,该数字会更新,timestamp基于内部时钟,不针对真实时间,每个标志能有一个timestamop变量 |
数据类型 | 描述 | 存储 |
---|---|---|
char(n) | 固定长度字符串,0~8000个字符 | n字节 |
varchar(n) | 可变长度,0~8000个字符 | 由实际长度决定 |
varchar(max) | 可变长度字符串,0~1 0737 41824个字符 | 由实际长度决定 |
text | 可变长度的字符串,最多2GB字符数据 | 由实际长度决定 |
此处省略
只需要在普通编码字符串的数据类型前加上一个n即可实现统一的Unicode格式的编码,存储长度除了ntext不变以外,其余均减半
举例:
数据类型 | 描述 | 存储 |
---|---|---|
nchar(n) | 固定长度字符串,0~4000个字符 | n字节 |
总结:以后的实际应用中只考虑使用统一编码何时规范即可,
数据类型 | 描述 | 存储 |
---|---|---|
bit | 允许0, 1, NULL | |
binary(n) | 固定长度二进制数据,0~8000B | nB |
varbinay(n) | 可变长度二进制数据,0~8000B | 由实际长度决定 |
varbinay(max) | 可变长度二进制数据,0~2GB | 由实际长度决定 |
image | 可变长度二进制数据,0~2GB | 由实际长度决定 |
对于双属性的元素比如人的性别,图书的借还等可以设置bit来节约空间
数据类型 | 描述 |
---|---|
sql_variant | 存储最多8000字节不同数据类型的数据, 除了text, ntext,以及timestamp |
uniqueidetifier | 存储全局标识符(GUID) |
xml | 存储XML格式化数据,0~2GB |
cursor | 存储对于数据库操作的指针的引用 |
table | 存储结果集,共稍后处理 |
二进制数据类型接触的比较少,所以了解即可
以上内容是笔者将书中的全面内容搬运过来,做了稍加的整理和解释说明。以后的开发中也用不到这么多的种类,因此不必全部都学会但是一定要有了解,知道用的时候选择什么样的数据类型最合适。
比如说生活中的小数据:全国新冠疫情疫苗接种人数就用int,银行必须使用decimal类型、因为客户数据重要,需要用到大内存的数据类型,全国人口的出生数据记录,为了日后能继续使用因此需要设置为datetime或者datetime2即可,两者之时精度不同罢了。。。
数据准备
创建 Student 表结构
列名 | 含义 | 数据类型 | 约束 |
---|---|---|---|
Sno | 学号 | char(6) | 主键 |
Sname | 姓名 | 非空 | |
Ssex | 性别 | nvarcahr(20) | 非空;默认为男;检查只能是"男"或"女" |
Sbirthday | 出生日期 | smalldatetime | |
Sdept | 所在系 | nvarchar(20) | |
Memo | 备注 | text |
CREATE DATABASE Stu
USE Stu
--创建学生表
CREATE DATABASE Stu
USE Stu
--创建学生表
CREATE TABLE Student(
Sno CHAR(6) PRIMARY KEY,
Sname NCHAR(20) NOT NULL,
Ssex NCHAR(2) NOT NULL DEFAULT '男' CHECK (Ssex IN ('男', '女')),
Sbirthday SMALLDATETIME,
Sdept NVARCHAR(20),
Memo text
)
创建 Course 表结构
列名 | 含义 | 数据类型 | 约束 |
---|---|---|---|
Cno | 课程号 | char(3) | 主键 |
Cname | 课程名 | nvarchar(20) | 非空 |
PreCno | 先修课程号 | char(3) | |
Credit | 学分 | tinyint | |
Semester | 开课学期 | tinyint |
--创建Course表
CREATE TABLE Course
(
Cno CHAR(3) PRIMARY KEY,
Cname NVARCHAR(20) NOT NULL,
PreCno CHAR(3),
Credit TINYINT
Semester TINYINT,
)
创建 SC 结构
列名 | 含义 | 数据类型 | 约束 |
---|---|---|---|
Sno | 学号 | char(6) | 主键,外键 |
Cno | 课程号 | nchar(20) | 主键,外键 |
Grade | 成绩 | smallint | 检查:成绩[0,100] |
--创建SC表
CREATE TABLE SC
(
Sno CHAR(6),
Cno NCHAR(20),
Grade SMALLINT CHECK (BETWEEN 0 AND 100)
CONSTRAINT PK_sno_cno PRIMARY KEY (Sno, Cno),
CONSTRAINT FK_sno FOREIGN KEY(Sno) REFERENCES Student(Sno)
)
在创建主外键的时候需要注意类型一致和形参一致
SC表的Sno字段要和Student表的Sno保持一致
-- 插入Student数据
INSERT INTO Student VALUES
(
('060101', '钟文辉', '男', '1997-05-01', '计算机系', '又系毕业生'),
('060102', '吴细文', '女', '1997=03-24', '计算机系', '爱好:音乐'),
('060103', '吴朝西', '男', '1998-07-01', '计算机系', ''),
('070101', '王冲瑞', '男', '1998-05-04', '机电系', '爱好:音乐')
('070102', '林滔滔', '女', '1997-04-03', '机电系', '爱好:体育'),
('070103', '李修雨', '女', '1996-03-03', '机电系', ''),
('070301', '李奇', '男', '1998-09-17', '信息管理', '')
)
--插入 Course数据
INSERT INTO Course VALUES
(
('C01', '高等数学', '', 4, 1),
('C02', '程序设计', '', 4, 2),
('C03', '数据结构', 'C02', 3, 3),
('C04', '数据库原理', 'C03', 3, 4),
('C05', '音乐欣赏', '', 1, 4),
('C06', '大学物理', 'C01', 4, 2),
('C07', '计算机网络', 'C02', 2, 4)
)
--插入SC数据
INSERT INTO SC VALUES
(
('060101', 'C01', 91),
('060101', 'C03', 88),
('060101', 'C04', 95),
('060101', 'C05'),
('060102', 'C02', 81),
('060102', 'C03', 76),
('060102', 'C04', 92),
('070101', 'C01', 50),
('070101', 'C03', 86),
('070101', 'C04', 90),
('070101', 'C05'),
('070103', 'C04', 52),
('070103', 'C06', 47),
('070301', 'C03', 87),
('070301', 'C04', 93)
)
把需要查询的列名加上即可或者用通配符 * 来代替所有列名
select 查询字段可以是表中存在的列也可以是表达式,常量或者函数
示例1:YEAR()和GETDATE()函数使用
SELECT Sname, YEAR(GETDATE())-YEAR(Sbirthday) FROM Student
YEAR():把当前出绳子断转为年
GETDATE():得到当前年
【AS别名】
示例2:AS可以省略
SELECT Sname 姓名,
YEAR(GETDATE()) - YEAR(Sbirthday) 年龄,
'今年是' 今年是,
YEAR(GETDATE()) 年份
FROM Student
BETWEEN…AND
查询条件 | 谓词 |
---|---|
关系运算符 | <, <=, ==, <>, !=, >=, > |
确定范围 | BETWEEN…AND, NOT BETWEEN…AND【左闭右闭区间】 |
确定集合 | IN, NOT IN |
字符匹配 | LIKE, NOT LIKE |
空值 | IS NULL, IS NOT NULL |
多重条件 | AND, OR |
关于 NULL 带来的影响,关系运算符中只有 <> 可以正确的判断 NULL
最好习惯性使用 IS NULL 和 IS NOT NULL 来对 NULL 值判断
OR优先级低于AND,因此在使用的时候需要使用()进行优先级区分
示例额3:查询在1997年出生的学生全部信息
日期也可以使用 BETWEEN…AND
SELECT * FROM Student WHERE Sbirthday BETWEEN '1997-01-01' AND '1997-12-31'
字符 | 含义 |
---|---|
- | 匹配任意一个字符 |
% | 匹配0个到多个字符 |
[] | 表示匹配匹配abcdg字符中任意一个,如要连续匹配[b-d]用 ‘-’ |
[^] | 不匹配任意一个字符, [^abcdg]表示不匹配abcdg任意一个字符;[^b-e]不匹配abcde这段连续字符中的任意一个字符 |
示例4:查询姓名第二个字是“冲”的学生信息
SELECT * FROM Student WHERE Sname LIKE '_冲%'
示例5:查询姓名“李”字开头的学生信息
SELECT * FROM Student WHERE Sname LIKE '李%'
示例6:查询学号最后一位是“2”或“3”的学生信息
SLECT * FROM Student WHERE Sno NOT LIKE '%[2,3]'
示例7:转义子符
ESCAPE:的使用
包含30%
SELECT * FROM Table WHERE filed LIKE '%30!%%' ESCAPE '!'
包含_
SELECT * FROM Table WHERE field LIKE '%!_%' ESCAPE '!'
关键字是:ORDER BY field DESC|ASC
升降/序 | 含义 |
---|---|
DESC | 降序 |
ASC | 升序 |
函数 | 含义 |
---|---|
COUNT | 统计去重后的数据个数 |
SUM | 累加和(必须是数值类型) |
AVG | 平均值(必须是数值类型) |
MAX | 得到列的最大值 |
MIN | 得到列的最小值 |
统计函数中除(*)之外,其它函数在计算过程中忽略NULL值
统计函数的计算范围可以是满足WHERE字句条件的记录,也可以满足条件的组进行计算【分组的话需要使用HAVING函数】
带有 GROUP BY 子句的 SELECT 语句的查询列表中只能出现分组依据列和聚合函数。因为每组中除了这两类相同外,其余属性的值可能不唯一
示例1:统计每门课程的选课人数,列出课称号和选课人数
SELECT Cno 课称号, COUNT(Sno) 选课人数
FROM SC
GROUP BY Cno
执行过程分析:
- 首先对SC表的数据按照Cno的值进行分组,所有具有相同Cno值的元素归位一组
- 然后再对一组使用COUNT函数进行计算
- 最后求出每组的学生人数
示例2:统计每个学生的选课门数,列出学号,选课门数和平均成绩
SELECT Sno 学号, COUNT(Cno) 选课门数, AVG(Grade) 平均成绩
FROM Student
GROUP BY Sno
示例3:统计每个系的男女生人数,结果按系名的升序排序
SELECT Count(*) 人数
FROM Student
GROUP BY Sdept, Sex
ORDER BY Sdept
先按照系分组,在对系中的性别分组。从而把每个系中相同性别的人聚合在一起,最后在对每个最终分组结果进行统计
当有多个分组依据时,统计是以最小单位进行的
示例4:本题也可以使用WHERE进行分组,因为性别只有两个属性
SELECT Count(*) 男生人数
FROM Student
WHERE Sex='男'
GROUP BY Sdept
执行顺序
带有 WHERE 子句的分组查询是先执行 WHERE 子句的选择,得到结果后再进行分组统计
注意
使用 HAVING 子句对分组后的统计结果再进行筛选,功能有点像 WHERE 子句,但它用于组而不是单个记录。
HAVING 子句中可以使用聚合函数,但在 WHERE 子句中则不能
示例5:查询选课门数超过3门的学生的学号和选课门数
SELECT Sno, COunt(*) 选课门数
FROM SC
GROUP BY Sno
HAVING Count(*) > 3
执行过程
- 先执行 GROUP BY 子句对 SC 表数据按 Sno 进行分组
- COUNT(*) 分别对每一组进行统计
- HAVING 筛选出统计结果大于3的组
正确的理解 WHERE, GROUP BY, HAVING 子句的作用及执行顺序
如何优化?
执行GROUP BY分组之前首先使用 WHERE 对数据进行优化减少GROUP BY的数据分组行
最后利用 HAVING 在对分组后的数据进行筛选
示例6:查询“计算机系”和“机电系”每个系的学生人数,可以有如下两种写法
第一种
SELECT Sdept, COUNT(*) FROM Student
GROUP BY Sdept
HAVING Sdept in("计算机系", "机电系")
第二种
SELECT Sdept, COUNT(*) FROM Student
WHERE Sdept in("计算机系", "机电系")
GROUP BY Sdept
第二种执行效率比第一种高:因为分组的数据少
非ANSI标准:链接操作写在WHERE子句中
-- 别名可要可不要【如果需要别名的话就按如下方式使用】
SELECT 字段 FROM 表1 别名1, 表2 别名2 WHERE 条件1 AND 条件2;
ANSI-SQL-92中:链接操作写在JOIN中
-- 别名可要可不要【如果需要别名的话就按如下方式使用】
SELECT 字段 FROM 表1 别名1 [INNER] JOIN 表2 别名2 ON 链接条件 AND 其他条件;
示例1:查询每个学生及其选课的详细信息
SELECT * FROM
Student INNER JOIN SC
ON Student.Sno=SC.Sno
解释
没有
ON Student.Sno=SC.Sno
则是获取Student
和SC
表的笛卡尔积【所谓笛卡尔积就是数学中的排列】
加上ON Student.Sno=SC.Sno
之后就相当于对这个笛卡尔积进行去重操作
即得到组合
示例2:查询每个学生及其选课的详细,要求去掉重复的列
SELECT S.Sno, Sname, Sex, Sbirthday, Sdept, Memo, Cno, Grade
FROM Student S INNER JOIN SC
ON S.Sno=SC.Sno
示例3:查询“计算机系”选修了“数据库原理”课程的学生成绩单,成绩单包含姓名,课程名称,成绩信息
SELECT Sname, Cname, Grade
FROM Student S INNER JOIN SC
ON S.Sno=SC.Sno
JOIN Course C
ON SC.Cno=C.Cno
WHERE Sdept='计算机系' AND Cname='数据库原理'
示例4:查询选修了“数据库原理”课程的学生姓名和所在系
SELECT Sname, Sdept
FROM Student INNER JOIN SC
ON S.Sno=SC.Sno
示例5:统计每个系的学生的平均成绩
SELECT Sdept, AVG(Grade) 系平均成绩
FROM Student S INNER JOIN SC
ON S.Sno=SC.Sno
JOIN Course C
ON SC.Cno=C.Cno
GROUP BY Sdept
示例6:统计“计算机系”学生中每门课程的选课人数,平均分布,最高分和最低分
SELECT Cno, COUNT(*) 选课人数 , AVG(Grade) 平均分, MAX(Grade) 最高分, MIN(Grade) 最低分
FROM Student S INNER JOIN SC ON S.Sno=SC.Sno
WHERE Sdept='计算机系'
GROUP BY Cno
有了上面内连接的基础,可以猜到自连接的本质就是自己和自己形成一个笛卡尔积
SELECT 字段 FROM 表1 AS T1 INNER JOIN 表1 AS T2 ON T1.列名=T2.列名
因此在使用自连接的时候一定要为表取别名
示例:查询课程“数据库原理”的先修课程名
SELECT C1.Cname 课程名, C2.Cname 先修课程名
FROM Course C1 JOIN Course C2
ON C1.PreCno=C2.Cno
WHERE C1.name='数据库原理'
示例2:查询与“钟文辉”在同一个系学习的学生的姓名和所在系
SELECT S2.Sname, S1.Sdept
FROM Student S1 JOIN Student S2
ON S1.Sdept=S2.Sdept
WHERE S1.Snam='钟文辉' AND S2.Sname != '钟文辉'
概念部分
- 外连接分为左外连接和右外连接
- 如果联合查询,左侧的表完全显示我们就说是左外连接;右侧的表完全显示就是右外连接
- 如果外连接也是在对表里做作笛卡尔积,则结果和内连接差不多
- 如果两个表里的数据都是一 一对应,此时内外连接是等价的,但是有时候可能存在一些数据没有对应关系,此时内外连接就有差别
-- 左外连接:表1完全显示
SELECT * FROM 表1 LEFT JOIN 表2 ON 连接条件
-- 右外连接:表2完全显示
SELECT * FROM 表1 RIGHT JOIN 表2 ON 连接条件
示例1:查询计算机系全体学生的选课情况
SELECT S.Sno, Sname, Sdept, SC.Sno
FROM Student S LEFT JOIN SC
ON S.Sno=SC.Sno
WHERE Sdept='计算机系'
ON S1.Sdept=S2.Sdept
示例2:查询没有人选的课程的课程名
SELECT Cname, Sno
FROM Course C LEFT JOIN SC
ON C.Cno=SC.Cno
WHERE SC.Cno IS NULL
这里的 NULL 需要使用 IS 来判断
示例3:统计“计算机系”每个学生的选课门数,应包含没有选课的学生
SELECT S.Sno 学号, COUNT(SC.Cno) 选课门数
FROM Student S LEFT JOIN SC
ON S.Sno=SC.Sno
WHERE Sdept='计算机系'
GROUP BY S.Sno
COUNT并不会对 NULL 进行计数
示例4:统计“机电系”选课门数少于3门的学生的学号和选课门数,包括没选课的学生,查询结果按选课门数降序排序
SELECT S.Sno 学号, COUNT(SC.Cno) 选课门数
FROM Student S LEFT JOIN SC
ON S.Sno=SC.Sno
WHERE Sdept='机电系"
GROUP BY S.Sno
HAVING COUNT(SC.Cno) < 3
ORDER BY (SC.Cno) DESC
执行顺序解析
- 执行连接操作FROM Student S LEFT JOIN SC ON S.Sno=SC.Sno
- 对连接的结果执行 WHERE进行筛选
- 执行 GROUP BY进行对步骤②的数据进行筛选
- 执行HAVING对步骤③的数据进行筛选
- 执行ORDER BY 对步骤④的数据进行筛选
相当于MySQL的LIMIT的分页查询
示例1:查询学分最多的四门课程的课程名称,学分和开课时期
SELECt TOP 4 Cname, Credit, Semester
FROM Course
ORDER BY Credit DESC
示例2:查询选课人数最多的两门课程,列出课程号和选课人数
SELECT TOP 2 WITH TIES Cno, COUNT(*) 选课人数
FROM SC GROUP BY Cno
ORDER BY COUNT (Cno) DESC
如果在 TOP 子句中使用 WHIT TIES 谓词,则要求必须使用 ORDER BY 子句对查询结果进行排序,否则会语法报错
CASE通俗易懂理解
示例1:查询全体学生信息,并对所在系用代码显示
假设:
简单CASE表达式:
SELECR Sno 学号, Sname 姓名, Ssex 性别,
CASE Sdept
WHEN '计算机系' THEN 'CS'
WHEN '机电系' THEN 'JD'
WHEN '信息管理系' THEN 'IM'
END '所在系'
FROM Student
复杂CASE表达式:
SELECR Sno 学号, Sname 姓名, Ssex 性别,
CASE
WHEN Sdept = '计算机系' THEN 'CS'
WHEN Sdept = '机电系' THEN 'JD'
WHEN Sdept = '信息管理系' THEN 'IM'
END '所在系'
FROM Student
注意
- CASE和查询的列名之间
加一个 ','
这一点容易会被忽略- 每一个
WHEN语句之间不用 ','
分割
示例1:查询“C04”号课程的考试情况,列出学号和成绩,同时对成绩进行如下处理
成绩大大于等于90,则在显示优
80~89:良
70~79:中
60~69:及格
60以下:不及格
SELECT Sno, Grade,
CASE
WHEN Grade >= 90 THEN '优'
WHEN Grade >= 80 AND Grade <= 89 THEN '良'
WHEN Grade >= 70 AND Grade <= 79 THEN '中'
WHEN Grade >= 60 AND Grade <= 69 THEN '及格'’
WHEN Grade < 60 THEN '不及格'
END 等级
FROM SC
WHERE Cno = 'C04'
示例2:统计 ‘计算机系’ 每个学生的选课门数,包括没有选课的学生。列出学号,选课门数和选课情况,其中选课处理情况为:
选课门数超过4门,则为多
选课门数2~4:一般
选课门数少于2:少
没有选课:未选
分析
- 涉及到学生的有选课和未选课,因此需要用的外链接
- 分情况处理,所以需要 CASE
SELECT S.Sno 学号, COUNT(Cno) 选课门数
CASE
WHEN COUNT(Cno) >= 4 THEN '多'
WHEN COUNT(Cno) >= 2 AND COUNT(Cno) < 4 THEN '一般'
WHEN COUNT(Cno) == 1 THEN '少'
WHEN COUNT(Cno) == 0 THEN '未选'
END 选课情况
FROM Student S LEFFT JOIN SC S.Sno=SC.Sno
WHERE Sdept = '计算机系'
GROUP BY S.Sno
为何用到外链接: 因为查询的要求是涉及到了学生表和课程表,所以需要利用到两张表的连接
为何是左外联而不是右外联: 因为查询的最终主体是学生,所以应该让学生全部显示出来而不是把课程全部显示出来。所以需要 左连学生表
为何使用GROUP BY: 因为统计的是 ‘计算机系’,所以需要对 ‘计算机系’ 进行分组统计
之前的查询都是把数据保存在内存中的,构建的是一张临时表,如果需要持久化存储。SQLServer提供了 INTO语句,讲查询结果写入一个新表,新表就可以通过数据库实现永久化存储
示例1:讲"计算机系"学生的学号,姓名,性别,年龄,保存到新表 Student_CS中
SELECT Sno, Sname, Ssex, YEAR(GETDATE())-YEAR(Sbirthday) Sage
INTO Student_CS
FROM Student WHERE Sdept = '计算机系'
查询结果保存在新表中后就可以对新表进行查询即可得到结果
示例1:查询与“钟文辉”在同一个系学习的学生学号,姓名,性别,所在系
使用子查询的方式我们可以拆开SQL语句
①查找与“钟文辉”同系的所有系
SELECT Sdept FROM Student WHERE Sname='钟文辉'
②再在这个系中查找这个集合中的所有学生信息
SELECT Sno, Sname, Ssex FROM Student
WHERE Sdept IN(xxx)
①和②合并就是:
SELECT Sno, Sname, Ssex FROM Student
WHERE Sdept IN(SELECT Sdept FROM Student WHERE Sname='钟文辉')
说明
SQL 语句是先执行子查询,然后再执行外层查询
子查询进行比较测试的语法格式为:
WHERE <列名> 比较运算符(子查询)
示例1:查询选了“C04”号课程且成绩高于此课程的平均成绩的学生的学号和该门课程成绩
分析:
①查询选了“C04”号课程的平均成绩查找出来
SELECT AVG(Grade) FROM SC WHERE Cno='C04'
②查询选了“C04”,且成绩高于步骤1结果中的平均成绩的学号和成绩
SELECT Sno, Grade
FROM SC
WHERE Cno = 'C04' AND Grade > (xxx)
①和②合并就是:
SELECT Sno, Grade
FROM SC
WHERE Cno = 'C04' AND Grade > (SELECT AVG(Grade) FROM SC WHERE Cno='C04')
运算符 | 含义 |
---|---|
> ANY | 大于查询结果中的某个值 |
< ANY | 小于查询结果中的某个值 |
>= ANY | 大于等于查询结果中的某个值 |
<= ANY | 小于等于查询结果中的某个值 |
= ANY | 等于查询结果中的某个值 |
!= ANY 或<> ANY | 不等于查询结果中的某个值 |
> ALL | 大于查询结果中的所有值 |
< ALL | 小于查询结果中的所有值 |
>= ALL | 大于等于查询结果中的所有值 |
<= ALL | 小于等于查询结果中的所有值 |
= ALL | 等于查询结果中的所有值 |
!= ALL或<> ALL | 不等于查询结果中的所有值 |
示例1:查询比“C03”课程成绩都高的选了“C04”课程的学生的学号和成绩
SELECT Sno, Grade FROM SC
WHERE Cno='C04' AND GRADE > ALL
(SELECT Grade FROM SC WHERE Cno='C03')
EXISTS:相当于true
NOT EXISTS:相当于false
一般用于存在性测试
示例1:查询选了“C04”号课程的学生姓名
SELECT Sname FROM Student
WHERE EXISTS(
SELECT * FROM SC
WHERE SC.Sno=Student.Sno AND Cno = 'C04'
)
执行过程分析:
总结:
- 带有 EXISTS的子查询是先执行外层查询然后再执行内层查询
- 由外层查询的值决定内层查询的结果,外层查询的结果决定内层查询的执行次数
UNION:并集
INTERSECT:交集
EXCEPT:差集
示例1:查询“计算机系”和“机电系”的所有学生信息
(SELECT Sno, Sname, Ssex, Sdept FROM Student WHERE Sdept='计算机系')
UNION
(SELECT Sno, Sname, Ssex, Sdept FROM Student WHERE Sdept='机电系')
示例2:查询同时选修了“C03”与“C04”课程的学生学号
(SELECT Sno FROM SC WHERE Cno='C03')
UNION
(SELECT Sno FROM SC WHERE Cno='C04')
示例3:查询选修了“C01”但没选“C02”课程的学生的学号
(SELECT Sno FROM SC WHERE Cno='C01')
EXCEPT
(SELECT Sno FROM SC WHERE Cno='C02')
优点:
示例1:创建一个包涵“计算机系”学生的成绩单视图,视图中包含学生的学号,姓名,课程号,课程名和成绩信息
CREATE VIEW V_GRADE_CS
AS
SELECT S.Sno, Sname, C.Cno, Cname, Grade
FROM Student S, SC, Course C
WHERE S.Sno=SC.Sno AND SC.Cno=C.Cno
AND Sdept = '计算机系'
-- 查询视图
SELECT * FROM V_GRADE_CS
一般不建议通过视图来修改表数据
示例1:将上个所得到的V_GRADE_CS视图加一个学生的年龄信息
ALTER VIEW V_GRADE_CS
AS
SELECT S.Sno, Sname, YEAR(GETDATE())-YEAR(Sbirthday) Sage, C.Cno, Cname, Grade
FROM Student S, SC, Course C
WHERE S.Sno=SC.Sno AND SC.Sno=C.Cno
AND Sdept ='计算机系'
示例2:删除上述中的视图
DROP VIEW V_GRADE_CS
注意
如果被删除的视图A是其他视图B的数据,那么删除视图A将会导致视图B无法使用;同样的基本表被删除后也会导致视图无法使用。
单行插入示例1:向Student表中插入(050101, 赵林, 男, 1999-09-08, 计算机系)的纪录
INSERT INTO Student VALUES('050101', '赵林', '男', '1999-09-08', '计算机系', NULL)
多行插入示例2:用CREATE语句建立表StudentBAK,包含(与Student的Sno,Sname,Sdept相同)3个字段,然后向StudentBAK添加Studen表中的计算机系学生的学号,姓名,所在系的信息
1⃣️先创建StudentBAK表
CREATE TABLE StudentBAK(
Sno CHAR(6) PRIMARY KEY,
Sname NVARCHAR(20),
Sdept NVARCHAR(20),
)
2⃣️向StudentBAK表批量插入“计算机系”学生信息
INSERT INTO StudentBAK
SELECT Sno, Sname, Sdept FROM Student WHERE Sdept='计算机系'
示例1:创建的Student_CS表中学生年龄加1
UPDATE Student_CS SET Sage=Sage+1
示例2:把“C04”课程学分加1
UPDATE Course SET Gredit = Gredit+1
WHERE Cno='C04'
基于其它表的更新。实例3:将数据库原理课程的成绩都减5分
1⃣️子查询实现
UPDATE SC SET Grade=Grade-5
WHERE Cno IN(SELECT Cno FROM Course WHERE Cname='数据库原理')
2⃣️多表链接实现
UPDATE SC SET Grade=Grade-5
FROM SC JOIN Course ON SC.Cno=Course.Cno
示例1:删除Studetn_CS表中的数据
DELETE FROM Student_CS
实例2:删除数据库原理的选课记录
1⃣️子查询实现
DELETE FROM SC
WHERE Cno IN(SELECT Cno FROM COurse WHERE Cname='数据库原理')
2⃣️多表链接实现
DELETE FROM SC JOIN Course ON SC.Cno=Course.Cno
WHERE Cname='数据库原理'
GRANT:授权
REVOKE:回收
DENY:拒权
给用户May和John授权创建数据库和创建表的权限
GRANT CREATE DATABASE, CREATE TABLE
TO Mary, John
将用户Mary和John创建数据库和创建表的权限回收
REVOKE CREATE DATABASE, CREATE TABLE
TO Mary, John
拒绝让Mary,John拥有CREATE DATABASE, CREATE TABLE全县,除非给他们显示授予权限
DENY CREATE DATABASE, CREATE TABLE
TO Mary, John
大家都知道索引可以提升数据库查询速度。
举例
假设我们在Student表的Sno列上建立了一个索引(Sno为索引项或索引关键字),则在索引部分就有指向每个学号所对应的学生的存储位置信息。【有点像数据结构中的“哈希表”:索引是按照数组顺序排列的,每个下标对应一个元素的信息】
当根据Sno查询Student上制定的学生信息的时候,它能够识别上该表上的索引列(Sno),并首先在索引部分(按学号有序存储)查找该学号,然后根据该学号指定的位置直接检索出要查询的信息。
如果没有索引,则需要从数据库第一行中一行一行比对的查找,我们都知道有列表的查找远高于无需表的查找
但索引带来高效率的同时也有着代价
索引分为两大类:一类是聚集索引;另一类是非聚集索引。它们都是由B树组结构实现
不熟悉树结构的朋友可以点击查看树的知识点
此B树是自下而上的建立。依靠索引值构造根节点,其索引关键字选取子节点最大或最小【构成大/小根堆】
对于频繁的修改茶如何删除,了解B树后肯定知道会破坏掉平衡性,但这些平衡性不需要用户或者程序员去关心,交给数据库管理系统自动调整,但这些调整过于频繁也会降低操作的性能
因此对于读取需求高的场景适合使用聚集索引,但对于频繁更改的列则不适合建立聚集索引
最后一点
在创建聚集索引之前先了解数据是如何被访问的,因为数据的访问方式直接影响到索引的使用。不恰当的索引可能会事半功倍,因此索引有时候也并非是越多越好
非聚集索引与聚集索引一样用B树结构,但两者有两个重要差别:
一句话概括非聚集索引
因此聚集索引的最后的数据页都是按照索引排好序的;非聚集索引则是无序的。非聚集索引的每个内容都可以找到单内容并不按照索引的顺序排列下来。
示例1:为Student表的Sname列创建非聚集解索引
CREATE INDEX Sname_ind
ON Student(Sname)
实例2:为Student表的Sid列创建唯一聚集索引
CREATE UNIQUE CLUSTERED INDEX Sid_ind
ON Student(Sid)
示例3:为Employee表的FirstName和LastName列创建一个聚集索引
CREATE CLUSTERED INDEX EName_ind
ON Employee(FIrstName, LastName)
示例4:删除Student表中的Sname_ind索引
DROP INDEX Student.Sname_ind