【发布时间】:2021-03-29 04:49:56
【问题描述】:
我有 2 张桌子,我需要像这样INNER JOIN on 2 equal columns:
SELECT
`table1`.*
FROM
`table1`
INNER JOIN `table2`
ON `table2`.`type` = `table1`.`type`
AND `table2`.`num` = `table1`.`num`
WHERE
`table2`.`another_int` = 1
ORDER BY
`table1`.`id` DESC
LIMIT 10 OFFSET 0
当我尝试时,查询需要 1500ms
但删除 2 个 JOIN 条件中的任何一个,或删除 ORDER BY 都会导致查询在 1ms
更多信息:
-
表 2 有 1500 行,表 1 有 ~400,000 行
-
type 和 num 列都在两个表和
id上都建立了索引 表 1 上的(排序依据)也是主要的,因此已编入索引。 -
type:两个表上的 ENUM 具有完全相同的选项(6 个枚举选项) -
num:两个表上的无符号大整数
使用EXPLAIN:
两个表都使用键,但 Extra 列显示表 2:“使用索引;使用临时;使用文件排序”
删除 WHERE 条件或 LIMIT OFFSET 没有效果,但我刚刚注意到,删除 ORDER BY 同时保持 LIMIT 会导致查询在不到 1 毫秒内运行
不知道这里出了什么问题或者我应该怎么做,所以任何帮助都非常感谢......
编辑
/* Table 1 Keys */
KEY `posts_user_id_foreign` (`user_id`),
KEY `posts_composite_ind` (`post_followable_type`,`post_followable_id`,`id`) USING BTREE,
CONSTRAINT `posts_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=456501 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/* Table 2 Keys */
PRIMARY KEY (`id`),
UNIQUE KEY `unique_user_follows` (`user_id`,`followable_type`,`followable_id`),
CONSTRAINT `follows_user_id_foreign` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`) ON DELETE CASCADE
) ENGINE=InnoDB AUTO_INCREMENT=1525 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
/* Query */
SELECT
`posts`.*, `follows`.`user_id` AS `current_user`
FROM
`posts`
INNER JOIN `follows`
ON `follows`.`followable_type` = `post_followable_type`
AND `follows`.`followable_id` = `post_followable_id`
WHERE
`follows`.`user_id` = 1
ORDER BY
`posts`.`id` DESC
LIMIT 11 OFFSET 0
/* Explain */
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
|----|-------------|---------|------------|------|---------------------|---------------------|---------|-----------------------------------------------------|------|----------|----------------------------------------------|
| 1 | SIMPLE | follows | | ref | unique_user_follows | unique_user_follows | 8 | const | 511 | 100.00 | Using index; Using temporary; Using filesort |
| 1 | SIMPLE | posts | | ref | posts_composite_ind | posts_composite_ind | 9 | db.follows.followable_type,db.follows.followable_id | 453 | 100.00 | |
/* End */
【问题讨论】:
-
通过
(type, num, id)在table1和(another_int, type, num)在table2创建索引。 -
Mmm... 尝试添加 STRAIGHT_JOIN 以修复表格扫描订单帖子->关注。
-
如果你不能使用 STRAIGHT_JOIN 那么你可以尝试使用 LEFT JOIN 而不是 INNER (它也修复了表扫描顺序),你的 WHERE 会隐式地将你的连接类型转换为 INNER,所以输出不会改变。
-
请勿使用
STRAIGHT_JOIN。它很好地唤醒了当前值。对于其他一些值,它可能会非常缓慢! -
@Akina -
LEFT JOIN变成INNER JOIN当优化器可以看到它们是等价的。做EXPLAIN SELECT ... ; SHOW WARNINGS;看“证明”。同时,LEFT本身并不强制表顺序。
标签: mysql