首頁 資料庫 mysql教程 Oracle11g新特性之动态变量窥视

Oracle11g新特性之动态变量窥视

Jun 07, 2016 pm 03:12 PM
動態 變數 新特性 特性

欢迎进入Oracle社区论坛,与200万技术人员互动交流 >>进入 1. 11g之前的绑定变量窥视 我们都知道,为了能够让SQL语句共享执行计划,oracle始终都是强调在进行应用系统的设计时,必须使用绑定变量,也就是用一个变量来代替原来出现在SQL语句里的字面值。比如

欢迎进入Oracle社区论坛,与200万技术人员互动交流 >>进入

        1. 11g之前的绑定变量窥视

        我们都知道,为了能够让SQL语句共享执行计划,oracle始终都是强调在进行应用系统的设计时,必须使用绑定变量,也就是用一个变量来代替原来出现在SQL语句里的字面值。比如,对于下面三条SQL语句来说:

         select col1 from t where col2 = 1;
        select col1 from t where col2 = 2;
        select col1 from t where col2 = 3;

        我们可以看到,这三条SQL语句几乎一样,只有最后where条件里的字面值(分别是1、2、3)不同而已。但是如果写成这个样子,则oracle是不知道这三条SQL语句是一样的,仍然把它们当作三条完全不同的SQL语句,从而在shared pool里进行硬解析,并生成最终的执行计划。但是我们会发现,这三个执行计划可能都是一样的,因此后面两次生成执行计划的工作可能是完全不必要的,这在典型的OLTP环境中更是如此。由于解析本身属于CPU密集型操作,因此为了降低对CPU的消耗,oracle建议将这样的SQL写成:

         select col1 from t where col2 = :v1;

        然后,分别将1、2、3传递给v1,这样的话,只需要第一次传入1时进行解析即可。而后面执行2、3时,由于SQL文本本身没有变化,因此直接把执行计划拿来使用即可,不需要再次生成执行计划。

        但是,生成执行计划本身是基于概率的理论,在不访问具体表里的数据的前提下,根据你的where条件,来猜测返回的记录数大概是多少,从而判断应该采用怎样的访问路径。很明显,这是一定要参照具体的where条件里的值才能进行猜测的。这样就与节省CPU的初衷产生了矛盾,因为节省CPU的关键是使用绑定变量,你一旦使用了绑定变量,则oracle岂不是不知道你具体的字面值了吗?

        为了解决这一问题,oracle引入了绑定变量窥视。所谓绑定变量窥视,就是指oracle在第一次解析SQL语句的时候(也就是说该SQL第一次传入shared pool),会将你输入的绑定变量的值带入SQL语句里,从而参考你的字面值来猜测该SQL大概会返回多少条记录,从而得到优化的执行计划。然后,以后再次执行相同的SQL语句时,不再考虑你所输入的绑定变量的值,直接取出第一次生成的绑定变量。

        但是,很可惜的是,使用绑定变量从而共享游标与SQL优化是两个矛盾的目标。Oracle使用绑定变量的前提,是oracle认为大部分的列的数据都是分布比较均匀的。从而,使用第一次的绑定变量的值所得到的执行计划,大多数情况下都能适用于该绑定变量的其他的值。很明显,如果第一次传入的绑定变量的值恰好占整个数据量的百分比较高,从而导致全表扫描的执行计划。而后来传入的绑定变量的值都占整个数据量的百分比都很低,则应该走索引扫描会更好的,但是由于使用了绑定变量,从而oracle并不会再去看你的绑定变量的值,而是直接拿全表扫描的执行计划来用。这时,由于使用了绑定变量,虽然我们达到了游标共享,从而节省CPU的目的,但是SQL的执行计划却不够优化了。

        那么我们如何在绑定变量和SQL优化之间进行取舍呢?在OLTP应用中,由于并发性较高,CPU上的争用会比较严重,同时SQL本身执行时间较短,涉及到的数据量较少,解析所占的时间在整个SQL执行时间中占的比例较高,而花在I/O上的时间占的比例较低。因此尽管绑定变量会有SQL不够优化的问题,还是建议使用绑定变量。但是在DSS应用和数据仓库应用中,由于并发性较低,CPU上的争用较轻,同时SQL语句的执行时间都很长,而且主要时间花在等待I/O上,而解析占的比重较低,这时优化SQL执行计划的重要性就体现出来了。因此,建议不要使用绑定变量,而直接使用字面值。但是大多数的情况都是混合应用,既有OLTP又有数据仓库,这时就很难完美的解决该问题了。

        我们先来看一下11g之前的绑定变量窥视是如何工作的,以10g为例。

        我们先创建一个表,使得其含有的数据分布不均匀,并在该表上创建一个索引。

         hr@ora10g > create table t1 as select object_id as id,object_name from dba_objects;
        hr@ora10g > update t1 set id=1 where rownum        hr@ora10g > commit;
        hr@ora10g > create index idx_t1 on t1(id);

        这样,该表里id为的1记录有一万条,而id为其他值的记录都只有一条。从而,我们构建出一个分布不均匀的测试用表。然后,我们收集一下统计信息。注意,这里要收集直方图,为的是要让CBO知道id列上的数据分布不均匀。

         hr@ora10g> begin
        2 dbms_stats.gather_table_stats(
        3 user,
        4 't1',
        5 cascade => true,
        6 method_opt => 'for columns id size 254'
        7 );
        8 end;
        9 /

        我们找到表t1里最大的id,然后以该id作为第一个绑定变量传入,可以想象,该绑定变量将导致走索引。注意,我们这里设定的优化器目标为all_rows。

         hr@ora11g > select max(id) from t1;
        MAX(ID)
        ----------
        13871
        hr@ora10g> alter system flush shared_pool;
        hr@ora10g> var v_id number;
        hr@ora10g> var v_sql_id varchar2(20);
        hr@ora10g> exec :v_id := 13871;
        hr@ora10g> select * from t1 where id=:v_id;
        此处省略查询结果
        hr@ora10g > begin
        2 select sql_id into :v_sql_id from v$sql
        3 where sql_text like 'select * from t1 where id=:v_id%';
        4 end;
        5 /
        hr@ora10g > select * from table(dbms_xplan.display_cursor(:v_sql_id));
        PLAN_TABLE_OUTPUT
        --------------------------------------------------------------------------------
        SQL_ID djwq30cpbcz7k, child number 0
        -------------------------------------
        select * from t1 where id=:v_id
        Plan hash value: 50753647
        --------------------------------------------------------------------------------
        | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time
        --------------------------------------------------------------------------------
        | 0 | SELECT STATEMENT | | | | 11 (100) |
        | 1 | TABLE ACCESS BY INDEX ROWID| T1 | 1365 | 28665| 11 (0) | 00:00:01
        |* 2 | INDEX RANGE SCAN | IDX_T1 | 1365 | | 3 (0) | 00:00:01
        --------------------------------------------------------------------------------
        ......
        hr@ora10g> exec :v_id := 1;
        hr@ora10g> select * from t1 where id=:v_id;
        此处省略查询结果
        hr@ora10g > begin
        2 select sql_id into :v_sql_id from v$sql
        3 where sql_text like 'select * from t1 where id=:v_id%';
        4 end;
        5 /
        hr@ora10g > select * from table(dbms_xplan.display_cursor(:v_sql_id));
        PLAN_TABLE_OUTPUT
        --------------------------------------------------------------------------------
        SQL_ID djwq30cpbcz7k, child number 0
        -------------------------------------
        select * from t1 where id=:v_id
        Plan hash value: 50753647
        --------------------------------------------------------------------------------
        | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time
        --------------------------------------------------------------------------------
        | 0 | SELECT STATEMENT | | | | 11 (100) |
        | 1 | TABLE ACCESS BY INDEX ROWID| T1 | 1365 | 28665| 11 (0) | 00:00:01
        |* 2 | INDEX RANGE SCAN | IDX_T1 | 1365 | | 3 (0) | 00:00:01
        --------------------------------------------------------------------------------

        从上面结果可以看出,在为绑定变量传入第一个值为13871时,由于返回的记录条数较少,导致走索引扫描。当我们第二次传入绑定变量值1时,oracle不再生成新的执行计划,而直接拿索引扫描的执行路径来用。

        但是,如果先传入1的绑定变量值,然后再传入13871的绑定变量值时,会怎样?我们继续测试。

         hr@ora10g> alter system flush shared_pool;
        hr@ora10g> set autotrace traceonly exp stat;
        hr@ora10g> exec :v_id := 1;
        hr@ora10g> select * from t1 where id=:v_id;
        hr@ora10g > begin
        2 select sql_id into :v_sql_id from v$sql
        3 where sql_text like 'select * from t1 where id=:v_id%';
        4 end;
        5 /
        hr@ora10g > select * from table(dbms_xplan.display_cursor(:v_sql_id));
        PLAN_TABLE_OUTPUT
        --------------------------------------------------------------------------------
        SQL_ID djwq30cpbcz7k, child number 0
        -------------------------------------
        select * from t1 where id=:v_id
        Plan hash value: 3617692013
        --------------------------------------------------------------------------
        | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
        --------------------------------------------------------------------------
        | 0 | SELECT STATEMENT | | | | 13 (100)| |
        |* 1 | TABLE ACCESS FULL | T1 | 8738 | 179K | 13 (0) | 00:00:01 |
        --------------------------------------------------------------------------
        ......
        hr@ora10g > exec :v_id := 13871;
        hr@ora10g > select * from t1 where id=:v_id;
        hr@ora10g > begin
        2 select sql_id into :v_sql_id from v$sql
        3 where sql_text like 'select * from t1 where id=:v_id%';
        4 end;
        5 /
        hr@ora10g > select * from table(dbms_xplan.display_cursor(:v_sql_id));
        PLAN_TABLE_OUTPUT
        --------------------------------------------------------------------------------
        SQL_ID djwq30cpbcz7k, child number 0
        -------------------------------------
        select * from t1 where id=:v_id
        Plan hash value: 3617692013
        --------------------------------------------------------------------------
        | Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
        --------------------------------------------------------------------------
        | 0 | SELECT STATEMENT | | | | 13 (100)| |
        |* 1 | TABLE ACCESS FULL | T1 | 8738 | 179K | 13 (0) | 00:00:01 |
        --------------------------------------------------------------------------

        很明显,先传入1的绑定变量时将导致生成的执行计划走全表扫描。后面传入的13871的绑定变量的最佳执行路径应该是索引扫描,但是由于CBO并不知道这一点,而是直接拿第一次生成的执行计划来用了,于是也走全表扫描了。

[1] [2] 

Oracle11g新特性之动态变量窥视

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

<🎜>:泡泡膠模擬器無窮大 - 如何獲取和使用皇家鑰匙
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
北端:融合系統,解釋
3 週前 By 尊渡假赌尊渡假赌尊渡假赌
Mandragora:巫婆樹的耳語 - 如何解鎖抓鉤
3 週前 By 尊渡假赌尊渡假赌尊渡假赌

熱工具

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

SublimeText3漢化版

SublimeText3漢化版

中文版,非常好用

禪工作室 13.0.1

禪工作室 13.0.1

強大的PHP整合開發環境

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)

熱門話題

Java教學
1665
14
CakePHP 教程
1423
52
Laravel 教程
1321
25
PHP教程
1269
29
C# 教程
1249
24
將VirtualBox固定磁碟轉換為動態磁碟,反之亦然 將VirtualBox固定磁碟轉換為動態磁碟,反之亦然 Mar 25, 2024 am 09:36 AM

在建立虛擬機器時,系統會要求您選擇磁碟類型,您可以選擇固定磁碟或動態磁碟。如果您選擇了固定磁碟,後來意識到需要動態磁碟,或者相反,該怎麼辦?好!你可以把一種轉換成另一種。在這篇文章中,我們將看到如何將VirtualBox固定磁碟轉換為動態磁碟,反之亦然。動態磁碟是一種虛擬硬碟,它最初具有較小的大小,隨著您在虛擬機器中儲存數據,其大小會相應增長。動態磁碟在節省儲存空間方面非常高效,因為它們只佔用所需的主機儲存空間。然而,隨著磁碟容量的擴展,可能會稍微影響電腦的效能。固定磁碟和動態磁碟是虛擬機器中常用的

如何使用Ajax從PHP方法取得變數? 如何使用Ajax從PHP方法取得變數? Mar 09, 2024 pm 05:36 PM

使用Ajax從PHP方法取得變數是Web開發中常見的場景,透過Ajax可以實作頁面無需刷新即可動態取得資料。在本文中,將介紹如何使用Ajax從PHP方法中取得變量,並提供具體的程式碼範例。首先,我們需要寫一個PHP檔案來處理Ajax請求,並傳回所需的變數。下面是一個簡單的PHP檔案getData.php的範例程式碼:

Python 語法的心智圖:深入理解程式碼結構 Python 語法的心智圖:深入理解程式碼結構 Feb 21, 2024 am 09:00 AM

python憑藉其簡單易讀的語法,廣泛應用於廣泛的領域。掌握Python語法的基礎架構至關重要,既可以提高程式效率,又能深入理解程式碼的運作方式。為此,本文提供了一個全面的心智圖,詳細闡述了Python語法的各個面向。變數和資料類型變數是Python中用於儲存資料的容器。心智圖展示了常見的Python資料類型,包括整數、浮點數、字串、布林值和列表。每個資料類型都有其自身的特性和操作方法。運算符運算符用於對資料類型執行各種操作。心智圖涵蓋了Python中的不同運算子類型,例如算術運算子、比

什麼是Java中的實例變數 什麼是Java中的實例變數 Feb 19, 2024 pm 07:55 PM

Java中的實例變數是指定義在類別中,而不是方法或建構子中的變數。實例變數也稱為成員變量,每個類別的實例都有自己的一份實例變數副本。實例變數在創建物件的過程中被初始化,以及在物件的生命週期中保存並保持其狀態。實例變數的定義通常放在類別的頂部,可以用任何存取修飾符來聲明,可以是public、private、protected或預設存取修飾符。這取決於我們希望這個變

深入理解C語言中的const 深入理解C語言中的const Feb 18, 2024 pm 12:56 PM

C中const的詳解及程式碼範例在C語言中,const關鍵字用來定義常數,表示該變數的值在程式執行過程中不能被修改。 const關鍵字可以用來修飾變數、函數參數、函數傳回值。本文將對C語言中const關鍵字的使用進行詳細解析,並提供具體的程式碼範例。 const修飾變數當const用來修飾變數時,表示變數為唯讀變量,一旦賦值就無法再修改。例如:constint

jQuery使用實踐:判斷變數是否為空的幾種方式 jQuery使用實踐:判斷變數是否為空的幾種方式 Feb 27, 2024 pm 04:12 PM

jQuery是一個廣泛應用於Web開發中的JavaScript庫,它提供了許多簡潔方便的方法來操作網頁元素和處理事件。在實際開發中,常會遇到需要判斷變數是否為空的情況。本文將介紹使用jQuery判斷變數是否為空的幾種常用方法,並附上具體的程式碼範例。方法一:使用if語句判斷varstr="";if(str){co

Golang中有類似類別的物件導向特性嗎? Golang中有類似類別的物件導向特性嗎? Mar 19, 2024 pm 02:51 PM

在Golang(Go語言)中並沒有傳統意義上的類別的概念,但它提供了一種稱為結構體的資料類型,透過結構體可以實現類似類別的物件導向特性。在本文中,我們將介紹如何使用結構體實現物件導向的特性,並提供具體的程式碼範例。結構體的定義和使用首先,讓我們來看看結構體的定義和使用方式。在Golang中,結構體可以透過type關鍵字定義,然後在需要的地方使用。結構體中可以包含屬

解析MyBatis中的動態SQL標籤:select標籤 解析MyBatis中的動態SQL標籤:select標籤 Feb 24, 2024 pm 12:15 PM

動態SQL是MyBatis框架中非常重要的功能之一,它可以根據不同的條件在SQL語句中進行動態的拼接和處理,實現靈活的SQL操作。其中,選擇標籤是動態SQL中的關鍵標籤,它主要用於實現條件選擇的邏輯。本文將探討MyBatis中選擇標籤的使用方法,並提供具體的程式碼範例進行示範。 1.選擇標籤的基本語法選擇標籤在MyBatis主要有兩種形式:

See all articles