天天看點

sql中根據某個條件判斷是否groupby_通過錯誤的SQL來測試推理SQL的解析過程

這是學習筆記的第 1977 篇文章

如果抛出一個問題,你是如何了解 MySQL 解析器的,它和 Oracle 解析器有什麼差别?相信大多數同學都會比較迷茫,因為這個問題很難驗證,要不是看源碼,要不就是檢視書上是怎麼說的,其實這兩種方法對我們去了解這個問題來說不是很合适,如果能夠通過實踐來做下了解就好了。

我們可以對問題做一下細化,對一條簡單的SQL語句,整體上來說,解析流程如下所示:

sql中根據某個條件判斷是否groupby_通過錯誤的SQL來測試推理SQL的解析過程

整個流程上,我們做一些相關解釋:

① 對SQL的文法檢查,檢視是否有文法錯誤,比如from、select拼寫錯誤等。

② 在資料字典裡校驗SQL涉及的對象是否存在。

③ 将對象進行名稱轉換,比如同義詞轉義成對應的對象或者select * from test t,其中t是一個同義詞指向hr.test。

④ 檢查語句的使用者是否具有通路對象的權限。

⑤ 生成執行計劃。

如何通過測試來驗證呢,我們可以試一下以毒攻毒,即用錯誤的的SQL來推理SQL的解析過程,我們先來看一下在MySQL側的解析情況。

1)MySQL解析器簡單測試

如何在MySQL中進行驗證呢, 我們可以在MySQL中建立表test:

使用如下的語句:

Create table test (id int,name varchar(30));

如果我們送出一個文法錯誤的SQL,在解析方面MySQL提供的資訊非常有限,

mysql> select1 id3 from2 test1 where3 id2='aaa' group by4 id1 order by5 id0;

ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'select1 id3 from2 test1 where3 id2='aaa' group by4 id1 order by5 id0' at line 1

這種錯誤沒有下标,也沒有明确的錯誤提示,是難以去了解文法解析的過程的,在對象和權限的解析方面,MySQL的解析方式也相對比較單一,即從左到右。

比如我們需要驗證select子句,group by 子句,order by子句的解析順序。

可以使用下面的語句:

mysql> select id1 from test where id=100 group by id2 order by id3;

ERROR 1054 (42S22): Unknown column 'id1' in 'field list'

錯誤在select子句,修複select子句,繼續測試。

mysql> select id from test where id=100 group by id2 order by id3; 

ERROR 1054 (42S22): Unknown column 'id2' in 'group statement'

錯誤在group by子句,修複group by 子句,繼續測試。

mysql> select id from test where id=100 group by id order by id3; 

ERROR 1054 (42S22): Unknown column 'id3' in 'order clause'

錯誤在order by子句

在此,我們需要明确的是,以上對于SQL語句測試,僅僅是簡單測試了解析的過程,如果包含limit子句,整個SQL中是如下的順序來執行的,和解析的方式是有較大的差異的。

1) FROM子句

2) WHERE子句

3) GROUP BY子句

4) HAVING子句

5) ORDER BY子句

6) SELECT子句

7) LIMIT子句

8) 最終結果

為什麼解析順序和執行順序差别很大呢,歸根節點,兩種方式的差異總結來說,解析是在做SQL文本的解析,而運作則是在解析的基礎上做資料的提取,一個是WHAT(是什麼)的思維,一個是HOW(怎麼做)的思維。

如果想要做一些較為完整的測試,該怎麼辦呢,我們可以借鑒Oracle的實作,有的同學可能會想測試Oracle部署環境還是比較麻煩的,其實可以走快捷通道,即Oracle官方提供的線上測試入口:https://livesql.oracle.com,所有的測試和操作都可以線上完成,完全不需要再部署環境了,我們以Oracle 19c的線上環境做測試,來驗證下Oracle的解析器實作,加深我們對于SQL解析過程的了解。

2)Oracle解析器簡單測試

首先準備一個測試表,如下。

create table test (id number,name varchar2(30));

準備好之後,開始測試。重申一下,思路是用有問題的語句來測試和推理。

下面的語句存在很多的問題,來看看Oracle的反應。

select1 id3 from2 test1 where3 id2='aaa' group by4 id1 order by5 id0;

ORA-20001: Query must begin with SELECT or WITH

解析發現select的語句錯誤,其實後面的from、where、group by、order by都有錯誤。但是首先發現的是select的部分,可見解析還是從左至右的方向來做文法解析。

開始修複select的文法錯誤。

select id1 from2 test1 where3 id1='aaa' group by4 id1 order  by5 id1;

ORA-00923: FROM keyword not found where expected

這個時候錯誤指向了FROM,進一步論證解析的順序,我們修複from的錯誤,開始繼續驗證。

select id3 from test1 where3 id2='aaa' group by4 id1 order by5 id0;

ORA-00933: SQL command not properly ended

這個時候錯誤指向了where3,原因在于解析器把where3當作了表test1的别名,修複where的文法錯誤如下。

select id3 from test1 where id2='aaa' group by4 id1 order by5 id0;

ORA-00924: missing BY keyword

而這次又直接指向了group by的部分。通過這三次錯誤指向,更能斷定文法解析是從左至右。對于是否存在表,是否字段存在問題都不會解析。

如下,修複了group by、order by的文法錯誤。

select id3 from test1 where id2='aaa' group by id1 order by id0;

ORA-00942: table or view does not exist

這時發現錯誤指向了test1,提示沒有這個表。可見在文法解析之後隻是開始校驗是否存在這個表,還沒有開始校驗字段的情況。

修複了表名的錯誤,看看報錯資訊。

select id3 from test where id2='aaa' group by id1 order by id0;

ORA-00904: "ID0": invalid identifier

發現是在解析order by 的字段名,對于select、where、group by中的先不解析。

對于不同的Oracle版本,這裡的輸出結果是不一樣的,在11g中是group by,order by的解析順序,這裡僅供參考。

我們修複order by中的錯誤,繼續檢視。

select id3 from test where id2='aaa' group by id1 order by id;

ORA-00904: "ID1": invalid identifier

這個時候解析到了group by 子句中的字段值。但是select,where中還沒有開始解析。

修複group by 子句中的問題,繼續測試。

select id3 from test where id2='aaa' group by id order by id;   

ORA-00904: "ID2": invalid identifier

錯誤指向了where子句,這個時候就剩下了select的部分,修複where的部分。

select id3 from test where id='aaa' group by id order by id;

ORA-00904: "ID3": invalid identifier

通過上面的錯誤測試,基本能夠得到語句解析中的處理順序,但是這裡需要明确的是SQL的解析順序和SQL資料處理的順序是不一樣的,僅僅作為一種參考的思路,我麼來間接驗證一下。

比如我們更進一步,檢視如果字段ID為number,賦予varchar2的資料,是否會在解析的時候校驗出來。

select id from test where id='aaa' group by id order  by id;

no rows selected

  從錯誤來看,目前還沒有到執行階段,是沒有辦法做出判斷的。

而如果我們對表寫入資料,再來看看效果。

select id from test where id='aaa' group by id order  by id;

ORA-01722: invalid number

此時會發現錯誤已經在校驗資料的類型了。

當然在這些場景之外,我們還可以測試索引、統計資訊的一些場景,限于篇幅就不擴充了,大家可以自行測試。

sql中根據某個條件判斷是否groupby_通過錯誤的SQL來測試推理SQL的解析過程