DML语句的处理
本节给出一个例子来说明在DML语句处理的各个阶段到底发生了什么事情。
假设你使用Pro*C程序来为指定部门的所有职员增加工资。程序已经连到正确的用户,你可以在你的程序中嵌入如下的SQL语句:
EXEC SQL UPDATE employees
SET salary = 1.10 * salary
WHERE department_id = :var_department_id;
var_department_id是程序变量,里面包含部门号,我们要修改该部门的职员的工资。当这个SQL语句执行时,使用该变量的值。
每种类型的语句都需要如下阶段:
第1步: Create a Cursor 创建游标
第2步: Parse the Statement 分析语句
第5步: Bind Any Variables 绑定变量
第7步: Run the Statement 运行语句
第9步: Close the Cursor 关闭游标
如果使用了并行功能,还会包含下面这个阶段:
第6步: Parallelize the Statement 并行执行语句
如果是查询语句,则需要以下几个额外的步骤,如图 3所示:
第3步: Describe Results of a Query 描述查询的结果集
第4步: Define Output of a Query 定义查询的输出数据
第8步: Fetch Rows of a Query 取查询出来的行
下面具体说一下每一步中都发生了什么事情:.
第1步: 创建游标(Create a Cursor)
由程序接口调用创建一个游标(cursor)。任何SQL语句都会创建它,特别在运行DML语句时,都是自动创建游标的,不需要开发人员干预。多数应用中,游标的创建是自动的。然而,在预编译程序(pro*c)中游标的创建,可能是隐含的,也可能显式的创建。在存储过程中也是这样的。
第2步:分析语句(Parse the Statement)
在语法分析期间,SQL语句从用户进程传送到Oracle,SQL语句经语法分析后,SQL语句本身与分析的信息都被装入到共享SQL区。在该阶段中,可以解决许多类型的错误。
语法分析分别执行下列操作:
翻译SQL语句,验证它是合法的语句,即书写正确
实现数据字典的查找,以验证是否符合表和列的定义
在所要求的对象上获取语法分析锁,使得在语句的语法分析过程中不改变这些对象的定义
验证为存取所涉及的模式对象所需的权限是否满足
决定此语句非常好的的执行计划
将它装入共享SQL区
对分布的语句来说,把语句的全部或部分路由到包含所涉及数据的远程节点
以上任何一步出现错误,都将导致语句报错,中止执行。
只有在共享池中不存在等价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语句处理描述、定义和取数据阶段
第3步: 描述查询结果(Describe Results of a Query)
描述阶段只有在查询结果的各个列是未知时才需要;例如,当查询由用户交互地输入需要输出的列名。在这种情况要用描述阶段来决定查询结果的特征(数据类型,长度和名字)。
第4步: 定义查询的输出数据(Define Output of a Query)
在查询的定义阶段,你指定与查询出的列值对应的接收变量的位置、大小和数据类型,这样我们通过接收变量就可以得到查询结果。如果必要的话,Oracle会自动实现数据类型的转换。这是将接收变量的类型与对应的列类型相比较决定的。 10