❶ sql 語句的解析過程
五、 SELECT 子句
盡管出現在sql語句的最前面,SELECT在第五步的時候才被處理,SELECT子句返回的表會最終返回給調用者。這個子句包含三個子階段:(5-1)計算表達式,(5-2) 處理DISTINCT,(5-3)應用TOP過濾。
1、 計算表達式
SELECT子句中的表達式可以返回或者操作前一步表中返回的基本列。如果這個sql語句是一個聚合查詢,在Step 3之後,你只能使用GROUP BY中的列,對不屬於GROUP集合中的列必須使用聚合運算。不屬於FROM表中基本列的必須為其起一個別名,如YEAR(orderdate) AS orderyear。
注意:在SELECT子句中創建的別名,不能在之前的Step中使用,即使在SELECT子句中也不能。原因是sql的很多操作是同時操作(all at once operation),至於什麼是all-at-once operation這里就不再介紹了。因此,SELECT子句中創建的別名只能在後面的子句中使用,如ORDER BY。例如:SELECT YEAR(orderdate) AS orderyear . . . ORDER BY orderyear。
在這個例子中:
SELECT C.customerid, COUNT(O.orderid) AS numorders
結果會得到一個虛擬表VT5-1:
C.customerid numorders
FIFSSA 0
FRNDO 2
2、應用DISTINCT子句
如果sql語句中使用了DISTINCT,sql會把重復列去掉,生成虛擬表VT5-2。
3、應用TOP選項
TOP選項是T-SQL提供的一個功能,用來表示顯示多少行。基於ORDER BY子句定義的順序,指定個數的列會被查詢出來。這個過程生成虛擬表VT5-3。
正如上文提到的,這一步依賴於ORDER BY定義的順序來決定哪些列應該顯示在前面。如果你沒有指定結果的ORDER BY順序,也沒有使用WITH TIES子句,每一次的返回結果可能會不一致。
在我們的例子中,Step 5-3被省略了,因為我們沒有使用TOP關鍵字。
六、ORDER BY子句
前一步返回的虛擬表在這一步被排序,根據ORDER BY子句指定的順序,返回遊標VC6。ORDER BY子句也是唯一一個可以使用SELECT子句創建的別名的地方。
注意:這一步和之前不同的地方在於,這一步返回的結果是一個游標,而不是表。sql是基於集合理論的,一個集合沒有對他的行定義順序,它只是一個成員的邏輯集合,因此成員的順序並不重要。帶有ORDER BY子句的sql返回一個按照特定序列組織每一行的對象。ANSI 把這樣的一個對象叫游標。理解這一點對你了解sql很重要。
上面的步驟如圖所示:
❷ 如何對變異的sql語句做解析處理
優化SQL查詢:如何寫出高性能SQL語句1、首先要搞明白什麼叫執行計劃?執行計劃是資料庫根據SQL語句和相關表的統計信息作出的一個查詢方案,這個方案是由查詢優化器自動分析產生欀如一條SQL語句如果用來從一個10萬條記錄的表中查1條記錄,那查詢優化器會選擇「索引查找」方式,如果該表進行了歸檔,當前只剩下5000條記錄了,那查詢優化器就會改變方案,採用「全表掃描」方式。可見,執行計劃並不是固定的,它是「個性化的」。產生一個正確的「執行計劃」有兩點很重要:(1)SQL語句是否清晰地告訴查詢優化器它想干什麼?(2)查詢優化器得到的資料庫統計信息是否是最新的、正確的?2、統一SQL語句的寫法對於以下兩句SQL語句,程序員認為是相同的,資料庫查詢優化器認為是不同的。select*fromalselect*Fromal其實就是大小寫不同,查詢分析器就認為是兩句不同的SQL語句,必須進行兩次解析。生成2個執行計劃。所以作為程序員,應該保證相同的查詢語句在任何地方都一致,多一個空格都不行!3、不要把SQL語句寫得太復雜我經常看到,從資料庫中捕捉到的一條SQL語句列印出來有2張A4紙這么長。一般來說這么復雜的語句通常都是有問題的。我拿著這2頁長的SQL語句去請教原作者,結果他說時間太長,他一時也看不懂了。可想而知,連原作者都有可能看糊塗的SQL語句,資料庫也一樣會看糊塗。一般,將一個Select語句的結果作為子集,然後從該子集中再進行查詢,這種一層嵌套語句還是比較常見的,但是根據經驗,超過3層嵌套,查詢優化器就很容易給出錯誤的執行計劃。因為它被繞暈了。像這種類似人工智慧的東西,終究比人的分辨力要差些,如果人都看暈了,我可以保證資料庫也會暈的。另外,執行計劃是可以被重用的,越簡單的SQL語句被重用的可能性越高。而復雜的SQL語句只要有一個字元發生變化就必須重新解析,然後再把這一大堆垃圾塞在內存里。可想而知,資料庫的效率會何等低下。4、使用「臨時表」暫存中間結果簡化SQL語句的重要方法就是採用臨時表暫存中間結果,但是,臨時表的好處遠遠不止這些,將臨時結果暫存在臨時表,後面的查詢就在tempdb中了,這可以避免程序中多次掃描主表,也大大減少了程序執行中「共享鎖」阻塞「更新鎖」,減少了阻塞,提高了並發性能。5、OLTP系統SQL語句必須採用綁定變數select*>』2010-10-2000:00:01′select*>』2010-09-2200:00:01′以上兩句語句,查詢優化器認為是不同的SQL語句,需要解析兩次。如果採用綁定變數select*>@chgtime@chgtime變數可以傳入任何值,這樣大量的類似查詢可以重用該執行計劃了,這可以大大降低資料庫解析SQL語句的負擔。一次解析,多次重用,是提高資料庫效率的原則。6、綁定變數窺測事物都存在兩面性,綁定變數對大多數OLTP處理是適用的,但是也有例外。比如在where條件中的欄位是「傾斜欄位」的時候。「傾斜欄位」指該列中的絕大多數的值都是相同的,一張人口調查表,其中「民族」這列,90%以上都是漢族。那麼如果一個SQL語句要查詢30歲的漢族人口有多少,那「民族」這列必然要被放在where條件中。這個時候如果採用綁定變數@nation會存在很大問題。試想如果@nation傳入的第一個值是「漢族」,那整個執行計劃必然會選擇表掃描。然後,第二個值傳入的是「布依族」,按理說「布依族」占的比例可能只有萬分之一,應該採用索引查找。但是,由於重用了第一次解析的「漢族」的那個執行計劃,那麼第二次也將採用表掃描方式。這個問題就是著名的「綁定變數窺測」,建議對於「傾斜欄位」不要採用綁定變數。7、只在必要的情況下才使用begintranSQLServer中一句SQL語句默認就是一個事務,在該語句執行完成後也是默認commit的。其實,這就是begintran的一個最小化的形式,好比在每句語句開頭隱含了一個begintran,結束時隱含了一個commit。有些情況下,我們需要顯式聲明begintran,比如做「插、刪、改」操作需要同時修改幾個表,要求要麼幾個表都修改成功,要麼都不成功。begintran可以起到這樣的作用,它可以把若干SQL語句套在一起執行,最後再一起commit。好處是保證了數據的一致性,但任何事情都不是完美無缺的。Begintran付出的代價是在提交之前,所有SQL語句鎖住的資源都不能釋放,直到commit掉。可見,如果Begintran套住的SQL語句太多,那資料庫的性能就糟糕了。在該大事務提交之前,必然會阻塞別的語句,造成block很多。Begintran使用的原則是,在保證數據一致性的前提下,begintran套住的SQL語句越少越好!有些情況下可以採用觸發器同步數據,不一定要用begintran。8、一些SQL查詢語句應加上nolock在SQL語句中加nolock是提高SQLServer並發性能的重要手段,在oracle中並不需要這樣做,因為oracle的結構更為合理,有undo表空間保存「數據前影」,該數據如果在修改中還未commit,那麼你讀到的是它修改之前的副本,該副本放在undo表空間中。這樣,oracle的讀、寫可以做到互不影響,這也是oracle廣受稱贊的地方。SQLServer的讀、寫是會相互阻塞的,為了提高並發性能,對於一些查詢,可以加上nolock,這樣讀的時候可以允許寫,但缺點是可能讀到未提交的臟數據。使用nolock有3條原則。(1)查詢的結果用於「插、刪、改」的不能加nolock!(2)查詢的表屬於頻繁發生頁分裂的,慎用nolock!(3)使用臨時表一樣可以保存「數據前影」,起到類似oracle的undo表空間的功能,能採用臨時表提高並發性能的,不要用nolock。9、聚集索引沒有建在表的順序欄位上,該表容易發生頁分裂比如訂單表,有訂單編號orderid,也有客戶編號contactid,那麼聚集索引應該加在哪個欄位上呢?對於該表,訂單編號是順序添加的,如果在orderid上加聚集索引,新增的行都是添加在末尾,這樣不容易經常產生頁分裂。然而,由於大多數查詢都是根據客戶編號來查的,因此,將聚集索引加在contactid上才有意義。而contactid對於訂單表而言,並非順序欄位。比如「張三」的「contactid」是001,那麼「張三」的訂單信息必須都放在這張表的第一個數據頁上,如果今天「張三」新下了一個訂單,那該訂單信息不能放在表的最後一頁,而是第一頁!如果第一頁放滿了呢?很抱歉,該表所有數據都要往後移動為這條記錄騰地方。SQLServer的索引和Oracle的索引是不同的,SQLServer的聚集索引實際上是對表按照聚集索引欄位的順序進行了排序,相當於oracle的索引組織表。SQLServer的聚集索引就是表本身的一種組織形式,所以它的效率是非常高的。也正因為此,插入一條記錄,它的位置不是隨便放的,而是要按照順序放在該放的數據頁,如果那個數據頁沒有空間了,就引起了頁分裂。所以很顯然,聚集索引沒有建在表的順序欄位上,該表容易發生頁分裂。曾經碰到過一個情況,一位哥們的某張表重建索引後,插入的效率大幅下降了。估計情況大概是這樣的。該表的聚集索引可能沒有建在表的順序欄位上,該表經常被歸檔,所以該表的數據是以一種稀疏狀態存在的。比如張三下過20張訂單,而最近3個月的訂單只有5張,歸檔策略是保留3個月數據,那麼張三過去的15張訂單已經被歸檔,留下15個空位,可以在insert發生時重新被利用。在這種情況下由於有空位可以利用,就不會發生頁分裂。但是查詢性能會比較低,因為查詢時必須掃描那些沒有數據的空位。重建聚集索引後情況改變了,因為重建聚集索引就是把表中的數據重新排列一遍,原來的空位沒有了,而頁的填充率又很高,插入數據經常要發生頁分裂,所以性能大幅下降。對於聚集索引沒有建在順序欄位上的表,是否要給與比較低的頁填充率?是否要避免重建聚集索引?是一個值得考慮的問題!10、加nolock後查詢經常發生頁分裂的表,容易產生跳讀或重復讀加nolock後可以在「插、刪、改」的同時進行查詢,但是由於同時發生「插、刪、改」,在某些情況下,一旦該數據頁滿了,那麼頁分裂不可避免,而此時nolock的查詢正在發生,比如在第100頁已經讀過的記錄,可能會因為頁分裂而分到第101頁,這有可能使得nolock查詢在讀101頁時重復讀到該條數據,產生「重復讀」。同理,如果在100頁上的數據還沒被讀到就分到99頁去了,那nolock查詢有可能會漏過該記錄,產生「跳讀」。上面提到的哥們,在加了nolock後一些操作出現報錯,估計有可能因為nolock查詢產生了重復讀,2條相同的記錄去插入別的表,當然會發生主鍵沖突。11、使用like進行模糊查詢時應注意有的時候會需要進行一些模糊查詢比如select*fromcontactwhereusernamelike『%yue%』關鍵詞%yue%,由於yue前面用到了「%」,因此該查詢必然走全表掃描,除非必要,否則不要在關鍵詞前加%,12、數據類型的隱式轉換對查詢效率的影響sqlserver2000的資料庫一的程序在提交sql語句的時候,沒有使用強類型提交這個欄位的值,由sqlserver2000自動轉換數據類型,會導致傳入的參數與主鍵欄位類型不一致,這個時候sqlserver2000可能就會使用全表掃描。Sql2005上沒有發現這種問題,但是還是應該注意一下。13、SQLServer表連接的三種方式(1)MergeJoin(2)NestedLoopJoin(3)HashJoinSQLServer2000隻有一種join方式——NestedLoopJoin,如果A結果集較小,那就默認作為外表,A中每條記錄都要去B中掃描一遍,實際掃過的行數相當於A結果集行數xB結果集行數。所以如果兩個結果集都很大,那Join的結果很糟糕。SQLServer2005新增了MergeJoin,如果A表和B表的連接欄位正好是聚集索引所在欄位,那麼表的順序已經排好,只要兩邊拼上去就行了,這種join的開銷相當於A表的結果集行數加上B表的結果集行數,一個是加,一個是乘,可見mergejoin的效果要比NestedLoopJoin好多了。如果連接的欄位上沒有索引,那SQL2000的效率是相當低的,而SQL2005提供了Hashjoin,相當於臨時給A,B表的結果集加上索引,因此SQL2005的效率比SQL2000有很大提高,我認為,這是一個重要的原因。總結一下,在表連接時要注意以下幾點:(1)連接欄位盡量選擇聚集索引所在的欄位(2)仔細考慮where條件,盡量減小A、B表的結果集(3)如果很多join的連接欄位都缺少索引,而你還在用SQLServer2000,趕緊升級吧。
❸ 為什麼spring data jpa 的Query註解 不能解析SQL語句
MyBatis詳解 與配置MyBatis+Spring+MySqlMyBatis 是一個可以自定余坦義SQL、存儲過程和高級映射的持久層框架。MyBatis 摒除了大部分的JDBC代碼、手工設置參數和結果集重獲。MyBatis 只使用簡單的XML 和註解來配置和映射基本數據類型豎族桐、Map 介面和POJO 到資料庫記錄。相穗哪對Hibernate和Apache OJB等「一站式」ORM解決方案而言,Mybatis 是一種「半自動化」的ORM實現。需要使用的Jar包:mybatis-3.0.2.jar(mybatis核心包)。mybatis-spring-1.0.0.jar(與Spring結合包)。MyBatis簡介 MyBatis 是一個可以自定義SQL、存儲過程和高級映射的持久層框架。MyBatis 摒除了大部分的JDBC代碼、手工設置參數和結果集重獲。MyBatis 只使用簡單的XML 和註解來配置和映射基本數據類型、Map 介面和POJO 到資料庫記錄。相對Hibernate和Apache OJB等「一站式」ORM解決方案而言,Mybatis 是一種「半自動化」的ORM實現。需要使用的Jar包:mybatis-3.0.2.jar(mybatis核心包)。mybatis-spring-1.0.0.jar(與Spring結合包)。
❹ sql語句執行過程中會解析成二進制位被計算機識別嗎在哪一個階段被解析的
會解析成二進制的,和電腦交互的時候被解析的。
❺ oracle sql是怎麼解析的
導讀:Oracle的後台運作原理是什麼?我們的一條命令是如何被執行的?今天我們就從一條簡單的Select語句開始,看看Oracle資料庫後台的運作機制。
Select語句可以說是DBA和資料庫開發者在工作中使用最多的語句之一,但這條語句是如何執行?在Oracle資料庫中又是如何運作的呢?今天我們就從一條簡單的Select語句開始,看看Oracle資料庫後台的運作機制。這對於我們之後的系統管理與故障排除非常有幫助。
第一步:客戶端把語句發給伺服器端執行
當我們在客戶端執行select語句時,客戶端會把這條SQL語句發送給伺服器端,讓伺服器端的進程來處理這語句。也就是說,Oracle客戶端是不會做任何的操作,他的主要任務就是把客戶端產生的一些SQL語句發送給伺服器端。雖然在客戶端也有一個資料庫進程,但是,這個進程的作用跟伺服器上的進程作用事不相同的。伺服器上的資料庫進程才會對SQL語句進行相關的處理。不過,有個問題需要說明,就是客戶端的進程跟伺服器的進程是一一對應的。也就是說,在客戶端連接上伺服器後,在客戶端與伺服器端都會形成一個進程,客戶端上的我們叫做客戶端進程;而伺服器上的我們叫做伺服器進程。所以,由於所有的SQL語句都是伺服器進程執行的,所以,有些人把伺服器進程形象地比喻成客戶端進程的「影子」。
第二步:語句解析
當客戶端把SQL語句傳送到伺服器後,伺服器進程會對該語句進行解析。同理,這個解析的工作,也是在伺服器端所進行的。雖然這只是一個解析的動作,但是,其會做很多「小動作」。
1. 查詢高速緩存。伺服器進程在接到客戶端傳送過來的SQL語句時,不會直接去資料庫查詢。而是會先在資料庫的高速緩存中去查找,是否存在相同語句的執行計劃。如果在數據高速緩存中,剛好有其他人使用這個查詢語句的話,則伺服器進程就會直接執行這個SQL語句,省去後續的工作。所以,採用高速數據緩存的話,可以提高SQL語句的查詢效率。一方面是從內存中讀取數據要比從硬碟中的數據文件中讀取數據效率要高,另一方面,也是因為這個語句解析的原因。
不過這里要注意一點,這個數據緩存跟有些客戶端軟體的數據緩存是兩碼事。有些客戶端軟體為了提高查詢效率,會在應用軟體的客戶端設置數據緩存。由於這些數據緩存的存在,可以提高客戶端應用軟體的查詢效率。但是,若其他人在伺服器進行了相關的修改,由於應用軟體數據緩存的存在,導致修改的數據不能及時反映到客戶端上。從這也可以看出,應用軟體的數據緩存跟資料庫伺服器的高速數據緩存不是一碼事。
2. 語句合法性檢查。當在高速緩存中找不到對應的SQL語句時,則資料庫伺服器進程就會開始檢查這條語句的合法性。這里主要是對SQL語句的語法進行檢查,看看其是否合乎語法規則。如果伺服器進程認為這條SQL語句不符合語法規則的時候,就會把這個錯誤信息,反饋給客戶端。在這個語法檢查的過程中,不會對SQL語句中所包含的表名、列名等等進行SQL他只是語法上的檢查。
3. 語言含義檢查。若SQL語句符合語法上的定義的話,則伺服器進程接下去會對語句中的欄位、表等內容進行檢查。看看這些欄位、表是否在資料庫中。如果表名與列名不準確的話,則資料庫會就會反饋錯誤信息給客戶端。
所以,有時候我們寫select語句的時候,若語法與表名或者列名同時寫錯的話,則系統是先提示說語法錯誤,等到語法完全正確後,再提示說列名或表名錯誤。若能夠掌握這個順序的話,則在應用程序排錯的時候,可以節省時間。
4. 獲得對象解析鎖。當語法、語義都正確後,系統就會對我們需要查詢的對象加鎖。這主要是為了保障數據的一致性,防止我們在查詢的過程中,其他用戶對這個對象的結構發生改變。對於加鎖的原理與方法,我在其他文章中已經有專門敘述,在這里就略過不談了。
5. 數據訪問許可權的核對。當語法、語義通過檢查之後,客戶端還不一定能夠取得數據。伺服器進程還會檢查,你所連接的用戶是否有這個數據訪問的許可權。若你連接上伺服器的用戶不具有數據訪問許可權的話,則客戶端就不能夠取得這些數據。故,有時候我們查詢數據的時候,辛辛苦苦地把SQL語句寫好、編譯通過,但是,最後系統返回個 「沒有許可權訪問數據」的錯誤信息,讓我們氣半死。這在前端應用軟體開發調試的過程中,可能會碰到。所以,要注意這個問題,資料庫伺服器進程先檢查語法與語義,然後才會檢查訪問許可權。
6. 確定最佳執行計劃。當語句與語法都沒有問題,許可權也匹配的話,伺服器進程還是不會直接對資料庫文件進行查詢。伺服器進程會根據一定的規則,對這條語句進行優化。不過要注意,這個優化是有限的。一般在應用軟體開發的過程中,需要對資料庫的sql語言進行優化,這個優化的作用要大大地大於伺服器進程的自我優化。所以,一般在應用軟體開發的時候,資料庫的優化是少不了的。
當伺服器進程的優化器確定這條查詢語句的最佳執行計劃後,就會將這條SQL語句與執行計劃保存到數據高速緩存。如此的話,等以後還有這個查詢時,就會省略以上的語法、語義與許可權檢查的步驟,而直接執行SQL語句,提高SQL語句處理效率。
第三步:語句執行
語句解析只是對SQL語句的語法進行解析,以確保伺服器能夠知道這條語句到底表達的是什麼意思。等到語句解析完成之後,資料庫伺服器進程才會真正的執行這條SQL語句。
這個語句執行也分兩種情況。一是若被選擇行所在的數據塊已經被讀取到數據緩沖區的話,則伺服器進程會直接把這個數據傳遞給客戶端,而不是從資料庫文件中去查詢數據。若數據不在緩沖區中,則伺服器進程將從資料庫文件中查詢相關數據,並把這些數據放入到數據緩沖區中。
這里仍然要注意一點,就是Oracle資料庫中,定義了很多種類的高速緩存。像上面所說的SQL語句緩存與現在講的數據緩存。我們在學習資料庫的時候,需要對這些緩存有一個清晰的認識,並了解各個種類緩存的作用。這對於我們後續資料庫維護與資料庫優化是非常有用的。
第四步:提取數據
當語句執行完成之後,查詢到的數據還是在伺服器進程中,還沒有被傳送到客戶端的用戶進程。所以,在伺服器端的進程中,有一個專門負責數據提取的一段代碼。他的作用就是把查詢到的數據結果返回給用戶端進程,從而完成整個查詢動作。
從這整個查詢處理過程中,我們在資料庫開發或者應用軟體開發過程中,需要注意以下幾點:
一是要了解資料庫緩存跟應用軟體緩存是兩碼事情。資料庫緩存只有在資料庫伺服器端才存在,在客戶端是不存在的。只有如此,才能夠保證資料庫緩存中的內容跟資料庫文件的內容一致。才能夠根據相關的規則,防止數據臟讀、錯讀的發生。而應用軟體所涉及的數據緩存,由於跟資料庫緩存不是一碼事情,所以,應用軟體的數據緩存雖然可以提高數據的查詢效率,但是,卻打破了數據一致性的要求,有時候會發生臟讀、錯讀等情況的發生。所以,有時候,在應用軟體上有專門一個功能,用來在必要的時候清除數據緩存。不過,這個數據緩存的清除,也只是清除本機上的數據緩存,或者說,只是清除這個應用程序的數據緩存,而不會清除資料庫的數據緩存。
二是絕大部分SQL語句都是按照這個處理過程處理的。我們DBA或者基於Oracle資料庫的開發人員了解這些語句的處理過程,對於我們進行涉及到SQL語句的開發與調試,是非常有幫助的。有時候,掌握這些處理原則,可以減少我們排錯的時間。特別要注意,資料庫是把數據查詢許可權的審查放在語法語義的後面進行檢查的。所以,有時會若光用資料庫的許可權控制原則,可能還不能滿足應用軟體許可權控制的需要。此時,就需要應用軟體的前台設置,實現許可權管理的要求。而且,有時應用資料庫的許可權管理,也有點顯得繁瑣,會增加伺服器處理的工作量。因此,對於記錄、欄位等的查詢許可權控制,大部分程序涉及人員喜歡在應用程序中實現,而不是在資料庫上實現。
❻ 為什麼spring data jpa 的Query註解 不能解析SQL語句
為什麼spring data jpa 的Query註解 不能解析SQL語句
你鉛型舉這個findone(id)是用自己的槐碧@Query註解的jpql語句? 如果不是,方法應該是findOne(Interger id)才對,可但是你這個實體類哪裡有id的欄位; 你也可以試試findBy欄位這種方式 另外注意下幾個Repository介面的細微區別,會不租或會問題出在介面選擇上面!
❼ 無法解析SQL語句, 請檢查SQL語法錯誤. 代碼: 1054 錯誤: Unknown column 's.uid' in 'where clause'
s代表的那個表裡面沒有uid這個欄位,,,所以是未知的欄位名,,你看看那個欄位是不是uuid
❽ Sql語句解析過程
為了將用戶寫的SQL文本轉化為Oracle認識的且可執行的語句 這個過程就叫做解析過程 解析分為硬解析和軟解析 一條SQL語句在第一次被執行時必須進行硬解析
當客戶端發出一條SQL語句(也可以是一個存儲過程或者一個匿名PL/SQL塊)進入shared pool時(注意 我們從前面已經知道 Oracle對這些SQL不叫做SQL語句 而是稱為游標 因為Oracle在處理SQL時 需要很多相關的輔助信息 這些輔助信息與SQL語句一起組成了游標) Oracle首先將SQL文本轉化為ASCII值 然後根據hash函數計算其對應的hash值(hash_value) 根據計算出的hash值到library cache中找到對應的bucket 然後比較bucket里是否存在該SQL語句
如果不存在 則需要按照我們前面所描述的 獲得shared pool latch 然後在shared pool中的可用chunk鏈表(也就是bucket)上找到一個可用的chunk 之後釋放shared pool latch 在獲得了chunk以後 這塊chunk就可以認為是進入了library cache 接下來 進行硬解析過程 硬解析包括以下幾個步驟
對SQL語句進行文法檢查 看是否有文法錯誤 比如沒有寫from select拼寫錯誤等 如果存在文法錯誤 則退出解析過程
到數據字典里校驗SQL語句涉及的對象和列是否都存在 如果不存在 則退出解析過程 這個過程會載入dictionary cache
將對象進行名稱轉換 比如將同名詞翻譯成實際的對象等 比如select * from t中 t是一個同名詞 指向hr t 於是Oracle將t轉換為hr t 如果轉換失敗 則退出解析過程
檢查發出SQL語句的用戶是否具有訪問SQL語句里所引用的對象的許可權 如果沒有許可權 則退出解析過程
通過優化器創建一個最優的執行計劃 這個過程會根據數據字典里記錄的對象的統計信息 來計算最優的執行計劃 這一步牽涉大量數學運算 是最消耗CPU資源的
將該游標所產生的執行計劃 SQL文本等裝載進library cache的heap中
在硬解析的過程中 進程會一直持有library cache latch 直到硬解析結束為止 硬解析結束以後 會為SQL語句產生兩個游標 一個是父游標 另一個是子游標 父游標里主要包含兩種信息 SQL文本以及優化目標(optimizer goal) 父游標在第一次打開時被鎖定 直到其他所有的session都關閉該游標後才被解鎖 當父游標被鎖定的時候是不能被交換出library cache的 只有在解鎖以後才能被交換出library cache 父游標被交換出內存時 父游標對應的所有子游標也被交換出library cache 子游標包括游標所有的信息 比如具體的執行計劃 綁定變數等 子游標隨時可以被交換出library cache 當子游標被交換出library cache時 Oracle可以利用父游標的信息重新構建出一個子游標來 這個過程叫reload 可以使用下面的方式來確定reload的比率
select *sum(reloads)/sum(pins) Reload_Ratio from v$librarycache;
一個父游標可以對應多個子游標 子游標具體的個數可以從視圖v$sqlarea的version_count欄位體現出來 而每個具體的子游標則全都在視圖v$sql里體現 當具體綁定變數的值與上次綁定變數的值有較大差異(比如上次執行的綁定變數值的長度是 位 而這次執行綁定變數的值的長度是 位)時或者當SQL語句完全相同 但是所引用的表屬於不同的用戶時 都會創建一個新的子游標
如果在bucket中找到了該SQL語句 則說明該SQL語句以前運行過 於是進行軟解析 軟解析是相對於硬解析而言的 如果解析過程中 可以從硬解析的步驟中去掉一個或多個的話 這樣的解析就是軟解析 軟解析分為以下三種類型
第一種是某個session發出的SQL語句與library? cache里其他session發出的SQL語句一致 這時 該解析過程中可以去掉硬解析中的 和 但是仍然要進行硬解析過程中的 也就是表名和列名檢查 名稱轉換和許可權檢查
* 第二種是某個session發出的SQL語句是該session之前發出的曾經執行過的SQL語句 這時 該解析過程中可以去掉硬解析中的 和 這四步 但是仍然要進行許可權檢查 因為可能通過grant改變了該session用戶的許可權
* 第三種是當設置了初始化參數session_cached_cursors時 當某個session第三次執行相同的SQL時 則會把該SQL語句的游標信息轉移到該session的PGA里 這樣 該session以後再執行相同的SQL語句時 會直接從PGA里取出執行計劃 從而跳過硬解析的所有步驟 這種情況下 是最高效的解析方式 但是會消耗很大的內存
我們舉一個例子來說明解析SQL語句的過程 在該測試中 綁定變數名稱相同 但是變數類型不同時 所出現的解析情況 如下所示
首先 執行下面的命令 清空shared pool里所有的SQL語句
SQL> alter system flush shared_pool;
然後 定義一個數值型綁定變數 並為該綁定變數賦一個數值型的值以後 執行具體的查詢語句
SQL> variable v_obj_id number;
SQL> exec :v_obj_id := ;
SQL> select object_id object_name from sharedpool_test
where object_id=:v_obj_id;
OBJECT_ID OBJECT_NAME
AGGXMLIMP
接下來 定義一個字元型的綁定變數 變數名與前面相同 為該綁定變數賦一個字元型的值以後 執行相同的查詢
SQL> variable v_obj_id varchar ( );
SQL> exec :v_obj_id := ;
SQL> select object_id object_name from sharedpool_test
where object_id=:v_obj_id;
OBJECT_ID OBJECT_NAME
AGGXMLIMP
然後我們到視圖v$sqlarea里找到該SQL的父游標的信息 並到視圖v$sql里找該SQL的所有子游標的信息
SQL> select sql_text version_count from v$sqlarea where
sql_text like %sharedpool_test% ;
SQL_TEXT
VERSION_COUNT
select object_id object_name from sharedpool_test where
object_id=:v_obj_id
SQL> select sql_text child_address address from v$sql
where sql_text like %sharedpool_test% ;
SQL_TEXT
CHILD_ADDRESS ADDRESS
select object_id object_name from sharedpool_test where
object_id=:v_obj_id F
B D
select object_id object_name from sharedpool_test where
object_id=:v_obj_id FC
B D
從記錄父游標的視圖v$sqlarea的version_count列可以看到 該SQL語句有 個子游標 而從記錄子游標的視圖v$sql里可以看到 該SQL文本確實有兩條記錄 而且它們的SQL文本所處的地址(ADDRESS列)也是一樣的 但是子地址(CHILD_ADDRESS)卻不一樣 這里的子地址實際就是子游標所對應的heap 的句柄
lishixin/Article/program/Oracle/201311/18653