SQL 简介
SQL (发音为 sequel) 是基于集合的高级别声明性计算机语言, 所有程序和用户都使用它来访问 Oracle 数据库中的数据。
虽然某些 Oracle 工具和应用程序掩盖了对 SQL 的使用, 但实际上所有的数据库操作都是使用 SQL 执行的。任何其他数据访问方法,绕过了内置于数据库的安全机制,并可能危及数据安全和完整性。
SQL 提供一个对关系数据库如 Oracle 数据库的接口。 SQL 以一种一致的语言统一了以下任务:
- 创建、替换、改变、和删除对象
- 插入、 更新、 和删除表行
- 查询数据
- 控制对数据库及其对象的访问
- 保证数据库的一致性和完整性
SQL 可以交互地使用,即将语句手动地输入到程序。SQL 语句也可以嵌入到用不同的语言(如 C 或 Java) 编写的程序内。
SQL 数据访问
有两大计算机语言派别:非过程化的声明性语言,它描述应该做什么,和过程化语言,如 C + + 和 Java,它描述如何做。
SQL 是声明性的,即是说用户可以指定他们想要的结果,而不必说明如何得到它。例如,下面的语句查询姓氏以 K 开头的雇员记录:
数据库的工作是,生成一个过程,来操作数据和检索请求的结果。SQL的声明自然使您能够在逻辑层面使用数据。仅当您想要处理该数据时,才需要关注其实现细节。
SELECT last_name, first_name
FROM hr.employees
WHERE last_name LIKE 'K%'
ORDER BY last_name, first_name;
数据库用一个单一步骤检索满足 WHERE 条件(也称“谓词”) 的所有行。这些行可以作为一个整体传递给用户、另一个 SQL 语句、或应用程序。您不需要一行一行地处理,您也不需要知道行是如何被物理存储和检索的。
所有 SQL 语句都使用优化器,作为 Oracle 数据库的一个组成,它会确定访问指定数据的最有效方式。Oracle 数据库还支持其它多种技术,可以使优化器更好地执行其工作。
SQL 标准
Oracle 致力于遵循行业认可的标准,并积极参与到 SQL 标准委员会。
被行业认可的委员会包括美国国家标准协会 (ANSI) 和国际组织的标准化(ISO)。 ANSI 和 ISO/IEC 都已接纳 SQL 作为关系数据库的标准语言。
SQL标准由十个部分组成。一部分(SQL/RPR:2012)在2102中是新的。其他五个部分在2011年进行了修订。对于其他四个部分,2008年的版本仍然适用。
Oracle SQL 包含很多针对 ANSI/ISO 标准 SQL 语言的扩展, 而且 Oracle数据库工具和应用程序提供了一些其他语句。工具 SQL * Plus、SQL Developer、和 Oracle 企业管理器使您能够在 Oracle 数据库上运行任何 ANSI/ISO 标准 SQL 语句,及可用于这些工具的其他语句或函数。
SQL 语句概述
在 Oracle 数据库中的信息上执行的所有操作都是使用 SQL 语句来执行的。SQL 语句是一种计算机程序或指令,它包含标识符、参数、变量、名称、数据类型、和 SQL 的保留字。
SQL 语句必须等价于一个完整的 SQL 句子,例如:
SELECT last_name, department_id FROM employees
Oracle 数据库仅运行完整的 SQL 语句。像下面这样的片段将生成一个错误,指示需要更多的文本:
SELECT last_name;
数据定义语言 (DDL) 语句
数据定义语言 (DDL) 语句定义、更改、和删除模式对象。
DDL 使您能够更改对象的属性, 而无需更改访问该对象的应用程序。例如,可以将某个列添加到由人力资源应用程序访问的一个表,而无需重写应用程序。您还可以在当数据库用户正在数据库中执行工作时,使用 DDL 更改对象的结构。
更具体地说,DDL 语句使您能够:
- 创建、更改、和删除模式对象和其他数据库结构,包括数据库本身和数据库用户。大部分的 DDL 语句以关键字 CREATE、ALTER、或 DROP 开头。
- 删除模式对象中的所有数据,而不删除这些对象的结构(TRUNCATE) 。
- 授予和撤消权限和角色 (GRANT, REVOKE) 。
- 打开和关闭审核选项 (AUDIT , NOAUDIT) 。
- 将注释添加到数据字典 (COMMENT)。
例 7-1 DDL 语句
如下例子使用 DDL 语句来创建 plants 表,然后使用 DML 在该表中插入两行。然后,该示例使用 DDL 更改表结构、授予和撤消一个用户在该表上的权限,然后删除表。
CREATE TABLE plants
( plant_id NUMBER PRIMARY KEY,
common_name VARCHAR2(15) );
INSERT INTO plants VALUES (1, 'African Violet'); # DML statement
INSERT INTO plants VALUES (2, 'Amaryllis'); # DML statement
ALTER TABLE plants ADD
( latin_name VARCHAR2(40) );
GRANT READ ON plants TO scott;
REVOKE READ ON plants FROM scott;
DROP TABLE plants;
在数据库执行 DDL 语句之前会立即执行一个隐式提交,之后立即执行一个提交或回滚。在上例中,两个 INSERT 语句后面跟了一个 ALTER TABLE 语句, 因此数据库会提交这两个 INSERT 语句。如果 ALTER TABLE 语句成功, 则数据库提交此语句 ;否则,数据库回滚此语句。在任一情况下,两个 INSERT 语句都是已经提交的。
数据操作语言 (DML)
数据操纵语言 (DML) 语句查询或操作现有模式对象中的数据。
DDL 语句使您可以更改数据库的结构,而 DML 语句使您能够查询或更改其内容。例如,ALTER TABLE 更改一个的表的结构,而 INSERT 向表中添加一个或多个行。
DML 语句是最经常使用的 SQL 语句,并使您能够:
- 从一个或多个表或视图,检索或获取数据(SELECT) 。
- 通过指定一个列值列表,或使用一个子查询,来选择和操作现有数据,以将新的数据行添加到表中(INSERT)。
- 更改表或视图中的现有行中的值(UPDATE)。
- 更新或有条件地将行插入到表或视图 (MERGE)。
- 从表或视图中删除行(DELETE)。
- 查看 SQL 语句的执行计划(EXPLAIN PLAN) 。
- 锁定一个表或视图,临时限制其他用户的访问(LOCK TABLE) 。
下面的示例使用 DML 来查询 employees 表。该示例使用 DML 往 employees 表中插入一行、又更新此行、然后将其删除:
SELECT * FROM employees;
INSERT INTO employees (employee_id, last_name, email, job_id, hire_date, salary)
VALUES (1234, 'Mascis', 'JMASCIS', 'IT_PROG', '14-FEB-2008', 9000);
UPDATE employees SET salary=9100 WHERE employee_id=1234;
DELETE FROM employees WHERE employee_id=1234;
形成一个逻辑工作单元的 DML 语句的集合,称为一个事务。 例如,一个转账事务可能涉及三个独立的操作: 减少储蓄帐户余额、 增加支票帐户余额、 和在一个帐户历史记录表中记录转账日志。与 DDL 的语句不同的是,DML 语句不会隐式提交当前事务。
SELECT 语句
查询是一个从表或视图中检索数据的操作。
SELECT 是你唯一可用于查询数据的 SQL 语句。从执行 SELECT 语句检索到的数据集被称为结果集。
下表显示了在一个 SELECT 语句中两个必需的关键字,和两个常见的关键字。该表也说明了这些关键字在 SELECT 语句中的功能。
| 关键字 | 必需 ? | 描述 | 功能 |
|---|---|---|---|
| SELECT | 是 | 指定应在结果中显示哪些列。投影会生成表中所包含列的一个子集。 表达式是一个或多个值、操作符、和 SQL 函数的组合,经过计算后最终得到一个值。出现在 SELECT 关键字之后,和在 FROM 子句之前的表达式列表,叫做选择列表。 |
映射 |
| FROM | 是 | 指定应从哪些表或视图中检索数据。 | 联接 |
| WHERE | 否 | 指定一个条件来筛选行,以生成表中的行的一个子集。条件指定一个或多个表达式和逻辑(布尔)运算符的组合,并返回一个值(TRUE、FALSE、或 UNKNOWN) 。 | 选择 |
| ORDER BY | 否 | 指定行应以何种顺序显示。 |
连接
连接是合并两个或多个表、 视图、 或物化视图中的行的查询。
如下示例将 employees 表与 departments 表联接(FROM 子句),只选择满足指定条件的行(WHERE 子句),并使用投影从两个列中检索数据(SELECT)。该 SQL 语句的示例输出如下所示。
SELECT email, department_name
FROM employees
JOIN departments
ON employees.department_id = departments.department_id
WHERE employee_id IN (100,103)
ORDER BY email;
EMAIL DEPARTMENT_NAME
------------------------- ------------------------------
AHUNOLD IT
SKING Executive
下图以图形方式显示了上面查询中所示连接中的投影和选择操作。

大多数连接都至少有一个连接条件,要么在 FROM 子句中,要么在 WHERE 子句中,比较来自不同表的两个列。数据库合并行对, 每个行对包含来自不同表、 且连接条件计算结果为 TRUE 的一行。 基于连接条件、 索引、 和任何可用的表统计信息,由优化器确定数据库中联接表的顺序。
联接类型包括:
-
内连接
内连接是两个或更多表的联接, 只返回满足连接条件的行。例如,如果连接条件是 employees.department_id = departments.department_id ,则不满足此条件的行不被返回。 -
外连接
外连接返回所有满足联接条件的行,也返回一个表中不满足与另一个表的连接条件的行。左外连接的结果对于表 A 和 B 总是包含了表的所有记录,即使联接条件不匹配记录在正确的表B。如果从B匹配的行不存在,那么B列包含null行没有匹配的B。例如,如果不是所有的员工都在部门,然后,员工(左表)和部门(右表)的左外连接检索员工中的所有行,即使部门中没有行满足连接条件(employees.department_id 为 null)。
右外连接的结果表 A 和 B 包含右表的所有记录,即使联接条件与左表 A 中没有匹配的行,如果不存在来自A的匹配行,那么A列包含A中不匹配的行。例如,如果不是所有部门都有员工,员工(左表)和部门(右表)的右外连接检索部门中的所有行,即使员工中没有行满足连接条件。
完全外连接是左外连接和右外连接的组合。
-
笛卡儿积
如果两个表联接查询中的没有联接条件,则数据库返回其笛卡儿积。一个表中的每一行与另一个表中的每一行联合。例如,如果 employees 表有 107 行,而 departments 表有 27 行,则其笛卡儿积包含 107 x 27 行。笛卡儿积是很少用到的。
子查询
子查询是嵌套在另一个 SQL 语句中的 SELECT 语句。 在您必须执行多个查询以解决一个问题时,子查询非常有用。
一个语句每个查询部分称为一个查询块。在如下查询中,括号内的子查询是内部查询块:
SELECT first_name, last_name
FROM employees
WHERE department_id
IN ( SELECT department_id
FROM departments
WHERE location_id = 1800 );
内部的 SELECT 语句检索位置 ID 为 1800 的部门 id。这些部门 id 需要在外部查询块中用到,外部查询基于子查询提供的部门 ID 来检索员工姓名。
SQL 语句的结构不会强制数据库必须首先执行内部查询。 例如,数据库可能会重写整个查询,将 employees 表和 departments 表连接,这样, 子查询本身永远不会单独执行。 再举一个例子, 虚拟专用数据库 (VPD)功能可能会使用一个 WHERE 子句来限制对员工的查询, 这样, 数据库可能决定首先查询雇员表,然后获取部门 id。 由优化器确定用于检索请求行的最佳步骤序列。
事务控制语句
事务控制语句管理 DML 语句所做的更改, 和将多个 DML 语句按事务分组。
这些语句使您能够:
- 使一个事务的更改持久化(COMMIT)。
- 撤消在一个事务中自事务开始以来的更改(ROLLBACK) ,或自一个保存点以来的更改 (ROLLBACK TO SAVEPOINT)。 保存点是在一个事务上下文中由用户声明的中间标记。
- 设置一个你可以回滚到的点(SAVEPOINT)。
- 设定一个事务的属性(SET TRANSACTION) 。
- 指定是在每个 DML 语句之后、 还是在事务时提交后,执行可延迟完整性约束检查(SET CONSTRAINT)。
下面的示例启动一个名为 Update salaries 的事务。该示例创建一个保存点,更新一个雇员的工资,然后回滚事务到保存点。该示例又更新一个不同的薪金值,然后提交。
SET TRANSACTION NAME 'Update salaries';
SAVEPOINT before_salary_update;
UPDATE employees SET salary=9100 WHERE employee_id=1234 # DML
ROLLBACK TO SAVEPOINT before_salary_update;
UPDATE employees SET salary=9200 WHERE employee_id=1234 # DML
COMMIT COMMENT 'Updated salaries';
会话控制语句
会话控制语句动态地管理用户会话的属性。
会话是数据库实例内存中的一个逻辑实体, 表示登录到数据库中的一个当前用户的状态。 会话从该用户通过数据库验证开始, 一直持续到用户断开连接或退出数据库应用程序。
会话控制语句使您能够:
- 通过执行一项专门的功能来改变当前会话,如启用和禁用 SQL 跟踪(ALTER SESSION)。
- 启用和禁用角色,它是一组用于当前会话的权限(SET ROLE) 。
下面的语句将会话的默认日期格式动态更改为 ‘YYYY MM DD-HH24:MI:SS’:
ALTER SESSION
SET NLS_DATE_FORMAT = 'YYYY MM DD HH24:MI:SS';
会话控制语句不会隐式提交当前事务。
系统控制语句
系统控制语句更改数据库实例的属性。
唯一的系统控制语句是 ALTER SYSTEM。它使您能够更改系统设置,例如,共享服务器的最小数目、终止一个会话、和执行其他系统级任务。
系统控制声明的例子包括:
ALTER SYSTEM SWITCH LOGFILE;
ALTER SYSTEM KILL SESSION '39, 23';
ALTER SYSTEM 语句不会隐式提交当前事务。
嵌入式的 SQL 语句
嵌入式的 SQL 语句将 DDL、 DML 、 和事务控制语句混入过程化语言程序中。
他们和 Oracle 预编译器一起使用。嵌入式的 SQL 是一种在您的过程化语言应用程序中纳入 SQL 的方法。另一种方法是使用一个程序 API,如开放式数据库连接 (ODBC)或 Java 数据库连接 (JDBC) 。
嵌入式的 SQL 语句,使您可以:
- 定义、 分配、和释放游标(DECLARE CURSOR、OPEN、CLOSE)。
- 指定一个数据库, 并连接到该数据库(DECLARE DATABASE、CONNECT)。
- 分配变量名称 (DECLARE STATEMENT)。
- 初始化描述符(DESCRIB)。
- 指定如何处理错误和警告(WHENEVER)。
- 分析并运行 SQL 语句(PREPARE, EXECUTE, EXECUTE IMMEDIATE)。
- 从数据库中检索数据(FETCH) 。
优化器概述
若要理解数据库如何处理 SQL 语句, 有必要了解一下数据库中被称为优化器的部件(也称为查询优化器或基于成本的优化器)。所有 SQL 语句都使用优化器来确定访问指定的数据的最有效手段。
使用优化器
优化器生成执行计划来描述可能的执行方法。
优化器通过考虑几个信息来源来确定哪种执行计划是最有效的。例如,优化器考虑查询条件、可用的访问路径、 为系统收集的统计信息、 以及提示等。
要执行一个 DML 语句,数据库可能需要执行许多步骤。每一步或者是从数据库中物理地检索数据行,或者是在为发出语句的用户准备数据,等等。数据库用来执行一条语句的步骤, 很大程度上会影响该语句的运行速度有多快。许多不同的处理 DML 语句的方式通常都是可能的。例如, 访问表或索引的顺序可能会不同。
在确定SQL语句的最佳执行计划时,优化器将执行以下操作:
- 表达式和条件评估
- 检查完整性约束, 以了解数据和基于此元数据的优化的更多信息
- 语句转换
- 优化器目标选择
- 访问路径选择
- 连接顺序选择
优化器生成处理一个查询的几乎所有可能的方法,并给生成的执行计划中的每个步骤分配一个成本。具有最低成本的计划被选择为要执行的查询计划。
你可以通过设置优化器目标,并为优化程序收集有代表性的统计数据,来影响优化器的选择。例如,您可以设置优化器目标为以下之一:
- 总吞吐量
ALL_ROWS 提示指示优化器尽可能快地将所有结果数据返回给客户端应用程序。 - 初始响应时间
FIRST_ROWS 提示指示优化器尽可能快地获取第一行数据给客户端。
典型的交互式终端用户应用程序将受益于初始响应时间优化,而非交互式批处理模式应用程序将受益于总吞吐量的优化。
优化器组件
优化器包含三个主要组件:transformer、estimator 和 plan generator。
下图描述了组件:

优化器的输入是一个已解析的查询。优化器将执行以下操作:
- 优化器接收已解析的查询,并基于可用的访问路径和提示,为 SQL 语句生成一组潜在的计划。
- 优化器基于数据字典中的统计信息,估计每个计划的成本。成本是一个与特定用于执行该语句的计划所需的预期资源使用成正比的估计值。
- 优化器比较各个计划的成本,并选择具有最低成本的计划(也叫查询计划),然后传递给行源生成器。
查询转换器(Query Transformer)
查询转换器确定更改查询的形式是否有助于优化器生成一个更好的执行计划。查询转换器的输入是一个由一组查询块表示的已分析的查询。
估算器(Estimator)
估算器确定一个给定的执行计划的总体成本。
估算器生成三种不同类型的测量值,以实现这一目标:
- 选择性
这项测量表示一个行集中的一小部分。受如 last_name 的选择性依赖于查询谓词(或谓词的组合) ,比如 last_name=‘Smith’。 - 基数
这项测量表示行集中的行数。 - 成本
这项测量表示工作量或使用的资源。查询优化器使用磁盘 I/O、CPU 使用率、和内存使用情况作为工作量。
如果统计数据可用,则估算器使用它们来计算这些测量值。统计信息可以提高测量的精确程度。
计划生成器(Plan Generator)
计划生成器对提交的查询尝试不同的计划,并选出具有最低成本的计划。
优化器为每个由单独查询块表示的嵌套子查询和未合并试图生成子计划。计划生成器通过尝试不同的访问路径、联接方法、和联接顺序,来为查询块探究各种计划。
自适应查询优化功能基于语句执行过程中收集的统计信息进行更改。所有自适应机制都可以为与默认计划不同的语句执行最终计划。适应性优化使用动态计划(在语句执行期间在子计划中进行选择)或重新优化(在当前执行之后更改执行计划)。
访问路径
访问路径是查询用来检索行的技术。
例如, 使用索引的查询与不使用索引的查询具有不同的访问路径。 通常, 索引访问路径对于只检索表行中的一个小的子集是最佳的。 而完全扫描则对访问表中的一大部分更有效。
数据库可以使用几个不同的访问路径从表中检索数据。以下是一个有代表性的列表:
- 全表扫描
这种类型的扫描从一个表读取所有行,并滤掉那些不符合选择条件的行。数据库顺序扫描段中的所有数据块,包括那些高水位标记以下的块, 高水位标记用来分隔已使用和未使用的空间 (请参阅“Segment Space and the High Water Mark”) - Rowid 扫描
行的 rowid 指定包含行的数据文件和数据块,以及行在该块中的位置。 数据库通过语句的 WHERE 子句或一个索引扫描,首先获取所选的行的 rowids,然后基于这些 rowid 查找每个选定的行。 - 索引扫描
此扫描搜索被 SQL 语句访问的索引列的值(请参见“Index Scans”)。如果该语句仅访问已被索引的列,则数据库直接从索引读取索引的列值。 - 簇扫描
簇扫描用来检索存储在一个索引化的表簇中的一个表中的数据,具有相同的簇键的所有行都存储在同一个数据块中 (请参见“Overview of Indexed Clusters”)。Oracle 数据库首先通过扫描簇索引来获取所选行的rowid。然后基于此 rowid 查找相应行。 - 哈希扫描
哈希扫描用于查找哈希群集中的行,其中具有相同哈希值的所有行都存储在同一个数据块中 (请参阅“Overview of Hash Clusters”)。Oracle 数据库首先通过将哈希函数应用于由该语句指定的簇键值,以获得哈希值。然后扫描包含具有此哈希值的行的数据块。
优化器对访问路径的选择,基于语句的所有可用的访问路径,和使用每个访问路径或其组合的估算成本。
优化器统计
优化器统计信息是描述有关数据库和数据库中的对象的详细信息的数据集合。统计信息提供数据存储和分布的正确描述,以被优化器用来评估访问路径。
优化器统计信息包括:
- 表统计
这包括行数、块数、和平均行长等。 - 列统计
这包括非重复值数目、空值数目、和数据的分布。 - 索引统计
这包括叶块数目和索引级别。 - 系统统计
这包括 CPU 和 I/O 的性能及利用率。
Oracle 数据库自动收集所有数据库对象的优化器统计信息,并作为一项自动维护任务来维护这些统计信息。您还可以使用 DBMS_STATS 包手动收集统计信息。该 PL/SQL 包可以修改、查看、导出、导入、和删除统计信息。
Optimizer Statistics Advisor 是一种内置的诊断软件,它可以分析您当前如何收集统计信息、现有统计信息收集作业的有效性以及收集的统计信息的质量。Optimizer Statistics Advisor 维护规则,这些规则基于当前的特性集体现了Oracle的最佳实践。通过这种方式,顾问总是为统计信息收集提供最新的建议。
优化器提示
提示是 SQL 语句中的注释,作为优化程序的一个指示。
有时应用程序的设计者比优化器更了解一个特定的应用程序的数据的详细信息,他可以选择一个运行 SQL 语句的更有效方法。应用程序设计者可以在 SQL 语句中使用提示,来指定该语句应该如何运行。下面的例子说明了提示的用法。
例 7-2 带有 FIRST_ROWS 提示的 SELECT 语句的执行计划
假设你的交互式应用程序运行一个查询,返回 50 行。此应用程序最初只读取查询的前 25 行来呈现给终端用户。 你想使优化器生成一个计划,尽可能快地获取首批 25 条记录, 以使用户不必被迫等待。您可以使用一个提示来将此指令传递给优化器,参阅如下示例中的 SELECT 语句和 AUTOTRACE 输出:
SELECT /*+ FIRST_ROWS(25) */ employee_id, department_id
FROM hr.employees
WHERE department_id > 50;
------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes
------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 26 | 182
| 1 | TABLE ACCESS BY INDEX ROWID | EMPLOYEES | 26 | 182
|* 2 | INDEX RANGE SCAN | EMP_DEPARTMENT_IX | |
------------------------------------------------------------------------
这个实例中,执行计划显示,优化器选择 employees.department_id 列上的一个索引,来查找其部门 ID 超过 50 的雇员中的前 25 行。优化器使用从索引中检索到的 rowid, 从雇员表中检索相应记录,并将其返回给客户端。第一条记录的检索通常几乎是在瞬间即可完成的。
例 7-3 无提示 SELECT 语句的执行计划
假设您执行相同的语句,但没有优化器提示:
SELECT employee_id, department_id
FROM hr.employees
WHERE department_id > 50;
------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cos
------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 50 | 350 |
|* 1 | VIEW | index$_join$_001 | 50 | 350 |
|* 2 | HASH JOIN | | | |
|* 3 | INDEX RANGE SCAN | EMP_DEPARTMENT_IX | 50 | 350 |
| 4 | INDEX FAST FULL SCAN| EMP_EMP_ID_PK | 50 | 350 |
在这种情况下,执行计划,将两个索引联接以尽可能快的返回请求的记录。 优化器并不像示例 7-2 那样多次在表和索引间倒腾,而是在 EMP_DEPARTMENT_IX 索引上使用范围扫描, 找出所有部门 ID 超过 50 的行, 并将这些行放在一个哈希表中。然后优化器读取 EMP_EMP_ID_PK 索引。对该索引中的每一行,它探测一次该哈希表,以查找相应的部门 id。
在这种情况下,数据库不能在完成对 EMP_DEPARTMENT_IX 索引的范围扫描之前向客户端返回第一行。因此,此生成的计划将需要更长的时间返回第一条记录。与示例 7-2 中按索引 rowid 访问表的计划不同,这个示例的计划使用多数据块 I/O, 导致大量读取操作。 这种读取使得整个结果集的最后一行会更快地返回。
SQL 处理的概述
本部分说明了数据库如何处理 SQL 语句。具体而言,本部分说明了数据库处理创建对象的 DDL 语句、修改数据的 DML 语句、和检索数据的查询语句等的处理方式。
SQL 处理的阶段
SQL 处理的一般阶段:解析、优化、产生行源、和执行。数据库可能会忽略某些步骤,这取决于具体的语句。
下图描述了一般阶段:

SQL 解析
SQL 处理的第一阶段是解析。这一阶段涉及将 SQL 语句的各个片断分离到一个可由其他例程处理的数据结构。
当应用程序发出 SQL 语句时,该应用程序向数据库发出一个解析调用,以准备执行该语句。解析调用会打开或创建一个游标, 它是一个对特定于会话的私有 SQL 区的句柄,其中包含了已分析的 SQL 语句和其他处理信息。 游标和 私有 SQL 区位于 PGA 中。
在解析调用期间, 数据库会执行以下检查:
- 语法检查
- 语义检查
- 共享池检查
前面的检查确定在语句执行之前可以发现的错误。一些错误不能通过解析来捕获。例如,数据库在数据转换过程中可能会遇到死锁或错误,但这仅在语句执行中才会发生。
SQL 优化
查询优化是选择执行 SQL 语句的最有效手段的过程。
数据库对查询的优化基于对正在访问的实际数据收集的统计信息。优化器使用行数、数据集大小、和其他因素,来生成各种可能的执行计划,并为每个计划分配一个成本数值。数据库会使用具有最低成本的计划。
数据库对每个唯一的 DML 语句必须至少执行一次硬解析,并在解析期间执行优化。 DDL 永远不会被优化,除非它包括需要优化的 DML 组件,如子查询。
SQL 行源生成
行源生成器是一种软件, 它从优化器接收经过优化的执行计划,并生成一个称为查询计划的迭代计划, 可供数据库的其余部分使用。
查询计划采用组合多个步骤的形式。每一步返回一个行集。 该集合中的行可以在下一步被使用,或在最后一步返回给发出 SQL 语句的应用程序。
行源是执行计划中的某一步骤所返回的行集,且带有能够迭代该行集的控制结构。
SQL 执行
在执行期间,SQL 引擎执行行源生成器所产生的树中的每个行源。这一步是在 DML 处理中唯一的强制性步骤。
在执行期间,如果数据不在内存中,则数据库将数据从磁盘读入内存。数据库还会取出确保数据完整性所需的所有锁和锁存,并记录SQL执行期间所做的任何更改。处理SQL语句的最后一个阶段是关闭游标。
如果将数据库配置为使用内存中的列存储(IM列存储),那么数据库将在可能的情况下透明地将查询过程发送到IM列存储,并将查询过程发送到磁盘和数据库缓冲区缓存。单个查询还可以使用IM列存储、磁盘和缓冲区缓存。例如,查询可能连接两个表,其中只有一个表缓存在IM列存储中。
DML 和 DDL 处理之间的区别
Oracle 数据库对 DDL 的处理不同于 DML。
例如,在创建表时,数据库并不会优化 CREATE TABLE 语句。相反,数据库只是解析该 DDL 语句并执行该命令。
与 DDL 不同,大多数 DML 语句都有查询组件。在查询中,游标的执行将查询生成的行放置到结果集中。
数据库可以一次获取一行或分组的结果集行。在 fetch 中,数据库选择行,如果查询请求,则对行进行排序。每次后续获取都将检索结果的另一行,直到获取最后一行为止。




