MySQL隐式数据转换

引出

考虑如下问题:假设我们在MySQL中存在表test,结构如下:

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的隐式数据转换导致name会转换为浮点数然后和123的浮点数比较,这句话的意思就是希望找到表中name转换为浮点数后是123的行记录。
所以整个过程就是遍历test的每条记录,依次进行类型转换后与123比较。显然不走索引。
第二条:MySQL的隐式数据转换导致'12'会转换为数字12,这句话的意思就是:在表中想要找到id为数字12的行。
则上述语句和如下语句等同:

select * from test where id = 12;

所以显然是走索引的。

MySQL隐式数据转换

下面引入一段大家在很多地方都可以看到的话,即官方文档的翻译版:

MySQL 的隐式类型转换原则:
1.  两个参数至少有一个是 NULL 时,比较的结果也是 NULL,例外是使用 <=> 对两个 NULL 做比较时会返回 1,这两种情况都不需要做类型转换
2.  两个参数都是字符串,会按照字符串来比较,不做类型转换
3.  两个参数都是整数,按照整数来比较,不做类型转换
4.  十六进制的值和非数字做比较时,会被当做二进制串,和数字做比较时会按下面的规则处理
5.  有一个参数是 TIMESTAMP 或 DATETIME,并且另外一个参数是常量,常量会被转换为 timestamp
6.  有一个参数是 decimal 类型,如果另外一个参数是 decimal 或者整数,会将整数转换为 decimal 后进行比较,如果另外一个参数是浮点数,则会把 decimal 转换为浮点数进行比较
7.  所有其他情况下,两个参数都会被转换为浮点数再进行比较

注意一个安全问题:假如 password 类型为字符串,查询条件为 int 0 则会匹配上。

其实这7条都很好理解。

说一下最后的注意问题:
  • 这句话的意思是任何不以数字开头的字符串转换为浮点数的时候,结果都是0。
    举个:


    image.png

    可以看到where name = 0居然将所有行查出来了。所以说如果password传入的是数字0,且SQL又没有类型检测的话,密码就可以这样被破解了。

  • 上面的话没有说完,如果以数字开头呢?


    image.png

    可以看到这种转换是取开头的数字字符,如果没有取到则直接输出0。

你可能感兴趣的:(MySQL隐式数据转换)