前言:在技术群看到有人分享了一个bug:
SELECT * FROM student WHERE id IN (SELECT id FROM course),这个sql语句,course没有id列,会返回student所有行。
看到这条语句的时候,我第一反应就是如果course没有id列,那么执行的时候应该会报错才对,怎么可能执行成功呢?于是做了一个实验
CREATE TABLE `t1_0807` (
`id` int NOT NULL AUTO_INCREMENT,
`a` int DEFAULT NULL,
b varchar(10),
PRIMARY KEY (`id`),
KEY `idx_a` (`a`)
) ENGINE=InnoDB;
CREATE TABLE `t2_0807` (
`id` int NOT NULL AUTO_INCREMENT,
`c` int DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
insert into t1_0807(a,b) values (1,'one'),(2,'two'),(3,NULL);
insert into t2_0807(c) values (1),(2),(3);
select * from t1_0807 where b in (select b from t2_0807);
没想到真的执行成功了,是什么原因导致的呢?第一反应就是sql被mysql改写了,我们看下改写后的sql是怎么样的
select /*1*/ select `martin`.`t1_0807`.`id` AS `id`, `martin`.`t1_0807`.`a` AS `a`, `martin`.`t1_0807`.`b` AS `b` from `martin`.`t1_0807` semi join (`martin`.`t2_0807`) where (`martin`.`t1_0807`.`b` = `martin`.`t1_0807`.`b`)
我们可以看到,where条件里的b字段是t1_0807表的,也就是说,当找不到t2_0807的字段,那么就会转成t1_0807的。为什么会这样子呢?我们接着往下探索。
在 MySQL 中,LATERAL 关键字用于派生表(derived tables),允许派生表中的查询引用包含该派生表的 FROM 子句中的其他表。这种特性使得每一行都可以使用外部查询的列,从而进行更复杂的计算或数据操作。
注意的是,这里不止只有mysql有这个bug,pg和oracle也有。要解决这个bug也很简单,给表写上别名就可以了。
select * from t1_0807 b where b.b in (select a.b from t2_0807 a);
评论