1 数据库和SQL
1.1 数据库
DB, DBMS
DBMS的种类: 层次性数据库, 关系型数据库, 非关系型数据库
RDBMS, 关系数据库管理系统
1.2 数据库的结构
RDBMS常见的系统结构是 客户端服务器结构
表: 在关系数据库中管理数据的二维表
关系数据库必须以航为单位进行数据的读写
1.3 SQL
标准SQL和特定的SQL
SQL语句的分类
1) DDL 数据定义语言: create, drop, alter
2) DML 数据操作语言(90%的使用率): select, instert, update, delete
3) DCL 数据控制语言: commit, rollback, grant, revoke
SQL的基本原则
1) 以分号结尾
2) 不区分大小写, 一般关键字大写, 表首字母大写, 列小写
3) 常数的书写方式是固定的, 字符串日期常数用单引号, 数字直接写就行
4) 空格要是半角的
5) 注释有两种写法 单行用-- 多行用/**/
1.4 表的创建
数据库的创建
create database 数据库名称;
表的创建
create table 表名(
列名 数据类型 该列的约束
...
表的约束
);
命名规则
名字可以是: 半角英文字母, 数字, 下划线,
必须是以英文开头
名称不能重复
数据类型
integer, char(定长的字符串), varchar(不定长的字符串), date
主键约束
primary key
1.5 表的删除和更新
删除表
drop table 表名;
添加行
alter table 表名 add column 列的定义;
插入数据
insert into 表 values (值, 值...);
2 基础查询
2.1 select语句基础
1) 基本格式
select 列名, 列名...
from 表名,.. ;
2) 其中在列名去中输入* 表示显示所有列, 而且是按照表定义的列的顺序进行显示
3) 为列设置别名
select 列名 as 别名,
列名 as 别名,
...
from 表名,.. ;
设置别名之后, 查询得到的结果的行首就显示的是别名
如果设置成中文的话需要使用双引号来定义
4) 设置常数列
select 列名 as 别名,
列名 as 别名,
常数 as 别名
...
from 表名,.. ;
这个常数可以是数字也可是字符串
这么设置之后这个列的值就是这个常数值, 且表头显示的是别名, 如果不设置别名, 那也显示成该常数
5) 去除重复行
在select后面加上关键字distinct
注意distinct的位置, 只能在第一个列名之前
去除是根据后面选取行的组合来判断的重复
6) where限定行
在from之后加上where语句限定查询出来的结果
2.2 算数运算符和比较运算符
算数运算符: + - * /
关于NULL的运算结果都是NULL
比较运算符: = <> >= <= > <
数字的比较是按照值比较的
字符串的比较是按照字典顺序进行比较的
NULL不会和别的结果进行比较
判断是NULL用IS NULL
判断不是NULL用 IS NOT NULL
2.3 逻辑运算符
NOT 表示否定
AND OR 与或者非
AND的优先级是高于OR的
要想按照指定的顺序执行需要使用括号
逻辑运算中NULL需要单独讨论
对NULL使用比较运算符得到的结果是unkonwn(不确定)
除了true, false还有unknown
在SQL中的逻辑运算有三值逻辑
判断值的原则是 真>unknown>假, AND取等级低的, OR取等级高的
因此在AND中, 真 AND unknown = unknown, unknown AND false = false
在OR中, 真 OR unknown = 真, unknown OR false = unknown
3 聚合和排序
3.1 对表进行聚合查询
常用的聚合函数
count: 计算个数(行数), 可以指定参数, 当指定列值为NULL时, 不做统计
sum: 求和
avg: 求平均值
max: 求最大值
min: 求最小值
关于NULL
指定列时, 如果值为NULL, 会忽略
对于AVG, 如果有NULL值, 同样也是忽略, 也就是分子没有那个值, 分母也不算拿一行
在聚合函数中, 可以传入参数, 可以传入distinct来删除重复的值
3.2 对表进行分组
使用group by来分组
group by后面指定的列为聚合键或者分组列
分组NULL同样会单独形成一个分组
有where会先执行where, 得到筛选的结果之后再进行分组
注意事项:
1) select 不能选取除group by选定的列的其他列
原因是: 既然已经分组了, 那么一个分组对应的记录就有可能是多条, 在当前分组下, group by后面的列的值是一致的并没有什么关系, 但是如果选取别的列, 那么就有可能是不同的值, 那么此时就不能够确定显示哪一个值了, 这样就造成了歧义, 在mysql中会排序然后得到第一个值
2) group by后面不要写别名
原因是: select语句是在group by之后执行的, 因此使用了别名那个时候是找不到的
3) 不要在where中使用聚集函数
实际上能够使用聚集函数的就有: select子句, having, order by, group by
3.3 为聚合结果指定条件
使用having在分组的基础上进行过滤筛选
having的组成部分
常数
聚合函数(聚合函数类的参数可以是非聚合键)
聚合键
因此在having中使用别的键是错误的
having和where的区别
要将having和where区分开来
having: 指定组的条件
where: 指定行的条件
能使用where就使用where, 因为where的执行效率更高
3.4 对查询结果进行排序
排序使用order by
一般地, 查询的记录的顺序是随机的(或者有的数据库会有默认的排序)
由于进行数据的排序是在返回结果的时候执行, 因此一般地, order by语句放在查询语句的末尾
ASC表示升序, DESC表示降序
指定多个排序键
可以指定多个排序键, 这样在第一个键相同的时候判断第二个键, 进一步确定排序的顺序
NULL值会固定放在最后, 不管是升序还是降序
查询语句的执行顺序:
from -> where -> group by -> having -> select -> order by
order by可以使用的列
表中的列
select中选取的列的序号, 从1开始
聚合函数
4 数据更新
4.1 数据的插入
insert的基本语法
insert into 表名(列1, 列2, ...) values (值1, 值2,...)... ;
原则上, 一次insert语句只会插入一行数据
但是有的数据库是支持多行插入的, 用括号括起来表示一行, 用逗号间隔多行的数据
列清单可以省略, 省略之后按照顺序排序数据
值可以是NULL
插入默认值
1) 通过显式的方式
在设置了默认值的位置用default代替
2) 通过隐式的方式
列清单不写该列
从其他表中赋值数据
insert into 表名(列1, 列2, ...) select... ;
4.2 数据的删除
删除表
drop table 表名;
全部删除数据
delete from 表名;
删除指定记录
delete from 表名 where 条件;
清空表, 还原表为初始状态
truncate 表名;
不能加入where语句指定某记录
处理速度比delete快很多
4.3 数据的更新
update的基本语法
update 表名
set 列名 = 表达式
... where 条件;
表达式可以是一个固定的值, 也可以是一个运算
NULL清空
将值设置为NULL, 但是该列必须是允许空的
其中列名=表达式可以有多个, 这样可以方便进行多行更新
4.4 事务
事务: 需要在同一个处理单元中执行的一系列更新处理的集合
创建事务
事务开始语句;
DML语句;
...
...
事务结束语句;
一般地, 事务开始语句分数据库, SQL server的是begin transaction, MySQL是start transaction, Oracle没有开始语句
因此一定要注意事务的开始起点, 在标准SQL中已经规定了一种悄悄处理事务的方法
结束语句一般是commit表示提交
一定要记得在事务结束之后仔细确认
结束语句还可以是rollback, 表示取消事务包含的全部更新处理
一般的数据库产品已经不需要事务的开始语句了, 很多数据库在执行第一个SQL语句的时候就已经悄悄开始了事务
一般有两种情况
1) 每一条SQL语句都是一个事务(自动提交模式): SQL Server, PostgreSQL, MySQL
2) 知道用户执行了commit或者rollback为止算作一个事务: Oracle
因此在大多数情况下, 采用自动提交模式的数据库, 你一旦误删了某个表, 就再也找不回来了
酸性性质
ACID性质, 这是所有DBMS都必须遵守的规则
A: 原子性, 事务在结束时, 其中包含的更新处理要么全部执行, 要么完全不执行, 这样就防止了中途停止的情况, 不会对业务造成不好的影响
C: 一致性(完整性), 事务中执行的处理, 必须要满足数据库数据表设置好的约束, 如果不遵守约束, 就会报错, 然后事务回滚
I: 隔离性, 保证事务时间互不干扰的特性, 该事务之间不会相互嵌套, 当前事务如果没有提交, 所做的修改是对别的事务是不可见的, 直到事务完成
D: 持久性(耐久性), 事务一旦结束, 数据库会保证数据的状态得以保存, 常见的保证持久性的方法是将执行记录保存到存储介质上, 也就是一般意义的保存日志
5 复杂查询
5.1 视图
视图基本上与表差不多, 只是说表实际存储数据, 而视图是虚表, 不会存储实际的数据
一般的, 对表的修改更新等操作会实际的写到存储介质上进行保存, 但是对视图的处理, 不会将数据保存到任何地方, 实际上视图保存的就是select语句, 当需要从视图中读取数据的时候, 就是根据这个select语句来创建一个临时表
视图的优点
1) 无需保存数据, 节省存储空间
2) 可以将常用的select语句保存成视图, 这样就不会每次重写查询语句, 且会得到数据的 最新状态
创建视图
create view 视图名称 (视图列名, 视图列名, ...)
as
select语句 ;
创建视图的时候, 视图名称后面的名称是可以省略的, 省略的时候默认就是查询出来的列名; 如果指定了列名, 就相当于是重新命名的列名, 但是要注意此时的列名要和查询得到的列相同数量
视图的注意事项:
1) 可以在视图的基础上再创建视图, 也就是形成了多重视图, 但是这样会降低SQL的性能, 一般杜绝这样的操作
2) 一般定义视图的时候不使用order by, 因为数据行是没有顺序的, 尽管很多数据库支持这样的操作
3) 可以对视图进行更新, 但是有条件限制, 一般视图是无法进行更新的, 因为诸如你创建的视图是基于聚合函数的, 聚合函数是针对一个分组的, 如果对视图进行添加数据了, 那么对应这个数据反映到原表上是一组数据, 数据库是没有办法处理这一组数据仅仅只有一个聚合的结果
可以进行更新的条件
1) select子句中没有使用distinct
2) from后面就只有一张表
3) 没有使用group by, 也没有使用having
视图的使用
一般对视图的操作就是查询操作, 可以将视图名称当成一般的表使用就好了
视图的删除
drop view 视图名称;
在删除的时候, 如果有其他视图是在当前视图的基础上创建的, 那么这个视图是无法被删除的, 此时还要删除需要加上级联删除
drop view 视图名称 cascade;
5.2 子查询
子查询就是在select ..from的后面再加上select语句查询结果, 并用as关键字命名, 但不是所有的数据库都支持as, 直接写名字就行
select ..
from (select语句) as 名字
.. ;
可以在子查询中嵌套子查询
标量子查询: 只返回一行一列数据的子查询
标量子查询相当于就是一个标量值, 因此可以应用到标量值应用的所有位置, selet后面, where后面, having后面等
使用标量子查询需要注意的是, 一定要注意查询的结果是一行一列的
5.3 关联子查询
现在有个商品表
现在需要根据筛选出单价大于该分类的平均值的商品
如果仅仅使用子查询, 得到的SQL是错误的
SELECT *
FROM sp
WHERE sp_prise > (
SELECT AVG( sp_prise)
FROM sp
GROUP BY sp_class
);
但是这样会报错, 因为子查询的值不唯一, 那如何将当前商品和当前商品类别的平均价格对应上呢, 就是关联子查询
关联子查询就是在子查询中, 关联表的操作, 应用关联子查询得到的SQL是这样的
SELECT *
FROM sp as s1
WHERE s1.sp_prise > (
SELECT AVG( s2.sp_prise)
FROM sp as s2
WHERE s1.sp_class = s2.sp_class
GROUP BY s2.sp_class
);
6 函数, 谓词和case表达式
6.1 函数
函数: 给定一些输入, 输出特定的输出
输入值就是参数
输出值就是返回值
函数分为
1) 算术函数: abs(绝对值), mod(求余, 传入两个参数), round(四舍五入),
2) 字符串函数
拼接:
-- 一般数据库使用||拼接字符串
SELECT sp_name, sp_class, sp_name || sp_class as my_str
FROM sp;
-- SQL Server使用+拼接字符串
SELECT sp_name, sp_class, sp_name + sp_class as my_str
FROM sp;
-- MySQL使用concat()函数拼接字符串
SELECT sp_name, sp_class, concat(sp_name,sp_class) as my_str
FROM sp;
字符串长度: length(字符串) SQL Server使用的是len()
转成小写: lower()
转成大写: upper()
字符串替换: replace(字符串对象, 要替换的字符串, 替换成的字符串)
字符串截取: substring(字符串对象 from 起点 for 截取的字符数) 例如: substring("ABCDE" from 2 for 3) 得到的字符串是"BCD"
3) 日期函数
当前日期: current_date
当前时间: current_time
当前日期和时间点: current_timestamp
截取日期元素: extract(日期元素 from 日期)
SELECT CURRENT_TIMESTAMP,
EXTRACT(YEAR FROM CURRENT_TIMESTAMP),
EXTRACT(MONTH FROM CURRENT_TIMESTAMP),
EXTRACT(DAY FROM CURRENT_TIMESTAMP),
EXTRACT(HOUR FROM CURRENT_TIMESTAMP),
EXTRACT(MINUTE FROM CURRENT_TIMESTAMP),
EXTRACT(SECOND FROM CURRENT_TIMESTAMP);
4) 聚合函数: sum, avg, max, min, count
5) 转换函数: 转换函数是进行类型的转换或者值的转换
类型转换
cast(转换前的值 as 想转换的数据类型)
值转换, 将NULL转化为其他值
coalesce(数据1, 数据2, ...)
6.2 谓词
常见的谓词就是一些比较运算符(= < >等)
具体来说就是, 返回值是真值(true/false/unknown)
常见的谓词有like betwween, is null, is not null, in, exists
(1) like
涉及到前方一致, 中间一致, 后方一致, 分别是: 字符串%, %字符串%, %字符串
%表示任意个字符, _表示任意一个字符
(2) between A and B 范围查找
取值范围为 [A, B]
(3) is null, is not null
判断是不是null不能用 值 = null 来判定, 这样始终返回为false
判断是否为null
(4) in
in/not in 可以用or来实现, 但是使用in更加简洁
(5) exist
判断记录是否存在某种条件, 返回值是false或者true
6.3 case表达式
case表达式主要完成条件分歧
具体格式为
case when 判断表达式 then 表达式
when 判断表达式 then 表达式
when 判断表达式 then 表达式
...
else 表达式
end
课件不管有多么复杂的case语句, 最终的结果始终是一个简单的值
基于这样的特点, case语句也可以放在任何单值的位置
7 集合运算
7.1 表的加减法
表的加减法是处理增减或者减少数据行
(1) 表的加法 union
union是对表进行并集运算, 条件是列必须相同(一般地, 列对应是一样的union才有意义), 得到的结果会去掉重复
注意:
列要相同
列的类型必须一致
可以使用任何select大但是order by只能在最后使用且使用一次, 也就是说只能对union之后的表进行排序
如果要包含重复的行要使用union all
(2) 表的公共部分 intersect
intersect取得的是表的交集
(3) 表的减法 except
求的在A表中不在B表中的记录
7.2 联结
联结的作用是增加列, 是在列的方向进行一定的操作
(1) 內联结 inner join
格式为
select 列 from 表A
inner join 表B on 联结条件
其他操作;
得到的结果是同时满足联结条件的记录, 不存在补记录的现象
(2) 外联结 outer join
与內联结相比, 外联结存在补齐的情况
(3) 交叉联结 cross join
得到的就是笛卡尔积