数据库

位置:IT落伍者 >> 数据库 >> 浏览文章

通过分析SQL语句的执行计划优化SQL(三)


发布日期:2023年11月23日
 
通过分析SQL语句的执行计划优化SQL(三)
章SQL语句处理的过程

在调整之前我们需要了解一些背景知识只有知道这些背景知识我们才能更好的去调整sql语句

本节介绍了SQL语句处理的基本过程主要包括

· 查询语句处理

· DML语句处理(insert update delete)

· DDL 语句处理(create drop alter )

· 事务控制(commit rollback)

SQL 语句的执行过程(SQL Statement Execution)

概要的列出了处理和运行一个sql语句的需要各个重要阶段在某些情况下Oracle运行sql的过程可能与下面列出的各个阶段的顺序有所不同如DEFINE阶段可能在FETCH阶段之前这主要依赖你如何书写代码

对许多oracle的工具来说其中某些阶段会自动执行绝大多数用户不需要关心各个阶段的细节问题然而知道执行的各个阶段还是有必要的这会帮助你写出更高效的SQL语句来而且还可以让你猜测出性能差的SQL语句主要是由于哪一个阶段造成的然后我们针对这个具体的阶段找出解决的办法

SQL语句处理的各个阶段

DML语句的处理

本节给出一个例子来说明在DML语句处理的各个阶段到底发生了什么事情假设你使用Pro*C程序来为指定部门的所有职员增加工资程序已经连到正确的用户你可以在你的程序中嵌入如下的SQL语句

EXEC SQL UPDATE employees

SET salary = * salaryWHERE department_id = :var_department_id; var_department_id是程序变量里面包含部门号我们要修改该部门的职员的工资当这个SQL语句执行时使用该变量的值

每种类型的语句都需要如下阶段

· 第步: Create a Cursor 创建游标

· 第步: Parse the Statement分析语句

· 第步: Bind Any Variables 绑定变量

· 第步: Run the Statement 运行语句

· 第步: Close the Cursor 关闭游标

如果使用了并行功能还会包含下面这个阶段

· 第步: Parallelize the Statement 并行执行语句

如果是查询语句则需要以下几个额外的步骤如图 所示

· 第步: Describe Results of a Query 描述查询的结果集

· 第步: Define Output of a Query 定义查询的输出数据

· 第步: Fetch Rows of a Query 取查询出来的行

下面具体说一下每一步中都发生了什么事情

步: 创建游标(Create a Cursor)

由程序接口调用创建一个游标(cursor)任何SQL语句都会创建它特别在运行DML语句时都是自动创建游标的不需要开发人员干预多数应用中游标的创建是自动的然而在预编译程序(pro*c)中游标的创建可能是隐含的也可能显式的创建在存储过程中也是这样的

步:分析语句(Parse the Statement)

在语法分析期间SQL语句从用户进程传送到OracleSQL语句经语法分析后SQL语句本身与分析的信息都被装入到共享SQL区在该阶段中可以解决许多类型的错误

语法分析分别执行下列操作

l 翻译SQL语句验证它是合法的语句即书写正确

l 实现数据字典的查找以验证是否符合表和列的定义

l 在所要求的对象上获取语法分析锁使得在语句的语法分析过程中不改变这些对象的定义

l 验证为存取所涉及的模式对象所需的权限是否满足

l 决定此语句最佳的执行计划

l 将它装入共享SQL区

l 对分布的语句来说把语句的全部或部分路由到包含所涉及数据的远程节点

以上任何一步出现错误都将导致语句报错中止执行

只有在共享池中不存在等价SQL语句的情况下才对SQL语句作语法分析在这种情况下数据库内核重新为该语句分配新的共享SQL区并对语句进行语法分析进行语法分析需要耗费较多的资源所以要尽量避免进行语法分析这是优化的技巧之一

语法分析阶段包含了不管此语句将执行多少次而只需分析一次的处理要求Oracle只对每个SQL语句翻译一次在以后再次执行该语句时只要该语句还在共享SQL区中就可以避免对该语句重新进行语法分析也就是此时可以直接使用其对应的执行计划对数据进行存取这主要是通过绑定变量(bind variable)实现的也就是我们常说的共享SQL后面会给出共享SQL的概念

虽然语法分析验证了SQL语句的正确性但语法分析只能识别在SQL语句执行之前所能发现的错误(如书写错误权限不足等)因此有些错误通过语法分析是抓不到的例如在数据转换中的错误或在数据中的错(如企图在主键中插入重复的值)以及死锁等均是只有在语句执行阶段期间才能遇到和报告的错误或情况

查询语句的处理

查询与其它类型的SQL语句不同因为在成功执行后作为结果将返回数据其它语句只是简单地返回成功或失败而查询则能返回一行或许多行数据查询的结果均采用表格形式结果行被一次一行或者批量地被检索出来从这里我们可以得知批量的fetch数据可以降低网络开销所以批量的fetch也是优化的技巧之一

有些问题只与查询处理相关查询不仅仅指SELECT语句同样也包括在其它SQL语句中的隐含查询例如下面的每个语句都需要把查询作为它执行的一部分

INSERT INTO table SELECT

UPDATE table SET x = y WHERE

DELETE FROM table WHERE

CREATE table AS SELECT

具体来说查询

· 要求读一致性

· 可能使用回滚段作中间处理

· 可能要求SQL语句处理描述定义和取数据阶段

步: 描述查询结果(Describe Results of a Query)

描述阶段只有在查询结果的各个列是未知时才需要例如当查询由用户交互地输入需要输出的列名在这种情况要用描述阶段来决定查询结果的特征(数据类型长度和名字)

步: 定义查询的输出数据(Define Output of a Query)

在查询的定义阶段你指定与查询出的列值对应的接收变量的位置大小和数据类型这样我们通过接收变量就可以得到查询结果如果必要的话Oracle会自动实现数据类型的转换这是将接收变量的类型与对应的列类型相比较决定的

步: 绑定变量(Bind Any Variables)

此时Oracle知道了SQL语句的意思但仍没有足够的信息用于执行该语句Oracle 需要得到在语句中列出的所有变量的值在该例中Oracle需要得到对department_id列进行限定的值得到这个值的过程就叫绑定变量(binding variables)

此过程称之为将变量值捆绑进来程序必须指出可以找到该数值的变量名(该变量被称为捆绑变量变量名实质上是一个内存地址相当于指针)应用的最终用户可能并没有发觉他们正在指定捆绑变量因为Oracle 的程序可能只是简单地指示他们输入新的值其实这一切都在程序中自动做了因为你指定了变量名在你再次执行之前无须重新捆绑变量你可以改变绑定变量的值而Oracle在每次执行时仅仅使用内存地址来查找此值如果Oracle 需要实现自动数据类型转换的话(除非它们是隐含的或缺省的)你还必须对每个值指定数据类型和长度关于这些信息可以参考oracle的相关文档如Oracle Call Interface Programmers Guide

步: 并行执行语句(Parallelize the Statement )

ORACLE 可以在SELECTs INSERTs UPDATEs MERGEs DELETEs语句中执行相应并行查询操作对于某些DDL操作如创建索引用子查询创建表在分区表上的操作也可以执行并行操作并行化可以导致多个服务器进程(oracle server processes)为同一个SQL语句工作使该SQL语句可以快速完成但是会耗费更多的资源所以除非很有必要否则不要使用并行查询

步: 执行语句(Run the Statement)

到了现在这个时候Oracle拥有所有需要的信息与资源因此可以真正运行SQL语句了如果该语句为SELECT查询或INSERT语句则不需要锁定任何行因为没有数据需要被改变然而如果语句为UPDATE或DELETE语句则该语句影响的所有行都被锁定防止该用户提交或回滚之前别的用户对这些数据进行修改这保证了数据的一致性对于某些语句你可以指定执行的次数这称为批处理(array processing)指定执行N次则绑定变量与定义变量被定义为大小为N的数组的开始位置这种方法可以减少网络开销也是优化的技巧之一

步: 取出查询的行(Fetch Rows of a Query)

在fetch阶段行数据被取出来每个后续的存取操作检索结果集中的下一行数据直到最后一行被取出来上面提到过批量的fetch是优化的技巧之一

步: 关闭游标(Close the Cursor)

SQL语句处理的最后一个阶段就是关闭游标

DDL语句的处理(DDL Statement Processing)

DDL语句的执行不同与DML语句和查询语句的执行这是因为DDL语句执行成功后需要对数据字典数据进行修改对于DDL语句语句的分析阶段实际上包括分析查找数据字典信息和执行事务管理语句会话管理语句系统管理语句只有分析与执行阶段为了重新执行该语句会重新分析与执行该语句

事务控制(Control of Transactions)

一般来说只有使用ORACLE编程接口的应用设计人员才关心操作的类型并把相关的操作组织在一起形成一个事务一般来说我门必须定义事务这样在一个逻辑单元中的所有工作可以同时被提交或回滚保证了数据的一致性一个事务应该由逻辑单元中的所有必须部分组成不应该多一个也不应该少一个

· 在事务开始和结束的这段时间内所有被引用表中的数据都应该在一致的状态(或可以被回溯到一致的状态)

· 事务应该只包含可以对数据进行一致更改(one consistent change to the data)的SQL语句

例如在两个帐号之间的转帐(这是一个事务或逻辑工作单元)应该包含从一个帐号中借钱(由一个SQL完成)然后将借的钱存入另一个帐号(由另一个SQL完成)个操作作为一个逻辑单元应该同时成功或同时失败其它不相关的操作如向一个帐户中存钱不应该包含在这个转帐事务中

在设计应用时除了需要决定哪种类型的操作组成一个事务外还需要决定使用BEGIN_DISCRETE_TRANSACTIO存储过程是否对提高小的非分布式的事务的性能有作用

上一篇:设置sqlplus访问远程oracle数据库的方法

下一篇:在sqlplus中操作blob和clob