查询当前会话产生的所有profiles;
mysql>select @@profileing;
mysql>set profiling=1;
mysql>show profiles;
//查询指定的query id
mysql>show profiles for query 2;
DDL的基础语法
- 对数据库进行定义
CREATE DATABASE nba; // 创建一个名为nba的数据库
DROP DATABASE nba; // 删除一个名为nba的数据库
2.对数据表进行定义
CREATE TABLE [](字段名 数据类型,......)
- 改变表结构
//添加字段
ALTER TABLE player ADD (age int(11));
//修改字段名
ALTER TABLE player RENAME COLUMN age to player_age
//修改字段的数据类型
ALTER TABLE player MODIFY (player_age float(3,1));
//删除字段
ALTER TABLE player DROP COLUMN player_age;
数据表常见约束
- 主键约束:UNIQUE+NOT NULL
- 外键约束
- 唯一性约束
- NOT NULL :不应该为空,必须有值
- DEFAULT:默认值
- CHECK约束:检查特定字段取值范围的有效性,CHECK约束的结果不为0
去除重复行:DISTINCT
- DISTINCT 需要放在所有列名的前面
- DISTINCT 其实是对后面所有列名的组合进行去重
如何排序检索数据:ORDER BY
- 排列的列名:ORDER BY后面可以有一个或多个列名,如果是多个列名,会按照第一个列先进行排序,当第一列的值相同的时候,再按照第二列进行排序。
- 排序的顺序:ORDER BY后面可以注明排序规则,ASC代表递增排序,DESC代表递减顺序。默认按照ASC递增排序。
- 非选择列排序:ORDER BY可以使用非选择列进行排序,所以即使在SELECT后面没有这个列名,同样可以放到ORDER BY后面进行排序。
约束返回结果的数量
不同的DBMS中使用的关键字不同:
// MySQL ,PostgreSQL,MariaDB和SQLite中使用LIMIT关键字,并且必须放到select语句的最后面
SELECT name, hp_max FROM heros ORDER BY hp_max DESC LIMIT 5;
// SQL Sever和Access,需要使用TOP关键字
SELECT TOP 5 name, hp_max FROM heros ORDER BY hp_max DESC;
// DB2,使用FETCH FIRST 5 ROWS ONLY这样的关键字
SELECT name, hp_max FROM heros ORDER BY hp_max DESC FETCH FIRST 5 ROWS ONLY
// Oracle,需要基于ROWNUM来统计行数
SELECT name, hp_max FROM heros WHERE ROWNUM <=5 ORDER BY hp_max DESC
SELECT的执行顺序
- 关键字的顺序不能颠倒
SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ...
- SELECT语句的执行顺序
FROM > WHERE > GROUP BY > HAVING > SELECT的字段 > DISTINCT > ORDER BY > LIMIT
SQL数据过滤:WHERE子句
WHERE子句的基本格式:
SELECT ……(列名) FROM ……(表名) WHERE ……(子句条件)
- 比较运算符
注意:使用BETWEEN...AND...的时候,是可以取到最大值和最小值的 - 逻辑运算符
注意:当WHERE子句中同时出现(),AND和OR操作符的时候,优先级顺序:()> AND > OR - 通配符
使用关键字:LIKE- 匹配0个或多个字符:%
- 匹配一个字符:_
SELECT name FROM heros WHERE name LIKE '_%太%'
注意:尽量少用通配符,因为它需要消耗数据库更长的时间来进行匹配,索引可能失效
SQL函数
-
算数函数
-
字符串函数
-
日期函数
- 转换函数
CAST 函数在转换数据类型的时候,不会四舍五入,如果原数值有小数,那么转换为整数类型的时候就会报错。不过你可以指定转化的小数类型,在 MySQL 和 SQL Server 中,你可以用DECIMAL(a,b)来指定,其中 a 代表整数部分和小数部分加起来最大的位数,b 代表小数位数,比如DECIMAL(8,2)代表的是精度为 8 位(整数加小数位数最多为 8 位),小数位数为 2 位的数据类型。所以SELECT CAST(123.123 AS DECIMAL(8,2))的转换结果为 123.12
注意: 大部分 DBMS 会有自己特定的函数,这就意味着采用 SQL 函数的代码可移植性是很差的,因此在使用函数的时候需要特别注意。
SQL聚集函数,分组过滤
- 聚集函数如下:
注意:
- COUNT()会忽略值为 NULL 的数据行,而 COUNT(*) 只是统计数据行数,不管某个字段是否为 NULL
- AVG、MAX、MIN 等聚集函数会自动忽略值为 NULL 的数据行
- 对数据进行分组:GROUP BY子句
- 对分组数据进行过滤:HAVING
HAVING 的作用和 WHERE 一样,都是起到过滤的作用,只不过 WHERE 是用于数据行,而 HAVING 则作用于分组。如果把 HAVING 替换成了 WHERE,SQL 则会报错。HAVING 支持所有 WHERE 的操作,因此所有需要 WHERE 子句实现的功能,你都可以使用 HAVING 对分组进行筛选。 - 使用顺序不可颠倒
SELECT ... FROM ... WHERE ... GROUP BY ... HAVING ... ORDER BY ...
子查询
- 什么是关联子查询和非关联子查询:
- 非关联子查询:子查询从数据表中查询了数据结果,如果这个数据结果只执行了一次,然后这个数据结果作为主查询的条件进行执行,那么这样的查询叫做关联子查询;
SELECT player_name, height FROM player WHERE height = (SELECT max(height) FROM player);
- 关联子查询:如果子查询需要执行多次,即采用循环的方式,先从外部查询开始,每次都传入子查询进行查询,然后再将结果反馈给外部,这种嵌套的执行方式就称为关联子查询
SELECT player_name, height FROM player WHERE height = (SELECT max(height) FROM player)
- EXISTS子查询
关联子查询通常会和EXISTS一起来使用,EXISTS子查询用来判断条件是否满足,满足的话为True,不满足为False。SELECT player_id, team_id, player_name FROM player WHERE EXISTS (SELECT player_id FROM player_score WHERE player.player_id = player_score.player_id)
-
集合比较子查询
集合比较子查询的作用是与另一个查询结果集进行比较,可以在子查询中使用IN,ANY,ALL和SOME操作符
//IN SELECT player_id, team_id, player_name FROM player WHERE player_id in (SELECT player_id FROM player_score WHERE player.player_id = player_score.player_id) //ANY SELECT player_id, player_name, height FROM player WHERE height > ANY (SELECT height FROM player WHERE team_id = 1002) //ALL SELECT player_id, player_name, height FROM player WHERE height > ALL (SELECT height FROM player WHERE team_id = 1002)
- 子查询作为计算字段
SELECT team_name, (SELECT count(*) FROM player WHERE player.team_id = team.team_id) AS player_num FROM team
连接
- 内连接:将多个表之间满足连接条件的数据行查询出来。包括等值连接,非等值连接和自连接
// 自然连接
SELECT player_id, a.team_id, player_name, height, team_name FROM player as a, team as b WHERE a.team_id = b.team_id
// ON连接
SELECT player_id, player.team_id, player_name, height, team_name FROM player JOIN team ON player.team_id = team.team_id
// USING连接
SELECT player_id, team_id, player_name, height, team_name FROM player JOIN team USING(team_id)
// 自连接
SELECT b.player_name, b.height FROM player as a JOIN player as b ON a.player_name = '布雷克-格里芬' and a.height < b.height
- 外连接:会返回一个表中的所有记录,以及另外一个表中匹配的行。包括左外连接,右外连接和全连接
// 左外连接
SELECT * FROM player LEFT JOIN team ON player.team_id = team.team_id
// 右外连接
SELECT * FROM player LEFT JOIN team ON player.team_id = team.team_id
// 全连接
SELECT * FROM player FULL JOIN team ON player.team_id = team.team_id
- 交叉连接:也称为笛卡尔积,返回左表中每一行与右表中每一行的组合
SELECT * FROM player CROSS JOIN team
视图 VIEW
视图作为一张虚拟表,帮我们封装来底层与数据表的接口。它相当于是一张表或多张表的数据结果集。
- 创建视图:CREATE VIEW
CREATE VIEW view_name AS
SELECT column1, column2
FROM table
WHERE condition
实际上就是我们在 SQL 查询语句的基础上封装了视图 VIEW,这样就会基于 SQL 语句的结果集形成一张虚拟表。其中 view_name 为视图名称,column1、column2 代表列名,condition 代表查询过滤条件。
当视图创建之后,就相当于一个虚拟表,可以直接使用:
SELECT * FROM player_above_avg_height
- 嵌套视图
当创建一张视图后,可以在其基础上继续创建视图
CREATE VIEW player_above_above_avg_height AS
SELECT player_id, height
FROM player
WHERE height > (SELECT AVG(height) from player_above_avg_height)
- 修改视图:ALTER VIEW
ALTER VIEW view_name AS
SELECT column1, column2
FROM table
WHERE condition
- 删除视图:DROP VIEW
DROP VIEW view_name
存储过程 PROCEDURE
存储过程是由SQL语句和流控制语句构成的语句集合,和函数一样可以接收输入参数,也可以返回输出参数给调用者,返回计算结果。一旦存储过程被创建出来,直接通过调用存储过程名即可。
- 定义一个存储过程:
// 创建
CREATE PROCEDURE 存储过程名称([参数列表])
BEGIN
需要执行的语句
END
// 调用存储过程
call 存储过程名称;
- CREATE PROCEDURE:创建存储过程
- DROP PROCEDURE:删除存储过程
- ALTER PROCEDURE:更新存储过程
-
参数类型
DELIMITER:
DELIMITER //
CREATE PROCEDURE `add_num`(IN n INT)
BEGIN
DECLARE i INT;
DECLARE sum INT;
SET i = 1;
SET sum = 0;
WHILE i <= n DO
SET sum = sum + i;
SET i = i +1;
END WHILE;
SELECT sum;
END //
DELIMITER ;
DELIMITER:用来临时定义新的结束符。默认情况下SQL采用(;)作为结束符,这样当存储过程每一句SQL结束后,采用分号作为结束符就相当于告诉SQL可以执行这一句来。存储过程作为一个整体,不希望SQL逐行执行,而是采用存储过程整段执行,因此就需要临时定义新的DELIMITER,新的结束符可以用(//)或者($$)。存储过程完成后需要重新把结束符换回(;)。
- 控制流语句
- BEGIN...END:中间包含多个语句,每个语句都以(;)结束符;
- DECLEAR:用来声明变量,使用的位置在BEGIN...END语句中间,而且需要在其他语句使用之前进行变量的声明;
- SET:赋值语句,用于对变量进行赋值;
- SELECT...INTO:把从数据表中查询的结果存放到变量中,也就是为变量赋值;
- IF...THEN...ENDIF:条件判断语句,还可以在IF...THEN...ENDIF中使用ELSE和ELSEIF来进行条件判断;
- CASE:用于多条件的分支判断;
- LOOP,LEAVE,ITERATE:LOOP是循环语句,使用LEAVE可以跳出循环,使用ITERATE可以进入下一次循环;
- REPEATE...UNTIL...END REPEAT:首先执行一次循环,然后在UNTIL中进行表达式判断,满足就退出,即END REPEAT,不满足继续执行循环;
- WHILE...DO...END WHILE:先进行条件判断,满足条件进行循环,不满足退出循环
事务 transaction
SHOW ENGINS查看当前存储引擎是否支持事务
InnoDB支持事务,而MyISAM不支持
- 事务的特性:ACID
- A,原子性(Atomicity),就是不可分割
- C,一致性(Consistency),指的是数据库在进行事务操作后,会由原来的一致状态,变成另一种一致的状态。也就是说事务提交后,或者事务发送回滚后,数据库的完整性约束不能被破坏
- I,隔离性(Isolation),每个事务彼此独立,不会受到其他是否的执行影响
- D,持久性(Durablity),事务提交后对数据的修改是持久的。即使在系统出故障的情况下,数据的修改依然有效。当事务完成,数据库的日志会更新,这时通过日志,让系统恢复到最后一次成功的更新状态
- 事务的常用控制语句
- START TRANSACTION或者BEGIN,显示开启一个事务;
- COMMIT:提交事务;
Oracle默认不自动提交,需要手写COMMIT,MySQL默认自动提交,也可以进行配置:mysql> set autocommit =0; //关闭自动提交 mysql> set autocommit =1; //开启自动提交
- ROLLBACK或者ROLLBACK TO [SAVEPOINT] ,回滚事务。可以撤销正在进行的所有没有提交的修改,或者将事务回滚到某个保存点;
- SAVEPOINT:在事务中创建保存点,方便后续针对保存点进行回滚,一个事务中可以设置多个保存点;
- RELEASE SAVEPOINT:删除某个保存点;
- SET TRANSACTION:设置事务的隔离级别;
- 事务并发处理异常
- 脏读(Dirty Read):读到了其他事务还没有提交的数据;
- 不可重复读(Unrepeatable Read):对某数据进行读取,发现两次读取的结果不同,也就是说没有读到相同的内容。这时因为有其他事务对这个数据同时进行了修改或删除;
- 幻读(Phantom Read):事务A根据条件查询得到了N条数据,但此时事务B更改或者增加了M条符合事务A查询条件的数据,这样当事务A再次进行查询的时候发现会有N+M条数据,产生了幻读
四种隔离级别:
- 读未提交:也就是允许读到未提交的数据,这种情况下查询是不会使用锁的,可能会产生脏读,不可重复读,幻读等情况
- 读已提交:只能读到已提交的内容,可以避免脏读的产生,但是如果要避免不可重复读或者幻读,需要在SQL查询的时候编写带加锁的SQL语句
- 可重复读:保证一个事务在相同查询条件下两次查询得到的数据结果一致,避免不可重复读和脏读,但是无法避免幻读
- 可串行化:将事务进行串行化,也就是在一个队列中按照顺序执行,是最高级别的隔离登记,但是牺牲了系统的并发性
游标
在数据库中,游标是一种临时的数据库对象,可以指向存储在数据库表中的数据行指针。这里游标充当了指针的作用,可以通过操作游标来对数据进行操作。
使用游标的步骤:
- 定义游标:
DECLEAR cursor_name CURSOR FOR select_statement; //select_statement代表SELECT语句
- 打开游标:
OPEN cursor_name;
打开游标的时候SELECT语句的查询结果集就会送到游标工作区
- 从游标中取得数据:
FETCH cursor_name INTO var_name,...;
使用cursor_name这个游标来读取当前行,并且将数据保存到var_name这个变量中,游标指针指到下一行。如果游标读取的数据行有多个列名,则在INTO关键字后面赋值给多个变量即可。
- 关闭游标:
CLOSE cursor_name;
- 释放游标:
DEALLOCATE cursor_name ;
游标实际上是面向过程的思维方式,与面向集合的思维方式不同的地方在于,游标更加关注“如何执行”。我们可以通过游标更加精细、灵活地查询和管理想要的数据行。有的时候,我们需要找特定数据,用 SQL 查询写起来会比较困难,比如两表或多表之间的嵌套循环查找,如果用 JOIN 会非常消耗资源,效率也可能不高,而用游标则会比较高效。
虽然在处理某些复杂的数据情况下,使用游标可以更灵活,但同时也会带来一些性能问题,比如在使用游标的过程中,会对数据行进行加锁,这样在业务并发量大的时候,不仅会影响业务之间的效率,还会消耗系统资源,造成内存不足,这是因为游标是在内存中进行的处理。如果有游标的替代方案,我们可以采用替代方案。
范式
- 1NF 需要保证表中每个属性都保持原子性;
- 2NF 需要保证表中的非主属性与候选键完全依赖;
- 3NF 需要保证表中的非主属性与候选键不存在传递依赖;
索引
索引就是帮助数据库管理系统高校获取数据的数据结构。
- 索引分类
- 功能逻辑划分:普通索引,唯一索引,主键索引
普通索引:基础索引,没有任何约束,主要用于提高查询效率;
唯一索引:在普通索引基础上增加了数据唯一的约束,一张数据表里面可以有多个唯一索引;
主键索引:在唯一索引的基础上增加了不为空的约束,也就是NOT NULL+UNIQUE,一张表里面最多只能有一个主键索引; - 物理实现方式划分:聚集索引,非聚集索引(二级索引或者辅助索引)
聚集索引:可以按照主键来排序存储数据,在查找行的时候非常有效,一个表只能有一个聚集索引;
非聚集索引:在数据库系统会有单独的存储空间存放非聚集索引,这些索引项是按照顺序存储的,但是索引项指向的内容是随机的。也就是说系统会进行两次查找,先找到索引,再找到索引对应的位置取出数据行。可以有多个非聚集索引,也就是多个索引目录提供数据检索。 - 按照字段个数划分:单一索引,联合索引
单一索引:索引列为一列;
联合索引:多个列组合在一起创建的索引,联合索引存在最左匹配原则,也就是按照最左优先的方式进行索引的匹配;
- 索引的使用
- 创建表的时候直接指定:
CREATE TABLE mytable( ID INT NOT NULL, username VARCHAR(16) NOT NULL, INDEX [indexName] (username(length)) );
- 修改表结构:
ALTER table tableName ADD INDEX indexName(columnName);
- 删除索引:
DROP INDEX [indexName] ON mytable;
- 显示索引信息:
mysql> SHOW INDEX FROM table_name;
- 创建索引的规律
- 字段的数值有唯一性的限制;
- 频繁作为WHERE查询条件的字段,尤其是在数据表大的情况下;
- 需要经常GROUP BY 和 ORDER BY的列;
- 多条件联合查询的时候最好创建联合索引;
- UPDATE,DELETE的WHERE条件列,一般也需要创建索引;
如果进行更新的时候,更新的字段是非索引字段,提升的效率会更明显,这是因为非索引字段更新不需要对索引进行维护。不过在实际工作中,我们也需要注意平衡,如果索引太多了,在更新数据的时候,如果涉及到索引更新,就会造成负担。 - DISTINCT字段需要创建索引;
- 做多表JOIN连接操作时,创建索引需要注意:
a. 连接表不要超过3张;
b. 对WHERE条件创建索引,因为WHERE才是对数据条件的过滤;
c. 对用于连接的字段创建索引,并且该字段在多张表中的类型必须一致;
- 不需要创建索引的情况:
- WHERE 条件(包括 GROUP BY、ORDER BY)里用不到的字段不需要创建索引,索引的价值是快速定位,如果起不到定位的字段通常是不需要创建索引的;
- 如果表记录太少,比如少于 1000 个,那么是不需要创建索引的;
- 字段中如果有大量重复数据,也不用创建索引;
- 索引失效:
- 如果索引进行了表达式计算,则会失效;
- 如果对索引使用函数,也会造成失效;
- 在 WHERE 子句中,如果在 OR 前的条件列进行了索引,而在 OR 后的条件列没有进行索引,那么索引会失效;
- 当我们使用 LIKE 进行模糊查询的时候,后面不能是 %;
- 索引列尽量设置为 NOT NULL 约束;
- 我们在使用联合索引的时候要注意最左原则,最左原则也就是需要从左到右的使用索引中的字段,一条 SQL 语句可以只使用联合索引的一部分,但是需要从最左侧开始,否则就会失效;
- 三星索引标准
三星索引标准可以让SQL查询效率最大化:- 在 WHERE 条件语句中,找到所有等值谓词中的条件列,将它们作为索引片中的开始列;
- 将 GROUP BY 和 ORDER BY 中的列加入到索引中;
- 将 SELECT 字段中剩余的列加入到索引片中;
锁
- 锁的分类:
-
按照锁的粒度进行划分:行锁,页锁,表锁
不同的数据库和存储引擎支持的锁粒度不同:
- 从数据库管理的角度划分:共享锁,拍他锁
共享锁:也叫做读锁或S锁,共享锁锁定的资源可以被其他用户读取,但是不能修改。在进行select的时候,会将对象进行共享锁锁定。
排它锁:也叫做独占锁,写锁或X锁。排他锁锁定的数据只允许进行锁定操作的事务使用,其他事务无法对已锁定的数据进行查询或修改。当我们对数据进行更新的时候,也就是INSERT,DELETE或者UPDATE的时候,数据库也会自动使用排它锁。//加共享锁 LOCK TABLE product_comment READ; //解锁 UNLOCK TABLE;
意向锁:就是给更大一级别的空间示意里面是否已经上过锁。比如数据页或数据表加上意向锁,告诉其他人这个数据页或数据表已经有人上过排它锁了,这样当其他人想要获取数据表排它锁的时候,只需要了解是否有人已经获取了这个数据表的意向排他锁即可。//加排它锁 LOCK TABLE product_comment WRITE; //解锁 UNLOCK TABLE;
-
程序员角度划分:乐观锁,悲观锁(这两种锁是两种看待数据并发的思维方式)
乐观锁:认为对同一数据的并发操作不会总发生,属于小概率事件,不用每次都对数据上锁,也就是不采用数据库自身的锁机制,而是通过程序来实现(版本号机制或者时间戳机制)。乐观锁适合读操作多的场景。
悲观锁:对诗句被其他事务的修改持保守太大,会通过数据库自身的锁机制来实现,从而保证数据操作的排他性。悲观锁适合写操作多的场景。
- 死锁
死锁就是多个事务在执行过程中,因为竞争某个相同的资源而造成阻塞的现象。当死锁发生的时候,就需要一个事务进行回滚,另一个事务获取锁定完成事务,然后将锁释放掉。
避免死锁的方法:
- 如果事务涉及多个表,操作比较复杂,那么可以尽量一次锁定所有的资源,而不是逐步来获取;
- 如果事务需要更新数据表中的大部分数据,数据表又比较大,可以采用锁升级的方式,比如将行锁升级为表锁;
- 不同事务并发读写多张数据表,可以约定访问表的顺序,采用相同的顺序降低死锁发生的概览;