開始之前,先問個問題問題:mysql 數據類型是date ,怎么寫查詢條件索引有效?
——下面帶著疑問看下去。
一、mysql-8.隱式轉換導致索引失效或查出不符合where條件結果
今天在執行一條sql語句時候,where條件寫錯了,但是居然查詢出來了結果。
如下圖所示:
第一反映是,mysql的優化器是不是做了什么操作,導致最后查詢條件不是現在的72071003dsss,而是變成了其它的值。因為之前在一篇文章看過,如果where條件兩邊一邊是數值類型,一邊是字符型mysql會做隱式轉換,但是具體是怎么轉換的,我就不是很了解。所以就先朝這個方向去查找結果。
首先去看下表結構,user_id字段果然是int類型,和查詢條件72071003dsss不是同一個類型。
通過查詢官方文檔得到一個答案:(對于今天問題的探討,重點請看最后一條)
當操作符與不同類型的操作數一起使用時,會發生類型轉換以使操作數兼容。某些轉換是隱式發生的。
- 如果一個或兩個參數均為NULL,則比較的結果為NULL,但NULL-safe <=> 相等> 比較運算符除外。對于NULL <=> NULL,結果為true。無需轉換。
- 如果比較操作中的兩個參數都是字符串,則將它們作為字符串進行比較。
- 如果兩個參數都是整數,則將它們作為整數進行比較。
- 如果不與數字比較,則將十六進制值視為二進制字符串。
- 如果參數之一是a TIMESTAMP或 DATETIMEcolumn,而另一個參數是常量,則在執行比較之前,該常量將轉換為時間戳。這樣做是為了使ODBC更友好。對于的參數,此操作未完成 IN()。為了安全起見,在進行比較時請始終使用完整的日期時間,日期或時間字符串。例如,要在BETWEEN與日期或時間值一起使用時獲得最佳結果 ,請使用CAST()將值顯式轉換為所需的數據類型。
- 一個或多個表中的單行子查詢不被視為常量。例如,如果子查詢返回要與DATETIME 值進行比較的整數,則比較將作為兩個整數進行。整數不轉換為時間值。要將操作數作為DATETIME值進行比較 ,請使用 CAST()將子查詢值顯式轉換為DATETIME。
- 如果參數之一是十進制值,則比較取決于另一個參數。如果另一個參數是十進制或整數值,則將參數作為十進制值進行比較;如果另一個參數是浮點值,則將參數作為浮點值進行比較。
在所有其他情況下,將參數作為浮點數(實數)進行比較。例如,將字符串和數字操作數進行比較,將其作為浮點數的比較。
1.1 隱式轉換導致查詢出不符合where條件的結果
如果查詢條件的由字符轉為浮點時候,又是什么樣的轉換規則呢?
- 不以數字開頭的字符串都將轉換為0。如‘abc’、‘a123bc’、‘abc123’都會轉化為0;
- 以數字開頭的字符串轉換時會進行截取,從第一個字符截取到第一個非數字內容為止。比如‘123abc’會轉換為123,‘012abc’會轉換為012也就是12,5.3a66b78c’會轉換為5.3,其他同理。
所以我們文章開始貼出來的sql:
SELECT * FROM A WHERE config_name='test111' AND user_id ='72071003dsss'
查詢條件’72071003dsss’被轉換成了72071003,那么最后的查詢語句就是:
SELECT * FROM A WHERE config_name='test111' AND user_id ='72071003'
結果: 由于觸發隱式轉換,將我們的字符型查詢條件按照一定規則轉換成了浮點性,變成了另一個值,而剛好這另一個值有匹配的結果。這就是為什么查詢出了不符合where條件的結果原因。
1.2 隱式轉換導致索引失效
- 當操作符左右兩邊的數據類型不一致時,會發生隱式轉換。
- 當where查詢操作符左邊為數值類型時發生了隱式轉換,那么對效率影響不大,但還是不推薦這么做。
select* from test1 where int_column= ‘10000’
對于這條sql的 int_column字段是整型左邊為int類型10000,轉換為浮點數還是10000,右邊字符串類型’10000’,轉換為浮點數也是10000。兩邊的轉換結果都是唯一確定的,所以不影響使用索引。
- 當where查詢操作符左邊為字符類型時發生了隱式轉換,那么會導致索引失效,造成全表掃描效率極低。
sselect* from test1 where str_column= 10000,
對于這條sql str_column是字符型左邊是字符串類型’10000’,轉浮點數為10000是唯一的,右邊int類型10000轉換結果也是唯一的。
但是,因為左邊是檢索條件,'10000’轉到10000雖然是唯一,但是其他字符串也可以轉換為10000,比如’10000a’,‘010000’,'10000’等等都能轉為浮點數10000,這樣的情況下,是不能用到索引的。
tips:隱式轉換有可能會導致索引失效,這個我們工作中需要注意的。當where查詢操作符左邊為字符類型時發生了隱式轉換,那么會導致索引失效,造成全表掃描效率極低。
上面便解答了文章開始最初的問題。接下來,我們來回到date類型的總結。
二、問題:mysql 數據類型是date ,怎么寫查詢條件索引有效?
2.1 走索引的方式:
- 直接范圍比較:
WHERE date_column >= '2025-07-01' AND date_column <= '2025-07-31'
使用比較運算符(>=/<=)直接匹配DATE類型,數據類型一致,可觸發索引?。
2?. BETWEEN操作符
WHERE date_column BETWEEN '2025-07-01' AND '2025-07-31'
功能等效于范圍比較,同樣支持索引優化?。
- 精確日期匹配
WHERE date_column = '2025-07-29'
等值查詢時,若數據類型完全匹配(DATE=DATE),可走索引?。
2.2 不會走索引的寫法
- 使用日期函數,這種顯式轉換
WHERE DATE(date_column) = '2025-07-29'
或 WHERE MONTH(date_column)=7
或 WHERE DATE_FORMAT(date_column, '%Y-%m')='2025-07'
函數轉換會使索引失效,導致全表掃描?
- 類型不一致的查詢,這種隱式轉換
WHERE date_column = 20250729(INT vs DATE)
數據類型不匹配時,有可能無法走索引。隱式轉換不一定必然導致索引失效,看情況