引出
考慮如下問題:假設(shè)我們在MySQL中存在表test玻褪,結(jié)構(gòu)如下:
CREATE TABLE `test` (
`id` bigint(20),
`name` varchar(20),
PRIMARY KEY (`id`),
KEY `ix_name` (`name`)
);
則我們寫出如下SQL:
select * from test where name = 123;
select * from test where id = '12';
問:此時這兩條SQL都會走索引嗎闹获?
答:第一條不會杨赤,第二條會雹顺。
原因:
第一條:MySQL的隱式數(shù)據(jù)轉(zhuǎn)換導致name會轉(zhuǎn)換為浮點數(shù)然后和123的浮點數(shù)比較句葵,這句話的意思就是希望找到表中name轉(zhuǎn)換為浮點數(shù)后是123的行記錄厕鹃。
所以整個過程就是遍歷test的每條記錄,依次進行類型轉(zhuǎn)換后與123比較乍丈。顯然不走索引剂碴。
第二條:MySQL的隱式數(shù)據(jù)轉(zhuǎn)換導致'12'會轉(zhuǎn)換為數(shù)字12,這句話的意思就是:在表中想要找到id為數(shù)字12的行轻专。
則上述語句和如下語句等同:
select * from test where id = 12;
所以顯然是走索引的忆矛。
MySQL隱式數(shù)據(jù)轉(zhuǎn)換
下面引入一段大家在很多地方都可以看到的話,即官方文檔的翻譯版:
MySQL 的隱式類型轉(zhuǎn)換原則:
1. 兩個參數(shù)至少有一個是 NULL 時请垛,比較的結(jié)果也是 NULL催训,例外是使用 <=> 對兩個 NULL 做比較時會返回 1洽议,這兩種情況都不需要做類型轉(zhuǎn)換
2. 兩個參數(shù)都是字符串,會按照字符串來比較漫拭,不做類型轉(zhuǎn)換
3. 兩個參數(shù)都是整數(shù)绞铃,按照整數(shù)來比較,不做類型轉(zhuǎn)換
4. 十六進制的值和非數(shù)字做比較時嫂侍,會被當做二進制串儿捧,和數(shù)字做比較時會按下面的規(guī)則處理
5. 有一個參數(shù)是 TIMESTAMP 或 DATETIME,并且另外一個參數(shù)是常量挑宠,常量會被轉(zhuǎn)換為 timestamp
6. 有一個參數(shù)是 decimal 類型菲盾,如果另外一個參數(shù)是 decimal 或者整數(shù),會將整數(shù)轉(zhuǎn)換為 decimal 后進行比較各淀,如果另外一個參數(shù)是浮點數(shù)懒鉴,則會把 decimal 轉(zhuǎn)換為浮點數(shù)進行比較
7. 所有其他情況下,兩個參數(shù)都會被轉(zhuǎn)換為浮點數(shù)再進行比較
注意一個安全問題:假如 password 類型為字符串碎浇,查詢條件為 int 0 則會匹配上临谱。
其實這7條都很好理解。
說一下最后的注意問題:
-
這句話的意思是任何不以數(shù)字開頭的字符串轉(zhuǎn)換為浮點數(shù)的時候奴璃,結(jié)果都是0悉默。
舉個??:
可以看到where name = 0居然將所有行查出來了。所以說如果password傳入的是數(shù)字0苟穆,且SQL又沒有類型檢測的話抄课,密碼就可以這樣被破解了。
-
上面的話沒有說完雳旅,如果以數(shù)字開頭呢跟磨?
可以看到這種轉(zhuǎn)換是取開頭的數(shù)字字符,如果沒有取到則直接輸出0攒盈。