在MySQL數據庫中,varchar和int是兩種常用的數據類型。varchar用于存儲可變長度的字符數據,而int則用于存儲整數值。在實際應用中,我們可能會遇到需要在這兩種數據類型之間進行轉換的情況。MySQL提供了隱式轉換機制,可以在某些情況下自動將數據類型進行轉換,但同時也存在一些需要注意的細節。
現在來看一些常見的情況下varchar和INTEGER之間的隱式轉換。當我們在一個操作中將varchar類型的列與int類型的列進行比較時,MySQL會嘗試將varchar列的值轉換為整數值,然后進行比較。例如,我們有一個名為"age"的varchar列和一個名為"age_limit"的int列,我們可以執行以下查詢:
SELECT * FROM user WHERE age < age_limit;
在這個查詢中,MySQL會將"age"列的值隱式地轉換為整數值,然后與"age_limit"列的值進行比較。這種隱式轉換可以方便地進行數據比較,但需要注意的是,如果"age"列的值無法轉換為整數,或者不符合整數的范圍,可能會導致錯誤的結果。
1 SELECT * FROM test_demo WHERE num_int = 10;
2 SELECT * FROM test_demo WHERE num_int = '10';
3 SELECT * FROM test_demo WHERE num_str = 10;
4 SELECT * FROM test_demo WHERE num_str = '10';
在這個查詢中,1和2兩條SQL查詢語句的查詢字段是int類型,3和4兩條SQL查詢語句的查詢字段是varchar類型。以上四條查詢語句的查詢字段雖然相同,但是WHERE條件后一個是數字,一個是字符串。在千萬級數據量下執行四條語句,1,2和4的性能基本沒有差別,但是3的查詢效率卻很差。進一步,在執行以上語句,查看他們的執行計劃信息,發現語句3沒有用上索引,即執行了全表掃描,所以性能才會那么差。那么為什么會觸發全表掃描呢?
查閱 MySQL 相關文檔發現是隱式轉換造成的。MySQL 8.0 的官方文檔://dev.mysql.com/doc/refman/8.0/en/type-conversion.html,
以下規則描述了比較操作如何進行轉換:
1.如果一個或兩個參數是NULL,則比較的結果是NULL,但NULL-safe <=> 相等比較運算符除外。對于NULL <=> NULL,結果為真。無需轉換。
2.如果比較操作中的兩個參數都是字符串,則將它們作為字符串進行比較。
3.如果兩個參數都是整數,則將它們作為整數進行比較。
4.如果不與數字比較,十六進制值將被視為二進制字符串。
5.如果其中一個參數是 a TIMESTAMP或 DATETIME列,而另一個參數是常量,則在執行比較之前將常量轉換為時間戳。這樣做是為了對 ODBC 更友好。這不適用于 的參數 IN()。為了安全起見,在進行比較時,請始終使用完整的日期時間、日期或時間字符串。例如,要在使用 BETWEEN日期或時間值時獲得最佳結果,請使用CAST()將值顯式轉換為所需的數據類型。
6.來自一個或多個表的單行子查詢不被視為常量。例如,如果子查詢返回要與值進行比較的整數DATETIME ,則比較將作為兩個整數進行。整數不會轉換為時間值。要將操作數作為 DATETIME值進行比較,請使用 CAST()將子查詢值顯式轉換為DATETIME.
7.如果其中一個參數是十進制值,則比較取決于另一個參數。如果另一個參數是十進制或整數值,則將參數作為十進制值進行比較,如果另一個參數是浮點值,則將其作為浮點值進行比較。
8.在所有其他情況下,參數將作為浮點(雙精度)數字進行比較。例如,字符串和數字操作數的比較是作為浮點數的比較進行的。
按照上述規則的最后一條,我們的查詢SQL中,字符串與整數的比較會被轉換成兩個浮點數比較,左邊是字符串類型 "1" 轉換成浮點數為1.0,右邊 INT類型的 1 轉換成浮點數 1.0.
根據官方文檔的描述,我們的第 2、3 兩條 SQL 都發生了隱式轉換,第 2 條 SQL 的查詢條件num_int = '10',左邊是int類型右邊是字符串,第 3 條 SQL 相反,那么根據官方轉換規則第 7 條,左右兩邊都會轉換為浮點數再進行比較。在int類型的數據轉換為字符串時,進一步查閱相關資料,發現存在以下規則:
1.不以數字開頭的字符串都將轉換為0。如'abc'、'a123bc'、'abc123'都會轉化為0;
2.以數字開頭的字符串轉換時會進行截取,從第一個字符截取到第一個非數字內容為止。比如'123a'會轉換為123,'0123abc'會轉換為0123也就是123,'03.8abc'會轉換為3.8,其他同理。
根據以上規則,我們可以解釋語句3為什么會出現索引丟失的情況了。在查詢字段為字段串類型時發生了隱式轉換,進而執行全表掃描,導致查詢結果存在多條的情況發生。為了避免潛在的問題,我們應該盡量避免在不同數據類型之間進行隱式轉換。在設計數據庫表時,應該合理選擇數據類型,確保相同類型的數據存儲在相同類型的列中。如果需要進行數據類型轉換,最好明確地使用函數或操作符進行顯式轉換,以提高查詢的可讀性和可維護性。
總結起來,MySQL中的varchar和int之間存在隱式轉換的機制,可以在一定程度上方便我們進行數據比較和運算。然而,在使用隱式轉換時需要注意潛在的問題,并在設計數據庫和編寫查詢時盡量避免過度依賴隱式轉換。合理選擇數據類型和明確地進行類型轉換可以提高查詢的效率和可維護性,從而更好地利用MySQL的功能。