引出
考慮如下問題:假設(shè)我們?cè)贛ySQL中存在表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';
問:此時(shí)這兩條SQL都會(huì)走索引嗎?
答:第一條不會(huì),第二條會(huì)。
原因:
第一條:MySQL的隱式數(shù)據(jù)轉(zhuǎn)換導(dǎo)致name會(huì)轉(zhuǎn)換為浮點(diǎn)數(shù)然后和123的浮點(diǎn)數(shù)比較,這句話的意思就是希望找到表中name轉(zhuǎn)換為浮點(diǎn)數(shù)后是123的行記錄。
所以整個(gè)過程就是遍歷test的每條記錄,依次進(jìn)行類型轉(zhuǎn)換后與123比較。顯然不走索引。
第二條:MySQL的隱式數(shù)據(jù)轉(zhuǎn)換導(dǎo)致'12'會(huì)轉(zhuǎn)換為數(shù)字12,這句話的意思就是:在表中想要找到id為數(shù)字12的行。
則上述語句和如下語句等同:
select * from test where id = 12;
所以顯然是走索引的。
MySQL隱式數(shù)據(jù)轉(zhuǎn)換
下面引入一段大家在很多地方都可以看到的話,即官方文檔的翻譯版:
MySQL 的隱式類型轉(zhuǎn)換原則:
1. 兩個(gè)參數(shù)至少有一個(gè)是 NULL 時(shí),比較的結(jié)果也是 NULL,例外是使用 <=> 對(duì)兩個(gè) NULL 做比較時(shí)會(huì)返回 1,這兩種情況都不需要做類型轉(zhuǎn)換
2. 兩個(gè)參數(shù)都是字符串,會(huì)按照字符串來比較,不做類型轉(zhuǎn)換
3. 兩個(gè)參數(shù)都是整數(shù),按照整數(shù)來比較,不做類型轉(zhuǎn)換
4. 十六進(jìn)制的值和非數(shù)字做比較時(shí),會(huì)被當(dāng)做二進(jìn)制串,和數(shù)字做比較時(shí)會(huì)按下面的規(guī)則處理
5. 有一個(gè)參數(shù)是 TIMESTAMP 或 DATETIME,并且另外一個(gè)參數(shù)是常量,常量會(huì)被轉(zhuǎn)換為 timestamp
6. 有一個(gè)參數(shù)是 decimal 類型,如果另外一個(gè)參數(shù)是 decimal 或者整數(shù),會(huì)將整數(shù)轉(zhuǎn)換為 decimal 后進(jìn)行比較,如果另外一個(gè)參數(shù)是浮點(diǎn)數(shù),則會(huì)把 decimal 轉(zhuǎn)換為浮點(diǎn)數(shù)進(jìn)行比較
7. 所有其他情況下,兩個(gè)參數(shù)都會(huì)被轉(zhuǎn)換為浮點(diǎn)數(shù)再進(jìn)行比較
注意一個(gè)安全問題:假如 password 類型為字符串,查詢條件為 int 0 則會(huì)匹配上。
其實(shí)這7條都很好理解。
說一下最后的注意問題:
-
這句話的意思是任何不以數(shù)字開頭的字符串轉(zhuǎn)換為浮點(diǎn)數(shù)的時(shí)候,結(jié)果都是0。
舉個(gè)??:
image.png
可以看到where name = 0居然將所有行查出來了。所以說如果password傳入的是數(shù)字0,且SQL又沒有類型檢測的話,密碼就可以這樣被破解了。
-
上面的話沒有說完,如果以數(shù)字開頭呢?
image.png
可以看到這種轉(zhuǎn)換是取開頭的數(shù)字字符,如果沒有取到則直接輸出0。