8

当我们在 Oracle 中执行任何 sql 语句时,都会为该 sql 语句分配一个哈希值并存储到库缓存中。因此,稍后,如果另一个用户请求相同的查询,那么 Oracle 会找到哈希值并执行相同的执行计划。但是,我对哈希值有一个疑问。我的意思是,如何生成哈希值?,我的意思是,Oracle 服务器是否使用一些算法或者他们只是将 sql 字符串转换为一些数值。

因为,我正在阅读Pro Oracle SQL书,上面写着,

select * from employees where department_id = 60;

SELECT * FROM EMPLOYEES WHERE DEPARTMENT_ID = 60;

select /* a_comment */ * from employees where department_id = 60;

会返回不同的哈希值,因为执行sql语句时,Oracle首先将字符串转换为哈希值。但是,当我尝试这个时,它返回相同的哈希值。

SQL> select * from boats where bid=10;

no rows selected


Execution Plan
----------------------------------------------------------
Plan hash value: 2799518614

-------------------------------------------------------------------------------------
| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |       |     1 |    16 |     1   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| BOATS |     1 |    16 |     1   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN         | B_PK  |     1 |       |     0   (0)| 00:00:01 |
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("BID"=10)

SQL> SELECT * FROM BOATS WHERE BID=10;

no rows selected


Execution Plan
----------------------------------------------------------
Plan hash value: 2799518614

-------------------------------------------------------------------------------------
| Id  | Operation                   | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT            |       |     1 |    16 |     1   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID| BOATS |     1 |    16 |     1   (0)| 00:00:01 |
|*  2 |   INDEX UNIQUE SCAN         | B_PK  |     1 |       |     0   (0)| 00:00:01 |
-------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - access("BID"=10)
4

3 回答 3

10

在您的问题文本中,您似乎在描述sql_id和/或hash_value. 这是 SQL 语句文本的散列值,是 Oracle 用来确定特定 SQL 语句是否已存在于共享池中的内容。但是,您在示例中显示的plan_hash_value是为 SQL 语句生成的计划的哈希值。两者之间可能存在多对多的关系。一条 SQL 语句 ( sql_id/ hash_value) 可以有多个不同的计划 ( plan_hash_value),并且多个不同的 SQL 语句可以共享同一个计划。

因此,例如,如果我编写两个不同的 SQL 语句从EMP表中查询特定行,我将得到相同的plan_hash_value.

SQL> set autotrace traceonly;
SQL> select * from emp where ename = 'BOB';

no rows selected


Execution Plan
----------------------------------------------------------
Plan hash value: 3956160932

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |    39 |     3   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| EMP  |     1 |    39 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("ENAME"='BOB')


SQL> ed
Wrote file afiedt.buf

  1* select * FROM emp WHERE ename = 'BOB'
SQL> /

no rows selected


Execution Plan
----------------------------------------------------------
Plan hash value: 3956160932

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |    39 |     3   (0)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL| EMP  |     1 |    39 |     3   (0)| 00:00:01 |
--------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("ENAME"='BOB')

但是,如果我查看v$sql,我会看到生成了两个不同sql_idhash_value

SQL> set autotrace off;
SQL> ed
Wrote file afiedt.buf

  1  select sql_id, sql_text, hash_value, plan_hash_value
  2    from v$sql
  3   where sql_text like 'select%BOB%'
  4*    and length(sql_text) < 50
SQL> /

SQL_ID        SQL_TEXT                                 HASH_VALUE PLAN_HASH_VALUE
------------- ---------------------------------------- ---------- ---------------
161v96c0v9c0n select * FROM emp WHERE ename = 'BOB'      28618772      3956160932
cvs1krtgzfr78 select * from emp where ename = 'BOB'    1610046696      3956160932

Oracle 认识到这两个语句是具有不同sql_idhash_value散列的不同查询。但是他们都碰巧产生了相同的计划,所以他们最终得到了相同的plan_hash_value.

于 2013-04-15T09:51:48.563 回答
1

我会说你只是证明了这本书在这种情况下是错误的。从理论上讲,让散列标识概念 SQL 语句而不是随机大写的字符串似乎更好……我希望在生成散列时也忽略注释。;-)

于 2013-04-15T09:45:15.653 回答
0

设置第 300 行 col BEGIN_INTERVAL_TIME for a30 select a.snap_id, a.begin_interval_time, b.plan_hash_value from dba_hist_snapshot a, dba_hist_sqlstat b where a.snap_id=b.snap_id and b.sql_id='&sql_id' order by 1;

于 2015-10-15T01:42:23.467 回答