编写高效 SQL 需要以下知识

有关所查询内容的物理组织的知识

数据库能做什么的知识, 例如: 如果你不知道跳跃扫描索引及其用途, 那么你可能会看着模式说”索引丢了”

SQL 所有错综复杂的知识

对目标的真实理解-问题是什么

访问方法

全扫描,

各种类型的索引访问

通过散列 或 ROWIN 方式直接访问. 类似 3PL语言中的GOTO语句.

全扫描

全扫描是读取 ORACLE 的大量数据行的行之有效的方法, 因为数据库使用多块读取, 需要记住, 全扫描并不邪恶, 在很多情况下, 全扫描是获得结果的最快方法.

全扫描与高水位的关系

ROWID 访问

ROWID 是数据的物理地址, 一个ROWID包含关于文件, 块和该块中的行的一行信息. ROWID对于获取一个特定的行来说可能是最快的方法. 但是, 使用ROWID获取成千上万行不是最好的办法. 通常, ROWID用于访问经过某种排序索引扫描后的表, 另外, 还可以直接使用 ROWID, 例如:

select *

from wwc_people_people where ROWID IN (select row_id from wwc_fast_people where staring like :bv and rownum < 101);

另外, 还可以使用 between and, 例如:

select * from t where ROWID between :a1 and :a2

索引扫描

是最常用的访问数据的方式

B*树结构 : 该书中的最低级的块(成为叶子结点)包含了每个索引键值和一个指向正在索引的行的 ROWID(换句话说, 通过索引键值, 找到ROWID, 在通过ROWID去找真正的行), 有趣的是, 索引的叶子结点实际上是一个双向链表, 这样, 要执行区间扫描就很容易, 直接找这个结点的前边或者后边就可以了.

有两个经常发生的主索引扫描: 索引唯一扫描和索引范围扫描 , 索引跳跃扫描, 索引全扫描, 快速全扫描, 索引连接

索引唯一扫描(Index-unique scans), 优化程序知道索引中索引列是唯一的, 从索引查找中至多返回一行.

索引范围扫描, 对于索引范围扫描, 优化器知道可能返回 0行, 1行或多行. 例如 select max(empno) from emp; 这个查询, oracle 不是将全表扫描一遍然后查出最大的, 而是直接通过索引的后面开始读取最后(最高)的值. (因为一般情况下索引是按照从低到高存储的)

索引跳跃扫描, 通常, 为了使用一个索引, 定义在索引前面的列要在查询中引用, 比如: create table t(a, b, c, d, e, f, g);  create index t_idx on t (a, b, c);

通常我们可以使用 select * from t where a = :a;  select * from t where a = :a and b = :b; 但是下列查询不能使用该索引:

select * from t where b = :b and c = :c;  (因为没有从  a 开始)

oracle 8i 已前, 我们可以使用提示 select /*+ index(t t_idx) */ * from t; 不过, 从 oracle 9i 以后,

oracle 9i 中实现了索引跳跃扫描的概念, 它将在以下条件中使用:

  • 在谓词中引用索引里的列(例如 上边的 b 和 c)
  • 优化程序知道该索引前面的列很少有不连续的值

索引全扫描, 就是说, 首先通过索引结构找到第一个叶子块, 然后由于所有的叶子块组成了一个双向链表, 接下来, 可以通过这个链表的指针关系, 来找到下一个块.

索引快速全扫描, 与索引全扫描有明显不同, 索引快速全扫描有以下特征:

  • 它读取索引结构中的每个块, 包括所有的内部分枝块
  • 它采用多块读取, 就像表的全扫描一样.
  • 它不按排序顺序检索数据

索引连接, 是在表中存在多个索引时针对某个查询所选中的索引路径, 该索引中应该包含了查询中选中的全部列(不需要访问表)

连接概念

嵌套循环连接, 数据库的主要连接方法.

select ename, dname from emp, dept where emp.deptno = dept.deptno

执行计划显示: NESTED LOOPS, 以上语句的执行方法是, 伪代码如下:

for x in (select * from emp)

loop

index lookup ROWID for x.deptno   -- 找到每一个deptno 的 row id

select * from dept where rowid = that rowid

output joined record

end loop

执行步骤:

1) 读取 emp 表的第一块.

2) 取出第一行并使用其 DEPTNO 值

3) 至多读取 两个或三个在索引中查找到键值的索引块.

4) 读取DEPT表的引用块.

有嵌套循环的外连接, 执行计划显示: NESTED LOOPS (OUTER)

散列连接, 执行计划显示 hash join

例如:

select t1.object_name, t2.object_name from t t1, t t2

where t1.object_id = t2.object_id and t1.owner=’WMSYS’;

执行计划显示:

在最佳条件下, oracle 将取两个表中较小的, 在本例中, 将取全扫描T1后的 t1.owner=’WMSYS’的结果集-并创建此结果的一个内存中的散列表, 散列表时一个阵列, 与散列键相随的行存储在该索引的项中. 因此, oracle现在有一个可以快速的进行访问的内存结构-阵列, 如果现在从 T2中获取一个 OBJECT_ID值, oracle可以散列它并再内存中检索T1中相匹配的行. 由于此散列表在私有内存中, 访问此数据结构不会招致一般逻辑I/O中的闩锁活动. 一旦 oracle 将较小的表散列到内存中, 则它全扫描较大的表(本例中的T2), 然后对每一行, 它散列检索到的 object_id, 查找散列表中相匹配的行, 返回连接映像. 客户应用系统在得到第一行前需要等待oracle完全扫描T1并进行散列, 然后其他行的获取速度越来越快.

如果是外连接, 首先执行计划显示 hash join (OUTER), 另外, 外连接时, 先针对主表进行散列, 并存在内存中.

通常, 散列连接擅长两个一大一小结果集.

排序合并连接, 执行计划显示, Sort-Merge Join

排序合并连接, 将依次排序第一个输入集, 排序第2个输入集, 然后合并结果.

通常, 排序-合并连接在非相等连接操作中有用: 即, 在连接条件不是一个等式而是范围比较时(比如, <, >=)

例如:

select a.ename, b.ename, a.hiredate, b.hiredate from emp a , emp b

where a.hiredate <= b.hiredate and a.empno <> b.empno;

笛卡尔连接, 两个表没有连接条件的全连接, 比如第一个表有10行, 第2个表有10行, 连接后, 10*10=100 行

反连接(Anti-join) 反连接用于从一个表中返回不在另一个数据源中的数据行, 例如:

select * from dept where deptno not in (select deptno from emp)

测试 not in 问题:

create table t1
as select * from all_objects where rownum <= 10000;

create table t2
as select * from all_objects where rownum <= 9950;

create index t2_idx on t2(object_id);

1) not in

select count(*) from scott.t1 rbo
where object_id not in (select object_id from scott.t2);

2) not exists

select count(*) from scott.t1 rbo where not exists (select null from scott.t2 where t2.object_id = rbo.object_id);

3) 外连接

select count(*) from t1 , t2 rbo where t1.object_id = rbo.object_id(+) and rbo.object_id is NULL

在 CBO 模式下:

not exist 方法最耗时, 逻辑I/O最多

外连接 居中

not in 方法最好

在 RBO 模式下:

not in 方法最不好

外连接 居中

not exist 最好

综上,

1)首先外连接的方法实现反连接, 因为在 RBO和CBO, 性能都可以

2) 在CBO下, 可以使用 not in , 所以, 在使用 not in 时, 使用 hint 语句强制采用 CBO 优化计划.

另外, 在实现反连接时候, 一定要注意参考数据源中出现 null 的情况, 如果数据源字段没有 not null限制, 则应该使用 is not null 条件来限制:

真正理解SQL

with players as
( select 'P' || ROWNUM username
    from all_objects
   where rownum <= 8),
weeks as
(select rownum week
   from all_objects
  where rownum <= 7)
select week,
       max(decode(rn, 1, username, null)) u1,
       max(decode(rn, 2, username, null)) u2,
       max(decode(rn, 3, username, null)) u3,
       max(decode(rn, 4, username, null)) u4,
       max(decode(rn, 5, username, null)) u5,
       max(decode(rn, 6, username, null)) u6,
       max(decode(rn, 7, username, null)) u7,
       max(decode(rn, 8, username, null)) u8
  from ( select username,
                week,
                row_number() over (partition by week order by rnd) rn
           from (select username, week, dbms_random.random rnd from players, weeks)
       )
group by week;

分析函数, 如上蓝色部分

前边一个函数, over (区域, 空白表示全部区域) 别名

08 高效的SQL的更多相关文章

  1. Oracle如何写出高效的SQL

    转载:http://www.blogjava.net/ashutc/archive/2009/07/19/277215.html 1.选择最有效率的表明顺序(只在基于规则的优化器中有效) Oracle ...

  2. Oracle 如何写出高效的 SQL

    转自:Oracle 如何写出高效的 SQL 要想写出高效的SQL 语句需要掌握一些基本原则,如果你违反了这些原则,一般情况下SQL 的性能将会很差. 1. 减少数据库访问次数连接数据库是非常耗时的,虽 ...

  3. 如何编写高效的SQL

    编写高效的sql可以给用户带来好的体验,需要开发这没有意识到这一点因为他们不关心也不知道怎么做.高效的sql可以给用户节省很多时间,这样就不需要dba来找上门,也不需要搭建RCA和性能调优. 性能不好 ...

  4. 理解SQL原理,写出高效的SQL语句

    我们做软件开发的,大部分人都离不开跟数据库打交道,特别是erp开发的,跟数据库打交道更是频繁,存储过程动不动就是上千行,如果数据量大,人员流动大,那么我们还能保证下一段时间系统还能流畅的运行吗?我们还 ...

  5. MySql如何编写高效的SQL

    最近应团队要求,研究整理了下,mysql相关的优化,有些是根据实际java项目中碰到的情况经验之谈.欢迎讨论~ SQL 语言是一种强大而且灵活的语言,在使用 SQL 语言来执行某个关系查询的时候,用户 ...

  6. 高效的SQL分页存储过程

    CREATE PROCEDURE SP_CommonPageList @Fields VARCHAR(500), @From VARCHAR(1000), @Condition VARCHAR(100 ...

  7. MySql高效分页SQL

    public string GetQuerySql(ITSPAreaQueryModel model, object state = null) { ); sqlBuilder.AppendForma ...

  8. SQL调优学习之——sqlserver分页从低效到高效

    背景 首先感谢网友@aixuexi 在评论中的提醒,原博文介绍的几种都不是最高效,现已修改加入另一种更高效的方法. 以前都是使用mysql和oracle,对sqlserver的使用不多.最近因项目原因 ...

  9. [转]SQL Server 高性能写入的一些总结

    本文转自:http://www.cnblogs.com/rush/archive/2012/08/31/2666090.html 1.1.1 摘要 在开发过程中,我们不时会遇到系统性能瓶颈问题,而引起 ...

随机推荐

  1. Codeforces 745C:Hongcow Builds A Nation(并查集)

    http://codeforces.com/problemset/problem/744/A 题意:在一个图里面有n个点m条边,还有k个点是受限制的,即不能从一个受限制的点走到另外一个受限制的点(有路 ...

  2. northwind数据库

    ① Categories: 种类表相应字段:CategoryID :类型ID:CategoryName:类型名;Description:类型说明;Picture:产品样本 ② CustomerCust ...

  3. 网络统计学与web前端开发基础技术

    网络统计学与web前端开发基础技术 学习web前端开发基础技术(网页设计)需要了解:HTML.CSS.JavaScript三种语言.下面我们就来了解一下这三门技术在网页设计中的用途: HTML是网页内 ...

  4. [转]关于 initWithNibName 和 loadNibNamed 的区别和联系-iPhone成长之路

    转载地址:http://blog.sina.com.cn/s/blog_7b9d64af01018f2u.html 关于 initWithNibName 和 loadNibNamed 的区别和联系.之 ...

  5. jdbc连接集合

    JDBC里统一的使用方法:       Class.for(jdbcDriverName);     Connection conn=DriverManager.getConnection(url,u ...

  6. JAVA基础知识之JVM-——使用反射生成并操作对象

    Class对象可以获取类里的方法,由Method对象表示,调用Method的invoke可以执行对应的方法:可以获取构造器,由Constructor对象表示,调用Constructor对象的newIn ...

  7. nohup & rabbitmq & python

    用Python脚本执行rabbitmq的消费 nohup python consumer.py > out.log & 结果郁闷啊,怎么都查看不到输出! 终于找到了答案: 原来pytho ...

  8. 2016年11月10日 星期四 --出埃及记 Exodus 20:1

    2016年11月10日 星期四 --出埃及记 Exodus 20:1 And God spoke all these words: 神吩咐这一切的话说,

  9. (1)redis下载编译

    一.redis下载编译 这里没什么好说的 用的版本是redis-2.8.17 1)redis-server是可执行程序 2)mian函数在redis.c里面 3)如果要修改调试 这届在src目录下   ...

  10. C89标准库函数手册(待整理)

    http://zh.cppreference.com/w/c 前言 ANSI C(C89)标准库函数共有15个头文件.这15个头文件分别为: 1.<assert.h>            ...