本文總結一下ORACLE數據庫中如何獲取SQL綁定變量值的方法,在SQL優化調優過程中,經常會用到這方面的知識點。在此梳理、總結一下這方面的知識點,方面日后查找、翻閱。
方法1:查詢V$SQL
V$SQL視圖中的BIND_DATA字段用來存儲綁定變量的值,但是從這個視圖查詢綁定變量的值,有很大的局限性:
它的記錄頻率受_cursor_bind_capture_interval隱含參數控制,默認值為900,表示每900秒記錄一次綁定值,也就是說在900內,綁定變量值的改變不會反應在這個視圖中。除非你調整隱含參數_cursor_bind_capture_interval
它記錄的僅僅最后一次捕獲的綁定變量值。
BIND_DATA數據類型為RAW,需要進行轉換。
可以使用下面兩種方式來查看綁定變量的值。
COL?SQL_ID?FOR?A14;
COL?SQL_TEXT?FOR?A32;
COL?HASH_VALUE?FOR?99999999999;
COL?BIND_DATA?FOR?A32;
SELECT?SQL_ID??????????
??????,SQL_TEXT
??????,LITERAL_HASH_VALUE
??????,HASH_VALUE
??????,DBMS_SQLTUNE.EXTRACT_BINDS(BIND_DATA)?BIND_DATA
FROM?V$SQL
WHERE?SQL_TEXT?LIKE?'SELECT?*?FROM?TEST%';
COL?SQL_ID?FOR?A14;
COL?SQL_TEXT?FOR?A32;
COL?HASH_VALUE?FOR?99999999999;
COL?BIND_DATA?FOR?A32;
SELECT?SQL_ID??????????
??????,SQL_TEXT
??????,LITERAL_HASH_VALUE
??????,HASH_VALUE
??????,DBMS_SQLTUNE.EXTRACT_BIND(BIND_DATA,1).VALUE_STRING?BIND_DATA
FROM?V$SQL
WHERE?SQL_TEXT?LIKE?'SELECT?*?FROM?TEST%';
如下實驗所示,我們在一個會話中使用綁定變量的查詢SQL語句,然后,我們來嘗試獲取綁定變量的值,如下所示:
SQL>?SHOW?USER;
USER?is?"TEST"
SQL>?DESC?TEST;
?Name??????????????????????????????????????Null?????Type
?-----------------------------------------?--------?----------------------------
?ID?????????????????????????????????????????????????NUMBER(10)
?NAME???????????????????????????????????????????????VARCHAR2(32)
SQL>?
SQL>?VARIABLE?NAME?NVARCHAR2(32);
SQL>?EXEC?:NAME?:='KKKK';
PL/SQL?procedure?successfully?completed.
SQL>?SELECT?*?FROM?TEST?WHERE?NAME=:NAME;
no?rows?selected
SQL>?
SQL>SHOW?USER;
USER?is?"SYS"
SQL>?COL?SQL_ID?FOR?A14;
SQL>?COL?SQL_TEXT?FOR?A32;
SQL>?COL?HASH_VALUE?FOR?99999999999;
SQL>?COL?BIND_DATA?FOR?A32;
SQL>?SELECT?SQL_ID??????????
??2????????,SQL_TEXT
??3????????,LITERAL_HASH_VALUE
??4????????,HASH_VALUE
??5????????,DBMS_SQLTUNE.EXTRACT_BINDS(BIND_DATA)?BIND_DATA
??6??FROM?V$SQL
??7??WHERE?SQL_TEXT?LIKE?'SELECT?*?FROM?TEST%';
SQL_ID?????????SQL_TEXT?????????????????????????LITERAL_HASH_VALUE???HASH_VALUE?BIND_DATA(NAME,?POSITION,?DUP_PO
--------------?--------------------------------?------------------?------------?--------------------------------
0r7m5jyz9ng09??SELECT?*?FROM?TEST?WHERE?NAME=:N??????????????????0???3197778953?SQL_BIND_SET(SQL_BIND(NULL,?1,?N
???????????????AME??????????????????????????????????????????????????????????????ULL,?1,?'NVARCHAR2(128)',?2000,
????????????????????????????????????????????????????????????????????????????????NULL,?NULL,?128,?'04-SEP-17',?'K
????????????????????????????????????????????????????????????????????????????????KKK',?ANYDATA()))
SQL>?COL?SQL_ID?FOR?A14;
SQL>?COL?SQL_TEXT?FOR?A32;
SQL>?COL?HASH_VALUE?FOR?99999999999;
SQL>?COL?BIND_DATA?FOR?A32;
SQL>?SELECT?SQL_ID??????????
??2????????,SQL_TEXT
??3????????,LITERAL_HASH_VALUE
??4????????,HASH_VALUE
??5????????,DBMS_SQLTUNE.EXTRACT_BIND(BIND_DATA,1).VALUE_STRING?BIND_DATA
??6??FROM?V$SQL
??7??WHERE?SQL_TEXT?LIKE?'SELECT?*?FROM?TEST%';
SQL_ID?????????SQL_TEXT?????????????????????????LITERAL_HASH_VALUE???HASH_VALUE?BIND_DATA
--------------?--------------------------------?------------------?------------?--------------------------------
0r7m5jyz9ng09??SELECT?*?FROM?TEST?WHERE?NAME=:N??????????????????0???3197778953?KKKK
如果此時你給變量NAME賦值為kerry,然后你使用上面SQL語句查詢,你會發現綁定變量的值依然為"KKKK",這個是因為綁定變量何時被捕獲是有一定規律的:
含有綁定變量的sql語句被硬解析時
當含有綁定變量的sql語句以軟解析或者軟軟解析方式重復執行時,該SQL語句中的綁定變量的具體輸入值也可能被ORACLE捕獲,只不過默認情況下這種捕獲操作 受隱含參數_cursor_bind_capture_interval影響,默認需要間隔15(900秒)分鐘才會做一次
SQL>?exec?:NAME?:='kerry';
PL/SQL?procedure?successfully?completed.
SQL>?/
????????ID?NAME
----------?--------------------------------
??????1000?kerry
SQL>?
方法2:查詢wrh$_sqlstat
V$SQL中有BIND_DATA字段,當SQL被解析時,就會放到BIND_DATA字段中,最終會被存入wrh$_sqlstat。關于wrh$_sqlstat的介紹如下所示:
wrh$_sqlstat?contains a history for SQL execution statistics and stores snapshots of v$sql view.
wrh$_sqlstat中存儲是v$sql的執行統計信息的快照的歷史記錄,那么從這里可以查詢到一些歷史綁定變量的值,但是也有可能v$sql的快照信息沒有被捕獲到(如滿足什么條件才會被捕獲呢?)。如下截圖所示
SQL>?select?dbms_sqltune.extract_bind(bind_data,?1).value_string
??2??from?wrh$_sqlstat
??3??where?sql_id='0r7m5jyz9ng09';
no?rows?selected
如上測試所示,這個獲取綁定變量值的方法有一定的缺陷性,有可能V$SQL快照信息沒有被捕獲到,導致wrh$_sqlstat 里面查不到對應的信息。
注意,如果有1個的綁定值,可以使用如下查詢
select?dbms_sqltune.extract_bind(bind_data,?1).value_string???
from?wrh$_sqlstat?
where?sql_id?=?'1t2r2p48w4p0g'
如果有2個綁定值,可以使用如下查詢
select?dbms_sqltune.extract_bind(bind_data,?1).value_string||
???????'--'||dbms_sqltune.extract_bind(bind_data,?2).value_string
from?wrh$_sqlstat
where?sql_id?=?'1t2r2p48w4p0g'
如果有多個綁定變量,使用類似下面SQL
select?dbms_sqltune.extract_bind(bind_data,?1).value_string
??||'-'||?dbms_sqltune.extract_bind(bind_data,?2).value_string?
??||'-'||?dbms_sqltune.extract_bind(bind_data,?3).value_string?
??||'-'||?dbms_sqltune.extract_bind(bind_data,?4).value_string?
??||'-'||?dbms_sqltune.extract_bind(bind_data,?5).value_string?
??||'-'||?dbms_sqltune.extract_bind(bind_data,?6).value_string
from?wrh$_sqlstat
where?sql_id?=?'1t2r2p48w4p0g'
/
方法3:v$sql_bind_capture
使用V$SQL_BIND_CAPTURE獲取綁定變量的值,也有一些限制:
如果STATISTICS_LEVEL設置成BASIC,那綁定變量的捕捉就會關閉(Bind capture is disabled when the?STATISTICS_LEVEL?initialization parameter is set to?BASIC.)
默認是900秒捕捉一次綁定變量值,由_cursor_bind_capture_interval參數控制。
V$SQL_BIND_CAPTURE視圖中記錄的綁定變量只對WHERE條件后面的綁定進行捕獲,這點需要使用的時候注意。對于DML操作,V$SQL_BIND_CAPTURE無法獲取綁定變量的值。
SQL>?COL?NAME?FOR?A12;
SQL>?COL?DATATYPE_STRING?FOR?A24;
SQL>?COL?VALUE_STRING?FOR?A32;
SQL>?SELECT?NAME,?
??2?????????DATATYPE_STRING,?
??3?????????VALUE_STRING,?
??4?????????MAX_LENGTH,?
??5?????????LAST_CAPTURED?
??6??FROM???V$SQL_BIND_CAPTURE?
??7??WHERE??SQL_ID?=?'1t2r2p48W4P0g';?
NAME?????????DATATYPE_STRING??????????VALUE_STRING?????????????????????MAX_LENGTH?LAST_CAPT
------------?------------------------?--------------------------------?----------?---------
:NAME????????NVARCHAR2(128)???????????KD????????????????????????????????????128?04-SEP-17
SQL>?
v$sql_bind_capture視圖,可以查看綁定變量,但是這個視圖不太給力,只能捕獲最后一次記錄的綁定變量值。而且兩次捕獲的間隔也是受隱含參數_cursor_bind_capture_interval控制。默認是900秒后才會重新開始捕獲。在900內,綁定變量值的改變不會反應在這個視圖中。這個跟v$sql獲取變量值是一樣的。
SQL>?EXEC?:NAME?:='kerry';
PL/SQL?procedure?successfully?completed.
SQL>/
????????ID?NAME
----------?--------------------------------
??????1000?kerry
SQL>?
如上所示,我給變量賦予新的值,然后重新執行一次,你執行上面SQL,發現綁定變量的值為kerry了,之前的值KD就無法找到了。所以這個也是這個視圖的一個局限性。(注意,實驗結果結果也有可能是KD,這個取決于實驗的時間間隔與隱含參數_cursor_bind_capture_interval的值有關系)
This view can be joined with?V$SQLAREA?on (HASH_VALUE,?ADDRESS) and with?V$SQL?on (HASH_VALUE,?CHILD_ADDRESS).
--
SET?PAUSE?ON
SET?PAUSE?'Press?Return?to?Continue'
SET?PAGESIZE?60
SET?LINESIZE?300
COLUMN?sql_text?FORMAT?A120
COLUMN?sql_id?FORMAT?A13
COLUMN?bind_name?FORMAT?A10
COLUMN?bind_value?FORMAT?A26
SELECT
??sql_id,
??t.sql_text?sql_text,??
??b.name?bind_name,
??b.value_string?bind_value
FROM
??v$sql?t
JOIN
??v$sql_bind_capture?b??using?(sql_id)
WHERE
??b.value_string?is?not?null
AND
??sql_id='&sqlid'
/
SELECT
??b.sql_id,
??t.sql_text?sql_text,??
??b.name?bind_name,
??b.value_string?bind_value
FROM
??v$sql?t
JOIN
??v$sql_bind_capture?b??on?t.hash_value?=?b.hash_value?and?t.child_address?=?b.child_address
WHERE
??b.value_string?is?not?null
AND
??b.sql_id='&sqlid'
/
方法4:查詢視圖DBA_HIST_SQLBIND.
DBA_HIST_SQLBIND是視圖V$SQL_BIND_CAPTURE歷史快照. 所以從視圖DBA_HIST_SQLBIND能查到多個綁定變量的值。但是這里依然會遇到一個問題,就是有可能歷史快照沒有被捕獲到DBA_HIST_SQLBIND下。如下測試所示:
SELECT?SNAP_ID,?
???????NAME,?
???????POSITION,?
???????VALUE_STRING,?
???????LAST_CAPTURED,?
???????WAS_CAPTURED?
FROM???DBA_HIST_SQLBIND?
WHERE??SQL_ID?=?'&SQL_ID'?
???????AND?SNAP_ID?=?&SNAP_ID;?
SELECT?SNAP_ID,?
???????NAME,?
???????POSITION,?
???????VALUE_STRING,?
???????LAST_CAPTURED,?
???????WAS_CAPTURED?
FROM???DBA_HIST_SQLBIND?
WHERE??SQL_ID?=?'&SQL_ID';?
SQL>?SELECT?SNAP_ID,?
??2?????????NAME,?
??3?????????POSITION,?
??4?????????VALUE_STRING,?
??5?????????LAST_CAPTURED,?
??6?????????WAS_CAPTURED?
??7??FROM???DBA_HIST_SQLBIND?
??8??WHERE??SQL_ID?=?'&SQL_ID';
Enter?value?for?sql_id:?1t2r2p48w4p0g
old???8:?WHERE??SQL_ID?=?'&SQL_ID'
new???8:?WHERE??SQL_ID?=?'1t2r2p48w4p0g'
no?rows?selected
SQL>?exec?dbms_workload_repository.create_snapshot();
PL/SQL?procedure?successfully?completed.
SQL>?SELECT?SNAP_ID,?
??2?????????NAME,?
??3?????????POSITION,?
??4?????????VALUE_STRING,?
??5?????????LAST_CAPTURED,?
??6?????????WAS_CAPTURED?
FROM???DBA_HIST_SQLBIND?
??7????8??WHERE??SQL_ID?=?'&SQL_ID';
Enter?value?for?sql_id:?1t2r2p48w4p0g
old???8:?WHERE??SQL_ID?=?'&SQL_ID'
new???8:?WHERE??SQL_ID?=?'1t2r2p48w4p0g'
no?rows?selected
SQL>?

方法5:dbms_xplan.display_cursor
sql_id:? ? ? ? ? ? 指定位于庫緩存執行計劃中SQL語句的父游標。默認值為null。當使用默認值時當前會話的最后一條SQL語句的執行計劃將被返回.可以通過查詢V$SQL 或V$SQLAREA的SQL_ID列來獲得SQL語句的SQL_ID。
child_number: ? ? ? ? ?指定父游標下子游標的序號。即指定被返回執行計劃的SQL語句的子游標。默認值為0。如果為null,則sql_id所指父游標下所有子游標的執行計劃都將被返回。
format:? ? ? ? ? ? ? 控制SQL語句執行計劃的輸出部分,即哪些可以顯示哪些不顯示。
select?*?from?table(dbms_xplan.display_cursor('1t2r2p48w4p0g',?0,?'ADVANCED'));
-------------------------------------
SELECT?*?FROM?TEST?WHERE?NAME=:NAME
?
Plan?hash?value:?1357081020
?
--------------------------------------------------------------------------
|?Id??|?Operation?????????|?Name?|?Rows??|?Bytes?|?Cost?(%CPU)|?Time?????|
--------------------------------------------------------------------------
|???0?|?SELECT?STATEMENT??|??????|???????|???????|?????3?(100)|??????????|
|*??1?|?TABLE?ACCESS?FULL?|?TEST?|?????1?|????31?|?????3???(0)|?00:00:01?|
--------------------------------------------------------------------------
?
Query?Block?Name?/?Object?Alias?(identified?by?operation?id):
-------------------------------------------------------------
?
???1?-?SEL$1?/?TEST@SEL$1
?
Outline?Data
-------------
?
??/*+
??????BEGIN_OUTLINE_DATA
??????IGNORE_OPTIM_EMBEDDED_HINTS
??????OPTIMIZER_FEATURES_ENABLE('11.2.0.1')
??????DB_VERSION('11.2.0.1')
??????ALL_ROWS
??????OUTLINE_LEAF(@"SEL$1")
??????FULL(@"SEL$1"?"TEST"@"SEL$1")
??????END_OUTLINE_DATA
??*/
?
Peeked?Binds?(identified?by?position):
--------------------------------------
?
???1?-?:NAME?(VARCHAR2(30),?CSID=873):?'KKK'
?
Predicate?Information?(identified?by?operation?id):
---------------------------------------------------
?
???1?-?filter("NAME"=:NAME)
?
Column?Projection?Information?(identified?by?operation?id):
-----------------------------------------------------------
?
???1?-?"TEST"."ID"[NUMBER,22],?"NAME"[VARCHAR2,32]
方法6:10046事件捕獲綁定變量
alter?session?set?events?'10046?trace?name?context?forever,?level?4';?--level=4 表示啟用SQL_TRACE并捕捉跟蹤文件中的綁定變量。
實驗在此略過,其實ORACLE中"seq$"表更新頻繁的分析案例中已經展示如何使用10046事件捕獲綁定變量的值。另外 v$sql,v$sql_bind_capture、dba_hist_sqlbind只能捕獲查詢SQL(確切的說,只對WHERE條件后面的綁定變量進行捕獲)的綁定變量,但10046也能捕獲DML的SQL的值
最后如果需要可以通過alter system set "_cursor_bind_capture_interval"=10; 修改綁定變量捕獲的時間間隔。