SQL 的 select 语句完整的执行顺序
SQL Select 语句完整的执行顺序:
1、from 子句组装来自不同数据源的数据;
2、where 子句基于指定的条件对记录行进行筛选;
3、group by 子句将数据划分为多个分组;
4、使用聚集函数进行计算;
5、使用 having 子句筛选分组;
6、计算所有的表达式;
7、select 的字段;
8、使用 order by 对结果集进行排序。
SQL 语言不同于其他编程语言的最明显特征是处理代码的顺序。在大多数据库语言中,代码
按编码顺序被处理。但在 SQL 语句中,第一个被处理的子句式 FROM,而不是第一出现的
SELECT。SQL 查询处理的步骤序号:
(1) FROM <left_table>
(2) <join_type> JOIN <right_table>
(3) ON <join_condition>
(4) WHERE <where_condition>
(5) GROUP BY <group_by_list>
(6) WITH {CUBE | ROLLUP}
(7) HAVING <having_condition>
(8) SELECT
(9) DISTINCT
(9) ORDER BY <order_by_list>
(10) <TOP_specification> <select_list>
以上每个步骤都会产生一个虚拟表,该虚拟表被用作下一个步骤的输入。这些虚拟表对调用
者(客户端应
用程序或者外部查询)不可用。只有最后一步生成的表才会会给调用者。如果没有在查询中
指定某一个子句,将跳过相应的步骤。
逻辑查询处理阶段简介:
1、 FROM:对 FROM 子句中的前两个表执行笛卡尔积(交叉联接),生成虚拟表 VT1。
2、 ON:对 VT1 应用 ON 筛选器,只有那些使为真才被插入到 TV2。
3、 OUTER (JOIN):如果指定了 OUTER JOIN(相对于 CROSS JOIN 或 INNER JOIN),保留表
中未找到
匹配的行将作为外部行添加到 VT2,生成 TV3。如果 FROM 子句包含两个以上的表,则对上
一个联接生成的
结果表和下一个表重复执行步骤 1 到步骤 3,直到处理完所有的表位置。
4、 WHERE:对 TV3 应用 WHERE 筛选器,只有使为 true 的行才插入 TV4。
5、 GROUP BY:按 GROUP BY 子句中的列列表对 TV4 中的行进行分组,生成 TV5。
6、 CUTE|ROLLUP:把超组插入 VT5,生成 VT6。
7、 HAVING:对 VT6 应用 HAVING 筛选器,只有使为 true 的组插入到 VT7。
8、 SELECT:处理 SELECT 列表,产生 VT8。
9、 DISTINCT:将重复的行从 VT8 中删除,产品 VT9。
10、 ORDER BY:将 VT9 中的行按 ORDER BY 子句中的列列表顺序,生成一个游标(VC10)。
11、 TOP:从 VC10 的开始处选择指定数量或比例的行,生成表 TV11,并返回给调用者。
where 子句中的条件书写顺序
SQL 之聚合函数
聚合函数是对一组值进行计算并返回单一的值的函数,它经常与 select 语句中的 group
by 子句一同使
用。
a. avg():返回的是指定组中的平均值,空值被忽略。
b. count():返回的是指定组中的项目个数。
c. max():返回指定数据中的最大值。
d. min():返回指定数据中的最小值。
e. sum():返回指定数据的和,只能用于数字列,空值忽略。
SQL 之连接查询(左连接和右连接的区别)
外连接:
左连接(左外连接):以左表作为基准进行查询,左表数据会全部显示出来,右表如果和左
表匹配的
数据则显示相应字段的数据,如果不匹配则显示为 null。
右连接(右外连接):以右表作为基准进行查询,右表数据会全部显示出来,左表如果和右
表匹配的数据则显示相应字段的数据,如果不匹配则显示为 null。
全连接:先以左表进行左外连接,再以右表进行右外连接。
内连接:显示表之间有连接匹配的所有行。
SQL 之 sql 注入
通过在 Web 表单中输入(恶意)SQL 语句得到一个存在安全漏洞的网站上的数据库,而不
是按照设计者意图去执行 SQL 语句。举例:当执行的 sql 为 select * from user where
username = “admin”or “a”=“a”时,sql 语句恒成立,参数 admin 毫无意义。
防止 sql 注入的方式:
1. 预编译语句:如,select * from user where username = ?,sql 语句语义不会发生
改
变,sql 语句中变量用?表示,即使传递参数时为“admin or ‘a’= ‘a’”,也会把这
整体当做一个字符创去查询。
2. Mybatis 框架中的 mapper 方式中的 # 也能很大程度的防止 sql 注入($无法防止 sql
注入)。
Mysql 性能
1、当只要一行数据时使用 limit 1
查询时如果已知会得到一条数据,这种情况下加上 limit 1 会增加性能。因为 mysql 数据
库引擎会在找到一条结果停止搜索,而不是继续查询下一条是否符合标准直到所有记录查询
完毕。
2、选择正确的数据库引擎
Mysql 中有两个引擎 MyISAM 和 InnoDB,每个引擎有利有弊。
MyISAM适用于查询多的操作,支持表锁,不支持事物,外键,支持的约束很少。InnoDB适用于修改多的操作,支持行锁、表锁,事物,外键。
3、对操作符的优化,尽量不采用不利于索引的操作符,比如以%开头,有比较运算符,有函数等。
事务的四大特征是什么?
数据库事务 transanction 正确执行的四个基本要素。ACID,原子性(Atomicity)、一致
性(Correspondence)、隔离性(Isolation)、持久性(Durability)。
(1)原子性:整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞
在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,
就像这个事务从来没有执行过一样。
(2)一致性:在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。
(3)隔离性:隔离状态执行事务,使它们好像是系统在给定时间内执行的唯一操作。
如果有两个事务,运行在相同的时间内,执行相同的功能,事务的隔离性将确保每一事务在
系统中认为只有该事务在使用系统。这种属性有时称为串行化,为了防止事务操作间的混淆,
必须串行化或序列化请 求,使得在同一时间仅有一个请求用于同一数据。
(4)持久性:在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库
之中,并不会被回滚。
Mysql 中四种隔离级别分别是什么?
隔离级别 脏读 不可重复读 幻读
Read uncommitted(读未提交) 是 是 是
Read committed(读已提交) 否 是 是
Repeatable read(可重复读) 否 否 是
Serializable(串行读) 否 否 否
读未提交(READ UNCOMMITTED):未提交读隔离级别也叫读脏,就是事务可以读取其它
事务未提交的数据。
读已提交(READ COMMITTED):在其它数据库系统比如 SQL Server 默认的隔离级别就
是提交读,已提交读隔离级别就是在事务未提交之前所做的修改其它事务是不可见的。
可重复读(REPEATABLE READ):保证同一个事务中的多次相同的查询的结果是一致的,
比如一个事务一开始查询了一条记录然后过了几秒钟又执行了相同的查询,保证两次查询的
结果是相同的,可重复读也是 mysql 的默认隔离级别。
可串行化(SERIALIZABLE):可串行化就是保证读取的范围内没有新的数据插入,比如
事务第一次查询得到某个范围的数据,第二次查询也同样得到了相同范围的数据,中间没有
新的数据插入到该范围中。
MySQL 怎么创建存储过程
MySQL 存储过程是从 MySQL5.0 开始增加的新功能。存储过程的优点有一箩筐。不过最
主要的还是执行效率和 SQL 代码封装。特别是 SQL 代码封装功能,如果没有存储过程,在
外部程序访问数据库时,要组织很多 SQL 语句。特别是业务逻辑复杂的时候,一大堆的 SQL
和条件夹杂在代码中,让人不寒而栗。现在有了 MySQL 存储过程,业务逻辑可以封装存储
过程中,这样不仅容易维护,而且执行效率也高。
一、创建 MySQL 存储过程
下面代码创建了一个叫 pr_add 的 MySQL 存储过程,这个 MySQL 存储过程有两个 int
类型的输入参数“a”、“b”,返回这两个参数的和。
1)drop procedure if exists pr_add; (备注:如果存在 pr_add 的存储过程,则先
删掉)
2)计算两个数之和(备注:实现计算两个整数之和的功能)
create procedure pr_add ( a int, b int ) begin declare c int;
if a is null then set a = 0;
end if;
if b is null then set b = 0;
end if;
set c = a + b;
select c as sum;
调用 MySQL 存储过程
call pr_add(10, 20);
MySQL 语句优化
where 子句中可以对字段进行 null 值判断吗?
可以,比如 select id from t where num is null 这样的 sql 也是可以的。但是最
好不要给数据库留 NULL,尽可能的使用 NOT NULL 填充数据库。不要以为 NULL 不需要空
间,比如:char(100) 型,在字段建立时,空间就固定了,不管是否插入值(NULL 也包含
在内),都是占用 100 个字符的空间的,如果是 varchar 这样的变长字段,null 不占用
空间。可以在 num 上设置默认值 0,确保表中 num 列没有 null 值,然后这样查询: select
id from t where num = 0。
select * from admin left join log on admin.admin_id = log.admin_id where
log.admin_id>10 如何优化?
优化为: select * from (select * from admin where admin_id>10) T1 lef join log
on T1.admin_id = log.admin_id。
使用 JOIN 时候,应该用小的结果驱动大的结果(left join 左边表结果尽量小如果有
条件应该放到左边先处理,right join 同理反向),同时尽量把牵涉到多表联合的查询拆
分多个 query(多个连表查询效率低,容易到之后锁表和阻塞)。
limit 的基数比较大时使用 between
例如:select * from admin order by admin_id limit 100000,10
优化为:select * from admin where admin_id between 100000 and 100010 order by
admin_i
尽量避免在列上做运算,这样导致索引失效
例如:select * from admin where year(admin_time)>2014
优化为: select * from admin where admin_time> '2014-01-01′
说下原生 jdbc 操作数据库流程?
第一步:Class.forName()加载数据库连接驱动;
第二步:DriverManager.getConnection()获取数据连接对象;
第三步:根据 SQL 获取 sql 会话对象,有 2 种方式 Statement、PreparedStatement ;
第四步:执行 SQL 处理结果集,执行 SQL 前如果有参数值就设置参数值 setXXX();
第五步:关闭结果集、关闭会话、关闭连接。
什么要使用 PreparedStatement?
速度快、防止SQL注入、书写的SQL更容易阅读
关系数据库中连接池的机制是什么?
前提:为数据库连接建立一个缓冲池。
1:从连接池获取或创建可用连接
2:使用完毕之后,把连接返回给连接池
3:在系统关闭前,断开所有连接并释放连接占用的系统资源
4:能够处理无效连接,限制连接池中的连接总数不低于或者不超过某个限定值。
其中有几个概念需要大家理解:
最小连接数是连接池一直保持的数据连接。如果应用程序对数据库连接的使用量不大,
将会有大量的数据库连接资源被浪费掉。
最大连接数是连接池能申请的最大连接数。如果数据连接请求超过此数,后面的数据连
接请求将被加入到等待队列中,这会影响之后的数据库操作。
如果最小连接数与最大连接数相差太大,那么,最先的连接请求将会获利,之后超过最
小连接数量的连接请求等价于建立一个新的数据库连接。不过,这些大于最小连接数的数据
库连接在使用完不会马上被释放,它将被放到连接池中等待重复使用或是空闲超时后被释
放。
上面的解释,可以这样理解:数据库池连接数量一直保持一个不少于最小连接数的数量,
当数量不够时,数据库会创建一些连接,直到一个最大连接数,之后连接数据库就会等待。