寫錯了一條 SQL 語句,被經理邀請登山?

前戲

SQL 寫的秒,漲薪呱呱叫!mysql

新來的實習生小楊寫了一條 SQL 語句web

SELECT wx_id from `user` WHERE wx_id = 2
複製代碼

當小楊火燒眉毛準備下班回家的時候,隔壁的王經理一把抓住了小楊,並用 EXPLAIN 命令教育了小楊,小楊流下了沒有文化的淚水。sql

這條 SQL 語句中,wx_id 是具備索引的,可是王經理查出來的結果倒是這樣的數據庫

王經理的教育
王經理的教育

小楊仔細一瞅 key 字段顯示爲 Null,很明顯這條SQL語句沒有走索引。併發

小楊心想「糟糕,又寫錯 SQL 語句了,這下又要面臨運維和經理的混合雙打了, 不行我得立馬改下這條 SQL 語句,讓我想一想哪裏出錯了」 王經理的教育運維

小楊腦殼瓜瘋狂亂撞,仔細回想表結構,突然想到,wx_id 字段是 varchar 類型,本身查詢的時候居然沒有加引號。編輯器

小楊一把搶過經理手裏的鍵盤,往 wx_id 的查詢條件上加了引號,結果 小楊的掙扎 果真這條 SQL 語句開始走了索引。小楊沾沾自喜覺得解決了個天大的 Bug。ide

經理微微一笑問道「你知道爲何爲何加了引號就走了索引嗎?若是字段是 int 類型,那麼查詢的時候需不須要加引號呢?又是爲何呢?」函數

正餐來了

小楊被問的呆在原地,沒法回答。高併發

通過小楊研究發現,若是字段是 varchar類型,等號右側必須加引號才走索引;若是字段是 int 類型,那麼等號右側加不加引號都是會走索引的。

什麼?你不相信小楊說的話,有圖有真相。(bonus 字段類型爲int)

真相圖
真相圖

可是結論出來,仍是沒法回答經理的奪命三連問。苦惱的小楊打開了公衆號 碼兒嘟嘟騎 想要尋求答案。

這不是廣告
這不是廣告

小楊搬來了答案

碼兒嘟嘟騎的號主告訴小楊

在 MySQL 查詢中,當查詢條件左右兩側類型不匹配的時候會發生隱式轉換

也就是說
SELECT wx_id from `user` WHERE wx_id = 2 等價於 SELECT wx_id from `user` WHERE CAST(wx_id AS signed int) = 2 複製代碼

一旦對索引字段作函數操做,MySQL 會放棄使用索引

因此若是字段是 varchar 類型,等號右側必須加引號才走索引,不然因爲隱式轉換,MySQL 會放棄使用索引。那麼憑什麼 int 加不加引號均可以使用索引呢?

那是由於 int 類型的數字只有2能轉化爲'2',是惟一肯定的。因此雖然須要隱式轉換,但不影響使用索引

小楊追問:「你還能在告訴我一些隱式轉換的知識嗎?」

號主反手就是一個英文文檔

If one or both arguments are NULL, the result of the comparison is NULL, except for the NULL-safe <=> equality comparison operator. For NULL <=> NULL, the result is true. No conversion is needed.
 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 TIMESTAMP or DATETIME column and the other argument is a constant, the constant is converted to a timestamp before the comparison is performed. This is done to be more ODBC-friendly. Note that this is not done for the arguments to IN()! To be safe, always use complete datetime, date, or time strings when doing comparisons. For example, to achieve best results when using BETWEEN with date or time values, use CAST() to explicitly convert the values to the desired data type. A single-row subquery from a table or tables is not considered a constant. For example, if a subquery returns an integer to be compared to a DATETIME value, the comparison is done as two integers. The integer is not converted to a temporal value. To compare the operands as DATETIME values, use CAST() to explicitly convert the subquery value to DATETIME.  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. 複製代碼

貼心的我幫大家翻譯成了中文

1, 兩個參數至少有一個是 NULL 時,比較的結果也是 NULL,例外是使用 <=> 
對兩個 NULL 作比較時會返回 1,這兩種狀況都不須要作類型轉換  2, 兩個參數都是字符串,會按照字符串來比較,不作類型轉換  3, 兩個參數都是整數,按照整數來比較,不作類型轉換  4, 十六進制的值和非數字作比較時,會被當作二進制串  5, 有一個參數是 TIMESTAMP 或 DATETIME,而且另一個參數是常量,常量會被轉換爲 timestamp  6, 有一個參數是 decimal 類型,若是另一個參數是 decimal 或者整數會將整數轉換爲 decimal 後進行比較,  若是另一個參數是浮點數,則會把 decimal 轉換爲浮點數進行比較  7, 全部其餘狀況下,兩個參數都會被轉換爲浮點數再進行比較 複製代碼

再分享一個隱式轉換的坑

  • 你是否偶爾刪除了一些不知道的數據?
mysql> select * from test;
+----+-------+-----------+ | id | name | password | +----+-------+-----------+ | 1 | test1 | password1 | | 2 | test2 | password2 | | 3 | aaa | aaaa | | 4 | 55aaa | 55aaaa | | 5 | 1212 | aaa | | 6 | 1212a | aaa | +----+-------+-----------+ 6 rows in set (0.00 sec)  mysql> select * from test where name = 1212; +----+-------+----------+ | id | name | password | +----+-------+----------+ | 5 | 1212 | aaa | | 6 | 1212a | aaa | +----+-------+----------+ 2 rows in set, 5 warnings (0.00 sec)  mysql> select * from test where name = '1212'; +----+------+----------+ | id | name | password | +----+------+----------+ | 5 | 1212 | aaa | +----+------+----------+ 1 row in set (0.00 sec) 複製代碼

上面的例子本意是查詢id爲5的那一條記錄,結果把id爲6的那一條也查詢出來了。我想說明什麼狀況呢?有時候咱們的數據庫表中的一些列是varchar類型,可是存儲的值爲‘1123’這種的純數字的字符串值,一些同窗寫sql的時候又不習慣加引號。這樣當進行select,update或者delete的時候就可能會多操做一些數據。因此應該加引號的地方別忘記了。

總而言之

隱式類型轉換有沒法命中索引的風險,在高併發、大數據量的狀況下,命不中索引帶來的後果可不止被運維和經理混合雙打哦!且寫 SQL 且 EXPLAIN

本文使用 mdnice 排版

相關文章
相關標籤/搜索