天天看點

MySQL隐式轉化整理

當我們對不同類型的值進行比較的時候,為了使得這些數值「可比較」(也可以稱為類型的相容性),mysql會做一些隐式轉化(implicit type conversion)。比如下面的例子:

很明顯,上面的sql語句的執行過程中就出現了隐式轉化。并且從結果們可以判斷出,第一條sql中,将字元串的“1”轉換為數字1,而在第二條的sql中,将數字2轉換為字元串“2”。

mysql也提供了<code>cast()</code>函數。我們可以使用它明确的把數值轉換為字元串。當使用<code>conca()</code>函數的時候,也可能會出現隐式轉化,因為它希望的參數為字元串形式,但是如果我們傳遞的不是字元串呢:

官方文檔中關于隐式轉化的規則是如下描述的:

if both arguments in a comparison operation are strings, they are compared as strings. if both arguments are integers, they are compared as integers. hexadecimal values are treated as binary strings if not compared to a number. if one of the arguments is a decimal value, comparison depends on the other argument. the arguments are compared as decimal values if the other argument is a decimal or integer value, or as floating-point values if the other argument is a floating-point value. in all other cases, the arguments are compared as floating-point (real) numbers.

翻譯為中文就是:

兩個參數至少有一個是 null 時,比較的結果也是 null,例外是使用 &lt;=&gt; 對兩個 null 做比較時會傳回 1,這兩種情況都不需要做類型轉換

兩個參數都是字元串,會按照字元串來比較,不做類型轉換

兩個參數都是整數,按照整數來比較,不做類型轉換

十六進制的值和非數字做比較時,會被當做二進制串

有一個參數是 <code>timestamp</code> 或 <code>datetime</code>,并且另外一個參數是<code>常量</code>,常量會被轉換為 <code>timestamp</code>

有一個參數是 decimal 類型,如果另外一個參數是 decimal 或者整數,會将整數轉換為 decimal 後進行比較,如果另外一個參數是浮點數,則會把 decimal 轉換為浮點數進行比較

所有其他情況下,兩個參數都會被轉換為浮點數再進行比較

相信上面的例子,一些機靈的同學可以發現其實上面的例子也可以做sql注入。

假設網站的登入那塊做的比較挫,使用下面的方式:

如果username輸入的是<code>a' or 1='1</code>,那麼password随便輸入,這樣就生成了下面的查詢:

就有可能登入系統。其實如果攻擊者看過了這篇文章,那麼就可以利用隐式轉化來進行登入了。如下:

之是以出現上述的原因是因為:

下面通過一些例子來複習一下上面的轉換規則:

把字元串“aa”和1進行求和,得到1,因為“aa”和數字1的類型不同,mysql官方文檔告訴我們:

when an operator is used with operands of different types, type conversion occurs to make the operands compatible.

檢視warnings可以看到隐式轉化把字元串轉為了double類型。但是因為字元串是非數字型的,是以就會被轉換為0,是以最終計算的是<code>0+1=1</code>

上面的例子是類型不同,是以出現了隐式轉化,那麼如果我們使用相同類型的值進行運算呢?

是不是有點郁悶呢?

在看一個例子:

現在就看也很好的了解上面的例子了吧。<code>a+b=c</code>結果為1,1在mysql中可以了解為<code>true</code>,因為<code>'a'+'b'</code>的結果為0,<code>c</code>也會隐式轉化為0,是以比較其實是:<code>0=0</code>也就是true,也就是1.

​ 上面的例子本意是查詢id為5的那一條記錄,結果把id為6的那一條也查詢出來了。我想說明什麼情況呢?有時候我們的資料庫表中的一些列是varchar類型,但是存儲的值為‘1123’這種的純數字的字元串值,一些同學寫sql的時候又不習慣加引号。這樣當進行select,update或者delete的時候就可能會多操作一些資料。是以應該加引号的地方别忘記了。

從上面的例子可以看出,當把字元串轉為數字的時候,其實是從左邊開始處理的。

如果字元串的第一個字元就是非數字的字元,那麼轉換為數字就是0

如果字元串以數字開頭

如果字元串中都是數字,那麼轉換為數字就是整個字元串對應的數字

如果字元串中存在非數字,那麼轉換為的數字就是開頭的那些數字對應的值

如果你有其他更好的例子,或者被隐式轉化坑過的情況,歡迎分享。

<a href="http://dev.mysql.com/doc/refman/5.7/en/cast-functions.html">http://dev.mysql.com/doc/refman/5.7/en/cast-functions.html</a>

<a href="https://blog.eood.cn/mysql_params">https://blog.eood.cn/mysql_params</a>

<a href="http://dev.mysql.com/doc/refman/5.7/en/type-conversion.html">http://dev.mysql.com/doc/refman/5.7/en/type-conversion.html</a>