当我们在 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)
在您的问题文本中,您似乎正在描述
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_id
和 hash_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_id
和 hash_value
哈希值的不同查询。但他们碰巧生成了相同的计划,所以他们最终得到了相同的plan_hash_value
。
我想说,你刚刚证明了这本书在这种情况下是错误的。从理论上讲,让哈希来标识概念性 SQL 语句而不是随机大写的字符串似乎更好......并且我希望在生成哈希时注释也被忽略。 ;-)
set lines 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;