MySQL版本5.7.14(如果相关)。重写以避免任何混淆。这是一个非常简单/易于重现的问题。表格及其名称被混淆/概括。
CREATE TABLE `table1` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
AUTO_INCREMENT=5
;
CREATE TABLE `table2` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
AUTO_INCREMENT=5
;
CREATE TABLE `table1_table2` (
`id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
`table1_id` INT(10) UNSIGNED NULL DEFAULT NULL,
`table2_id` INT(10) UNSIGNED NULL DEFAULT NULL,
`created_at` TIMESTAMP NULL DEFAULT NULL,
`updated_at` TIMESTAMP NULL DEFAULT NULL,
PRIMARY KEY (`id`),
INDEX `table1_id_table1id` (`table1_id`),
INDEX `table2_id_table2id` (`table2_id`),
CONSTRAINT `table1_id_table1id` FOREIGN KEY (`table1_id`) REFERENCES `table1` (`id`) ON DELETE CASCADE,
CONSTRAINT `table2_id_table2id` FOREIGN KEY (`table2_id`) REFERENCES `table2` (`id`) ON DELETE CASCADE
)
COLLATE='utf8_unicode_ci'
ENGINE=InnoDB
AUTO_INCREMENT=7
;
INSERT INTO `table1` (`id`) VALUES
(1),
(2),
(3),
(4);
INSERT INTO `table2` (`id`) VALUES
(1),
(2),
(3),
(4);
INSERT INTO `table1_table2` (`id`, `table1_id`, `table2_id`, `created_at`, `updated_at`) VALUES
(1, 1, 3, '2016-12-28 14:47:40', '2016-12-28 14:47:41'),
(2, 1, 1, '2016-12-28 14:47:37', '2016-12-28 14:47:39'),
(3, 2, 3, '2016-12-28 14:59:48', '2016-12-28 14:59:48'),
(4, 3, 1, '2016-12-28 14:51:38', '2016-12-28 14:51:38'),
(5, 3, 2, '2016-12-28 14:52:33', '2016-12-28 14:52:34'),
(6, 3, 3, '2016-12-28 14:55:05', '2016-12-28 14:55:05'),
(7, 3, 4, '2016-12-28 14:56:48', '2016-12-28 14:56:48');
SELECT *
FROM `table1`
WHERE EXISTS (
SELECT *
FROM `table2`
INNER JOIN `table1_table2` ON `table2`.`id` = `table1_table2`.`table2_id`
WHERE `table1_table2`.`table1_id` = `table1`.`id`
AND `table2`.`id` = 3)
AND `table1`.`id` = 3;
请注意,未找到任何结果。
组合(table1
,table2
)(1,3),(3,3)在组合时不会返回结果(table1
,table2
)( 1,1),(2,3),(3,1),(3,2),(3,4)确实返回。
ALTER TABLE `table1_table2`
DROP COLUMN `id`;
SELECT *
FROM `table1`
WHERE EXISTS (
SELECT *
FROM `table2`
INNER JOIN `table1_table2` ON `table2`.`id` = `table1_table2`.`table2_id`
WHERE `table1_table2`.`table1_id` = `table1`.`id`
AND `table2`.`id` = 3)
AND `table1`.`id` = 3;
注意结果。
在此查询上运行EXPLAIN
时,会收到一条警告
Note: Field or reference 'table1.id' of SELECT #2 was resolved in
SELECT #1 Note:/* select#1 */ select '3' AS `id` from `table1` where
(exists(/*select#2 */ select 1 from `table2` join `table1_table2` where ((`table1_table2`.`table2_id` = 3) and (`table1_table2`.`table1_id` = '3'))))
请注意,警告将table1_id
包装在引号中(表明它将其视为字符串)。
我不确定这是一个错误还是我做错了。
答案 0 :(得分:0)
我相信mysql对列引用感到困惑。
我相信如果你改变
会有效SELECT * FROM table1 ...... WHERE table1.id = 3
使用别名:
SELECT * FROM table1 t1 ...... WHERE t1.id = 3
最好在查询中始终对表引用使用别名,以提高可读性并避免混淆。
顺便说一句,查询可以用以下更简单的方式编写。
SELECT t1.* FROM table1 t1
JOIN table1_table2 rel
ON t1.id = rel.table1_id
WHERE t1.id = 3 AND rel.table2_id = 3
如果没有重复的关系,它将没有区别,但这里使用的是EXISTS。
SELECT * FROM table1 t1
WHERE EXISTS (
SELECT 0 FROM table1_table2 rel
WHERE t1.id = rel.table1_id AND rel.table2_id = 3)
AND t1.id = 3
答案 1 :(得分:0)
似乎外键正在考虑MySQL / InnoDB中的索引优化。如果您对查询运行说明,则只有在使用失败的值时,才会注意到它使用index_merge
类型的最后DEPENDENT SUBQUERY
。使用其他值时,类型为ref
。我不知道查询根据值切换的原因,但它显然使结果失败。将IGNORE INDEX(table1_id_table1id)
添加到联接会使查询使用ref
并返回正确的结果。
SELECT *
FROM `table1` t1
WHERE EXISTS (
SELECT *
FROM `table2`
INNER JOIN `table1_table2` IGNORE INDEX(table1_id_table1id) ON `table2`.`id` = `table1_table2`.`table2_id`
WHERE `table1_table2`.`table1_id` = `t1`.`id`
AND `table2`.`id` = 3)
AND `t1`.`id` = 3;
如果有人对MySQL引擎做出这些决定的原因或者究竟发生了什么有更深入的了解,请解释/发布您自己的答案。