MySQL 避坑指南之隐式数据类型转换
作者 | 不剪發的Tony老師
責編 | 歐陽姝黎
出品 | CSDN博客
????知之為知之,不知為不知,是知也。——《論語》
今天我們來聊聊 MySQL 中存在的隱式數據類型轉換以及可能帶來的問題。
當兩個不同類型的數據進行運算時,為了使得它們能夠兼容,MySQL 可能會執行隱式的數據類型轉換。例如,MySQL 在需要時會自動將字符串轉換為數字,反之亦然。
mysql> SELECT 1+'1';-> 2 mysql> SELECT CONCAT(2,' test');->?'2?test'我們也可使用?CAST()?函數將數字顯式轉換為字符串。CONCAT()?函數中的隱式類型轉換是因為它只能接收字符串類型的參數。
mysql> SELECT 38.8, CAST(38.8 AS CHAR);-> 38.8, '38.8' mysql> SELECT 38.8, CONCAT(38.8);->?38.8,?'38.8'以下是比較運算中的類型轉換規則:
如果任意一個參數為 NULL,比較的結果為 NULL,<=> 相等比較運算符除外。NULL <=> NULL 的運算結果為 true,不需要進行類型轉換。
如果兩個參數都是字符串,執行字符串比較。
如果兩個參數都是整數,執行整數比較。
如果不是和數字進行比較,十六進制數值將被看作二進制字符串。
如果一個參數是 TIMESTAMP 或者 DATETIME 字段,另一個參數是常量,該常量將會在比較之前轉換為時間戳類型。這一規則是為了更好地支持 ODBC 規范。IN() 運算符中的參數不會執行這一轉換。為了保險起見,記得在執行比較運算時使用完整的日期時間、日期或者時間字符串。例如,在使用 BTWEEN 運算符判斷日期或者時間數據時,利用 CAST() 函數將數據的類型顯示轉換成相應的類型。
返回單行結果的子查詢不會被當作常量。例如,當一個返回整數的子查詢和 DATETIME 數據進行比較時,DATETIME 將會被轉換為整數類型,而不會將子查詢的結果轉換為時間類型。如果想要執行日期時間比較,可以使用 CAST() 函數顯式將子查詢的結果轉換為 DATETIME 類型。
如果一個參數為精確數字類型(decimal),比較的方法取決于另一個參數的類型。如果另一個參數是精確數字或者整數類型,使用精確數字比較;如果另一個參數是浮點數類型,使用浮點數比較。
其他情況下,使用浮點數比較。例如,字符串和精確數字的比較使用浮點數比較方法。
關于時間類型之間的轉換規則,可以參考官方文檔。
以下示例演示了將字符串轉換為數字的比較操作:
mysql> SELECT 1 > '6x';-> 0 mysql> SELECT 7 > '6x';-> 1 mysql> SELECT 0 > 'x6';-> 0 mysql> SELECT 0 = 'x6';->?1如果將字符串類型的字段和數字進行比較,MySQL 無法使用該字段上的索引快速查找數據。例如,str_col 是一個索引字段,該索引無法用于以下語句:
SELECT?*?FROM?tbl_name?WHERE?str_col=1;問題的原因在于很多不同的字符串都可以轉換為數字 1,例如’1’、’ 1’ 或者 ‘1a’。
浮點數和 INTEGER 類型的超大數值之間的比較是近似比較,因為整數在比較之前需要轉換為雙精度浮點數,雙精度浮點數無法精確地表示所有的 64 位整數。例如,整數 253 + 1 無法使用浮點數進行表示,只能近似為 253 或者 253 + 2。
舉例來說,以下只有第一個比較運算中的兩個值相等,但是兩個比較運算都返回了 true(1):
mysql> SELECT '9223372036854775807' = 9223372036854775807;-> 1 mysql> SELECT '9223372036854775807' = 9223372036854775806;->?1字符串轉換為浮點數與整數轉換為浮點數的方式可能不同。整數可能使用 CPU 轉換為浮點數,而字符串可能使用浮點數乘法進行逐位轉換。另外,轉換結果可能受到各種因素的影響,例如計算機的架構、編譯器版本或者優化級別等。避免這種問題的方法之一就是使用 CAST() 函數,這樣數據就不會被隱式轉換為浮點數。
mysql> SELECT CAST('9223372036854775807' AS UNSIGNED) = 9223372036854775806;->?0關于浮點數比較的更多信息,可以參考官方文檔。
MySQL 服務器提供了一個轉換庫 dtoa,可以支持字符串或者 DECIMAL 數據和近似數字(FLOAT/DOUBLE)之間的基本轉換功能:
跨平臺的一致性轉換結果,例如,可以消除 Unix 和 Windows 之間的差異。
可以精確表示之前無法提供足夠精度的數據,例如接近 IEEE 限制的數據。
以盡可能高的精度將數字轉換成字符串格式。dtoa 的精度總是等于或者高于標準 C 代碼庫函數。
數字或者時間類型到字符串的隱式轉換結果的字符集和排序規則取決于 character_set_connection 和 collation_connection 系統變量。(這些變量通常使用 SET NAMES 進行設置。關于連接的字符集的信息,可以參考官方文檔。)
這意味著這種轉換的結果是一個非二進制的字符串(CHAR、VARCHAR 或者 LONGTEXT),除非連接字符集被設置為 binary。此時,轉換結果是一個二進制字符串(BINARY、VARBINARY 或者 LONGBLOB)。
對于整數類型的表達式,前文所述的表達式求值和表達式賦值有所不同。例如以下語句:
CREATE?TABLE?t?SELECT?integer_expr;這種情況下,表 t 的字段類型取決于整數表達式的長度,可能是 INT 或者 BIGINT。如果表達式的最大長度超過了 INT,使用 BIGINT 類型。這就意味著我們可以通過一個足夠長的表達式創建 BIGINT 類型的字段:
CREATE TABLE t SELECT 000000000000000000000 AS col;DESC t; Field|Type |Null|Key|Default|Extra| -----+------+----+---+-------+-----+ col??|bigint|NO??|???|0??????|?????|JSON 數據的比較分為兩種情況。第一層次的比較基于被比較數據的 JSON 類型,如果兩個類型不同,比較的結果取決于具有更高優先級的類型;如果兩個數據的 JSON 類型相同,使用具體的類型規則進行第二層次的比較。對于 JSON 和非 JSON 數據的比較,先將非 JSON 數據轉換為 JSON 類型,然后進行比較。詳細信息可以參考官方文檔。
作者簡介:不剪發的 Tony 老師,CSDN 博客專家,CSDN 學院簽約講師, GitChat 專欄作者。十余年數據庫管理與開發經驗。目前在一家全球性的游戲公司從事數據庫架構設計和開發工作,擅長各種數據庫管理與 SQL 開發,擁有Oracle OCP 和 Redhat RHCE 證書。
總結
以上是生活随笔為你收集整理的MySQL 避坑指南之隐式数据类型转换的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 如何用Chrome读懂网站监测Cooki
- 下一篇: CPU:别再拿我当搬砖工!