概述:sql
1.基础命令
(1)数据库模式定义语言DDL(Data Definition Language)
1 | 查看服务器版本 |
(2)数据操纵语言DML(Data Manipulation Language)
1 | -- 插入学院数据 |
※ DML运算符:
(1).算术运算符(+、-、*、/、%)
(2).比较运算符(=、<>、<=>、<、<=、>、>=、BETWEEN…AND…、IN、IS (3).NULL、IS NOT NULL、LIKE、RLIKE、REGEXP)
(4).逻辑运算符(NOT、AND、OR、XOR)
(5).位运算符(&、|、^、~、>>、<<)
※ DML常用函数:
(1)字符串函数:
函数 | 功能 |
---|---|
CONCAT | 将多个字符串连接成一个字符串 |
FORMAT | 将数值格式化成字符串并指定保留几位小数 |
FROM_BASE64 / TO_BASE64 | BASE64解码/编码 |
BIN / OCT / HEX | 将数值转换成二进制/八进制/十六进制字符串 |
LOCATE | 在字符串中查找一个子串的位置 |
LEFT / RIGHT | 返回一个字符串左边/右边指定长度的字符 |
LENGTH / CHAR_LENGTH | 返回字符串的长度以字节/字符为单位 |
LOWER / UPPER | 返回字符串的小写/大写形式 |
LPAD / RPAD | 如果字符串的长度不足,在字符串左边/右边填充指定的字符 |
LTRIM / RTRIM | 去掉字符串前面/后面的空格 |
ORD / CHAR | 返回字符对应的编码/返回编码对应的字符 |
STRCMP | 比较字符串,返回-1、0、1分别表示小于、等于、大于 |
SUBSTRING | 返回字符串指定范围的子串 |
(2).数值函数
函数 | 功能 |
---|---|
ABS | 返回一个数的绝度值 |
CEILING / FLOOR | 返回一个数上取整/下取整的结果 |
CONV | 将一个数从一种进制转换成另一种进制 |
CRC32 | 计算循环冗余校验码 |
EXP / LOG / LOG2 / LOG10 | 计算指数/对数 |
POW | 求幂 |
RAND | 返回[0,1)范围的随机数 |
ROUND | 返回一个数四舍五入后的结果 |
SQRT | 返回一个数的平方根 |
TRUNCATE | 截断一个数到指定的精度 |
SIN / COS / TAN / COT / ASIN / ACOS / ATAN | 三角函数 |
(3).时间日期函数
函数 | 功能 |
---|---|
CURDATE / CURTIME / NOW | 获取当前日期/时间/日期和时间 |
ADDDATE / SUBDATE | 将两个日期表达式相加/相减并返回结果 |
DATE / TIME | 从字符串中获取日期/时间 |
YEAR / MONTH / DAY | 从日期中获取年/月/日 |
HOUR / MINUTE / SECOND | 从时间中获取时/分/秒 |
DATEDIFF / TIMEDIFF | 返回两个时间日期表达式相差多少天/小时 |
MAKEDATE / MAKETIME | 制造一个日期/时间 |
(4).流程函数
函数 | 功能 |
---|---|
IF | 根据条件是否成立返回不同的值 |
IFNULL | 如果为NULL则返回指定的值否则就返回本身 |
NULLIF | 两个表达式相等就返回NULL否则返回第一个表达式的值 |
(5).其他函数
函数 | 功能 |
---|---|
MD5 / SHA1 / SHA2 | 返回字符串对应的哈希摘要 |
CHARSET / COLLATION | 返回字符集/校对规则 |
USER / CURRENT_USER | 返回当前用户 |
DATABASE | 返回当前数据库名 |
VERSION | 返回当前数据库版本 |
FOUND_ROWS / ROW_COUNT | 返回查询到的行数/受影响的行数 |
LAST_INSERT_ID | 返回最后一个自增主键的值 |
UUID / UUID_SHORT | 返回全局唯一标识符 |
(3).数据控制语言 (Data Control Language)
1 | -- 创建可以远程登录的root账号并为其指定口令 |
2.索引
MySQL中,所有数据类型的列都可以被索引,常用的存储引擎InnoDB和MyISAM能支持每个表创建16个索引。InnoDB和MyISAM使用的索引其底层算法是B-tree(B树),B-tree是一种自平衡的树,类似于平衡二叉排序树,能够保持数据有序。这种数据结构能够让查找数据、顺序访问、插入数据及删除的操作都在对数时间内完成。
例1.使用explain关键字查看SQL执行查询过程
1 | explain select * from tb_student where stuname='林震南'\G |
为上述查询过程添加索引实现加速查询速度:
1 | create index idx_student_name on tb_student(stuname); |
再次查看SQL查询过程:
1 | *************************** 1. row *************************** |
MySQL还支持前缀索引,即对索引字段的前N个字符创建索引,这样的话可以减少索引占用的空间(但节省了空间很有可能会浪费时间,时间和空间是不可调和的矛盾),以下例子以第一个字符作为前缀索引:
1 | create index idx_student_name_1 on tb_student(stuname(1)); |
同样实现优化过程,减少索引的存储空间,但是可能查询速度会有所下降。
删除索引
1 | alter table tb_student drop index idx_student_name; |
索引的设计原则:
- 最适合索引的列是出现在WHERE子句和连接子句中的列。
- 索引列的基数越大(取值多重复值少),索引的效果就越好。
- 使用前缀索引可以减少索引占用的空间,内存中可以缓存更多的索引。
- 索引不是越多越好,虽然索引加速了读操作(查询),但是写操作(增、删、改)都会变得更慢,因为数据的变化会导致索引的更新,就如同书籍章节的增删需要更新目录一样。
- 使用InnoDB存储引擎时,表的普通索引都会保存主键的值,所以主键要尽可能选择较短的数据类型,这样可以有效的减少索引占用的空间,利用提升索引的缓存效果。
3.视图
视图就是虚拟的表,但与数据表不同的是,数据表是一种实体结构,而视图是一种虚拟结构,你也可以将视图理解为保存在数据库中被赋予名字的SQL语句。
使用视图可以获得以下好处:
- 可以将实体数据表隐藏起来,让外部程序无法得知实际的数据结构,让访问者可以使用表的组成部分而不是整个表,降低数据库被攻击的风险。
- 在大多数的情况下视图是只读的(更新视图的操作通常都有诸多的限制),外部程序无法直接透过视图修改数据。
- 重用SQL语句,将高度复杂的查询包装在视图表中,直接访问该视图即可取出需要的数据;也可以将视图视为数据表进行连接查询。
- 视图可以返回与实体数据表不同格式的数据。
(1)创建视图
1 | create view vw_avg_score |
(2)使用视图
1 | select stuname, avgscore from vw_student_score order by avgscore desc; |
视图的可更新性要视具体情况而定,以下类型的视图是不能更新的:
- 使用了聚合函数(SUM、MIN、MAX、AVG、COUNT等)、DISTINCT、GROUP BY、HAVING、UNION或者UNION ALL的视图。
- SELECT中包含了子查询的视图。
- FROM子句中包含了一个不能更新的视图的视图。
- WHERE子句的子查询引用了FROM子句中的表的视图。
(3)删除视图
1 | drop view vw_student_score; |
视图的规则和限制
- 视图可以嵌套,可以利用从其他视图中检索的数据来构造一个新的视图。视图也可以和表一起使用。
- 创建视图时可以使用
order by
子句,但如果从视图中检索数据时也使用了order by
,那么该视图中原先的order by
会被覆盖。 - 视图无法使用索引,也不会激发触发器(实际开发中因为性能等各方面的考虑,通常不建议使用触发器,所以我们也不对这个概念进行介绍)的执行。
4.存储过程
存储过程是事先编译好存储在数据库中的一组SQL的集合,调用存储过程可以简化应用程序开发人员的工作,减少与数据库服务器之间的通信,对于提升数据操作的性能也是有帮助的。其实迄今为止,我们使用的SQL语句都是针对一个或多个表的单条语句,但在实际开发中经常会遇到某个操作需要多条SQL语句才能完成的情况。例如,电商网站在受理用户订单时,需要做以下一系列的处理。
- 通过查询来核对库存中是否有对应的物品以及库存是否充足。
- 如果库存有物品,需要锁定库存以确保这些物品不再卖给别人, 并且要减少可用的物品数量以反映正确的库存量。
- 如果库存不足,可能需要进一步与供应商进行交互或者至少产生一条系统提示消息。
- 不管受理订单是否成功,都需要产生流水记录,而且需要给对应的用户产生一条通知信息。
我们可以通过存储过程将复杂的操作封装起来,这样不仅有助于保证数据的一致性,而且将来如果业务发生了变动,只需要调整和修改存储过程即可。对于调用存储过程的用户来说,存储过程并没有暴露数据表的细节,而且执行存储过程比一条条的执行一组SQL要快得多。
下面的存储过程实现了查询某门课程的最高分、最低分和平均分。
1 | drop procedure if exists sp_score_by_cid; |
说明:在定义存储过程时,因为可能需要书写多条SQL,而分隔这些SQL需要使用分号作为分隔符,如果这个时候,仍然用分号表示整段代码结束,那么定义存储过程的SQL就会出现错误,所以上面我们用delimiter $$
将整段代码结束的标记定义为$$
,那么代码中的分号将不再表示整段代码的结束,需要马上执行,整段代码在遇到end $$
时才输入完成并执行。在定义完存储过程后,通过delimiter ;
将结束符重新改回成分号。
上面定义的存储过程有四个参数,其中第一个参数是输入参数,代表课程的编号,后面的参数都是输出参数,因为存储过程不能定义返回值,只能通过输出参数将执行结果带出,定义输出参数的关键字是out
,默认情况下参数都是输入参数。
(1)调用存储过程。
1 | call sp_score_by_cid(1111, @a, @b, @c); |
(2)获取输出参数的值。
1 | select @a as 最高分, @b as 最低分, @c as 平均分; |
(3)删除存储过程。
1 | drop procedure sp_score_by_cid; |
在存储过程中,我们可以定义变量、条件,可以使用分支和循环语句,可以通过游标操作查询结果,还可以使用事件调度器,这些内容我们暂时不在此处进行介绍。虽然我们说了很多存储过程的好处,但是在实际开发中,如果过度的使用存储过程,将大量复杂的运算放到存储过程中,也会导致占用数据库服务器的CPU资源,造成数据库服务器承受巨大的压力。为此,我们一般会将复杂的运算和处理交给应用服务器,因为很容易部署多台应用服务器来分摊这些压力。
5.范式
- 第一范式:数据表的每个列的值域都是由原子值组成的,不能够再分割。
- 第二范式:数据表里的所有数据都要和该数据表的键(主键与候选键)有完全依赖关系。
- 第三范式:所有非键属性都只和候选键有相关性,也就是说非键属性之间应该是独立无关的。
6.数据完整性
(1)实体完整性
每个实体都是独一无二的
- 主键(primary key) / 唯一约束 / 唯一索引(unique)
(2)引用完整性(参照完整性)
关系中不允许引用不存在的实体
- 外键(foreign key)
(3)域完整性 - 数据是有效的
数据类型及长度
非空约束(not null)
默认值约束(default)
检查约束(check)
说明:在MySQL数据库中,检查约束并不起作用。
7.数据一致性
事务:一系列对数据库进行读/写的操作,这些操作要么全都成功,要么全都失败。
事务的ACID特性
- 原子性:事务作为一个整体被执行,包含在其中的对数据库的操作要么全部被执行,要么都不执行
- 一致性:事务应确保数据库的状态从一个一致状态转变为另一个一致状态
- 隔离性:多个事务并发执行时,一个事务的执行不应影响其他事务的执行
- 持久性:已被提交的事务对数据库的修改应该永久保存在数据库中
MySQL中的事务操作
(1)开启事务环境
1
start transaction
或
1
begin
(2)提交事务
1
commit
(3)回滚事务
1
rollback
8.Python数据库编程
(1)安装PyMySQL
1 | pip install pymysql |
(2)添加一个部门
1 | import pymysql |
(3)删除一个部门
1 | import pymysql |
说明:如果不希望每次SQL操作之后手动提交或回滚事务,可以像上面的代码那样,在创建连接的时候多加一个名为autocommit
的参数并将它的值设置为True
,表示每次执行SQL之后自动提交。如果程序中不需要使用事务环境也不希望手动的提交或回滚就可以这么做。
(4)更新一个部门
1 | import pymysql |
(5)查询所有部门
1 | import pymysql |
(6)分页查询员工信息
1 | import pymysql |
9.存储过程(Stored Procedure)
存储过程是一种在数据库中存储复杂程序,以便外部程序调用的一种数据库对象。
存储过程是为了完成特定功能的SQL语句集,经编译创建并保存在数据库中,用户可通过指定存储过程的名字并给定参数(需要时)来调用执行。
(1)存储与函数的区别
1.一般来说,存储过程实现的功能要复杂一点,而函数的实现的功能针对性比较强。
2.对于存储过程来说可以返回参数(output),而函数只能返回值或者表对象。
3.存储过程一般是作为一个独立的部分来执行,而函数可以作为查询语句的一个部分来调用,由于函数可以返回一个表对象,因此它可以在查询语句中位于FROM关键字的后面。
(2)存储过程的优缺点
优点:
(1)重复使用。存储过程可以重复使用,从而可以减少数据库开发人员的工作量。
(2)提高性能。存储过程在创建的时候就进行了编译,将来使用的时候不用再重新编译。一般的SQL语句每执行一次就需要编译一次,所以使用存储过程提高了效率。
(3)减少网络流量。存储过程位于服务器上,调用的时候只需要传递存储过程的名称以及参数就可以了,因此降低了网络传输的数据量。
(4)安全性。参数化的存储过程可以防止SQL注入式的攻击,而且可以将Grant、Deny以及Revoke权限应用于存储过程。
缺点:
存储过程的性能调校与撰写,受限于各种数据库系统。
(3)常用系统存储过程
1 | exec sp_databases; --查看数据库 |
(4)创建&调用存储过程
4.1显示平均价格的存储过程
过程名为productpricing
1 | CREATE PROCEDURE productpricing() |
1 | CALL productpricing(); |
4.2返回平均值、最小/大值结果
1 | CREATE PROCEDURE productpricing( |
1 | CALL productpricing(@pricelow, |
显示上述平均价格:
1 | SELECT @priceaverage; |
4.3带有形参的返回
1 | CREATE PROCEDURE ordertotal( |
1 | -- 调用 |
1 | -- 显示 |
(6)删除存储过程
1 | DROP PROCEDURE productpricing |
(7)复杂存储过程
- 获得合计;
- 把营业税有条件地添加到合计;
- 返回合计(带或不带税)。
1 | CREATE PROCEDURE ordertotal( |
分析:
1.增加注释(前面放置–)。
2.添加另外一个参数taxable,它是一个布尔值(如果要增加税则为真,否则为假)。
3.用DECLARE要求指定变量名和数据类型,它也支持可选的默认值(这个例子中的taxrate的默认被设置成6%)。
4.SELECT语句已经改变,因此其结果存储到total(局部变量)而不是ototal。
5.IF语句检查taxable是否为真,如果为真,则用另一SELECT语句增加营业税到局部变量total。最后,用另一个SELECT语句将total(它增加或许不增加营业税)保存到ototal。
6.COMMENT关键字 本例子中的存储过程在CREATE PROCEDURE语句中包含了一个COMMENT值。它不是必须的,但如果给出,将在SHWO PROCEDURE STATUS的结果中显示。
调用和显示:
1 | CALL ordertotal(20005,0,@total); |
(8)检查存储过程
1 | SHOW CREATE PROCEDURE ordertotal; |
优化
索引的优势:
a. 可以提高数据检索的效率,降低数据库的IO成本,类似于书的目录。
b. 通过索引列对数据进行排序,降低数据排序的成本,降低了CPU的消耗。
索引的劣势:
a. 索引会占据磁盘空间。
b. 索引虽然会提高查询效率,但是会降低更新表的效率。比如每次对表进行增删改操作,MySQL不仅要保存数据,还有保存或者更新对应的索引文件。
索引的数据结构:
mysql在数据库设计时有,Hash、B+树 两种类型可以选择。选择使用依据及常用场景:
Hash,由于hash表特点使用Hash索引,可以快速的精确查询,但不支持范围查询。常用于等值查询场景(只有key-value的情况),如redis,memcached等这些Nosql的中间键。
创建索引应遵循的原则
索引是提高效率的一个因素,因此在建立索引的时候应该遵循以下原则:
- 在经常需要搜索的列上建立索引,可以在经常使用 WHERE 子句的列上创建索引可以加快搜索的速度。
- 在作为主键的列上创建索引,强制该列的唯一性,并组织表中数据的排列结构。
- 在经常使用表连接的列上创建索引,这些列主要是一些外键,可以加快表连接的速度。
- 在经常需要根据范围进行搜索的列上创建索引,因为索引已经排序,所以其指定的范围是连续的。
- 在经常需要排序的列上创建索引,因为索引已经排序,所以查询时可以利用索引的排序,加快排序查询。
索引优化
- 独立的列
在进行查询时,索引列不能是表达式的一部分,也不能是函数的参数,否则无法使用索引。
1 | SELECT actor_id FROM sakila.actor WHERE actor_id + 1 = 5; |
- 联合索引
在需要使用多个列作为条件进行查询时,使用多列索引比使用多个单列索引性能更好。例如下面的语句中,最好把 actor_id 和 film_id 设置为多列索引。
1 | SELECT film_id, actor_ id FROM sakila.film_actor |
- 索引列的顺序(最左匹配原则)
让选择性最强的索引列放在前面,可以减少回表的搜索范围。
- 索引下推
Mysql 5.6引入索引下推,可以在索引遍历过程中,对索引包含的字段先做判断过滤掉不符合的记录,减少回表次数。
1 | select name,tel from tuser where job='HR' and age>18 and ismale=0; |
- 前缀索引
对于 text 和 varchar 类型的列,使用前缀索引只索引某些部分字符关键字段。
eg: 用户邮箱字段建立索引,如:www.123456.qq.com 前后字段都是无用字符,可使用substring()截取中间关键字符添加索引。
- 覆盖索引
索引包含所有需要查询的字段的值。
1 | select id from itemCenter where size between 1 and 6; |
id是数据表主键,size索引上包含有这个值,不需要再去返回表中去查找id数据。
客户端执行SQL流程
查询性能优化
使用explain分析select查询语句的执行计划,可以通过分析explain结果来优化查询语句。
使用方式: 在查询select语句前加explain关键字。
explain输出信息的字段,分别的含义有:
- id: 包含一组数据,表示查询中执行select子语句或操作表的顺序;
- select_type: 表示查询的类型, 常用的有 SIMPLE 简单查询,UNION 联合查询, SUBQUERY 子查询等。
- table: 要查询的表
- possible_keys: 可选择的索引,但实际不一定使用到
- key: 实际使用到的索引
- key_len: 表示索引使用的字节数,在索引不失精度的情况下越短越好
- ref: 显示哪一列索引被使用了
- rows: 扫描的行数
- type: 对表的访问方式,表示mysql中找到所需行的方式,又称访问类型。
type值常用的类型有:all, index, range, ref, eq_ref, const, system, null 从左到右性能 以此增加。
- Extra: 包含不适合在其他列中显示的信息。
ps: 在进行索引优化时需要排除sql缓存影响。
优化数据访问
只返回必要的列:最好不要使用 SELECT * 语句。
只返回必要的行:使用 LIMIT 语句来限制返回的数据。
缓存重复查询的数据。
使用索引来覆盖查询减少服务器扫描的行数。
重构查询方式
切分大查询
如果一个大查询一次执行,可能锁住多条数据,耗尽系统资源,阻塞很多很小但很重要的查询。
1 | DELETE FROM messages WHERE create < DATE_SUB(NOW(), INTERVAL 3 MONTH); # 可使用limit进行分批删除 |
分解大链接查询
拆分成单表查询,可以让缓存更高效,减少锁竞争,并且更加容易对数据库进行拆分同时对sql的执行效率也有所提高。
1 | SELECT * FROM tag |
1 | SELECT * FROM tag WHERE tag='mysql'; |
事务
1.全局配置
在settings里面配置:
1 | "ATOMIC_REQUESTS": True, #全局开启事务,绑定的是http请求响应整个过程 |
使得一个http请求对应的所有sql都放在一个事务中执行,是全局性的配置。
2.局部配置
(1)函数装饰器
1 | from django.db import transaction |
(2)上下文管理器
1 | with transaction.atomic() |