对于 mysql 的关联查询,应该直接使用 join 一步到位,还是拆分成两次查表?

需求:获取某个人的粉丝信息

相关表结构:

CREATE TABLE `auth_user` (
  `id` int NOT NULL AUTO_INCREMENT,
  `password` varchar(128) NOT NULL,
  `last_login` datetime(6) DEFAULT NULL,
  `is_superuser` tinyint(1) NOT NULL,
  `username` varchar(150) NOT NULL,
  `first_name` varchar(150) NOT NULL,
  `last_name` varchar(150) NOT NULL,
  `email` varchar(254) NOT NULL,
  `is_staff` tinyint(1) NOT NULL,
  `is_active` tinyint(1) NOT NULL,
  `date_joined` datetime(6) NOT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `username` (`username`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `friendships_friendship` (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `created_at` datetime(6) NOT NULL,
  `from_user_id` int DEFAULT NULL,
  `to_user_id` int DEFAULT NULL,
  PRIMARY KEY (`id`),
  UNIQUE KEY `friendships_friendship_from_user_id_to_user_id_c3116feb_uniq` (`from_user_id`,`to_user_id`),
  KEY `friendships_friendship_to_user_id_986baf39_fk_auth_user_id` (`to_user_id`),
  CONSTRAINT `friendships_friendship_from_user_id_ca1edd7e_fk_auth_user_id` FOREIGN KEY (`from_user_id`) REFERENCES `auth_user` (`id`),
  CONSTRAINT `friendships_friendship_to_user_id_986baf39_fk_auth_user_id` FOREIGN KEY (`to_user_id`) REFERENCES `auth_user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;

下面两种 sql 哪种效率更高?

方式一:

SELECT
    `friendships_friendship`.`id`,
    `friendships_friendship`.`from_user_id`,
    `friendships_friendship`.`to_user_id`,
    `friendships_friendship`.`created_at`,
    T3.`id`,
    T3.`password`,
    T3.`last_login`,
    T3.`is_superuser`,
    T3.`username`,
    T3.`first_name`,
    T3.`last_name`,
    T3.`email`,
    T3.`is_staff`,
    T3.`is_active`,
    T3.`date_joined`
FROM
    `friendships_friendship`
    LEFT OUTER JOIN `auth_user` T3 ON (
        `friendships_friendship`.`from_user_id` = T3.`id`
    )
WHERE
    `friendships_friendship`.`to_user_id` = 1
LIMIT
    21;

方式二:

先获取所有 id

SELECT
    `friendships_friendship`.`id`,
    `friendships_friendship`.`from_user_id`,
    `friendships_friendship`.`to_user_id`,
    `friendships_friendship`.`created_at`
FROM
    `friendships_friendship`
WHERE
    `friendships_friendship`.`to_user_id` = 1
LIMIT
    21;

把这些 id 作为第二次查询的 from_user_id 使用 in 操作符 select

SELECT
    T3.`id`,
    T3.`password`,
    T3.`last_login`,
    T3.`is_superuser`,
    T3.`username`,
    T3.`first_name`,
    T3.`last_name`,
    T3.`email`,
    T3.`is_staff`,
    T3.`is_active`,
    T3.`date_joined`
FROM
    `auth_user` T3
WHERE
    T3.`from_user_id` in (xxxx, xxx, xxxx)
LIMIT
    21;

mysql 对于有 join 的 select 是先执行 where 还是 join?

比如对于方式一,mysql 是先 LEFT OUTER JOIN auth_user T3 ON (friendships_friendship.from_user_id = T3.id) 还是先 friendships_friendship.to_user_id = 1 ?


我的表都是空的

执行计划:

图片.png

阅读 2.4k
2 个回答

第一种方式效率会更高,毕竟只执行了一次sql,虽然使用了join操作,但只会将满足条件的记录进行join操作,跟第二种方式使用in不会有太大差别。
对于方式一,先会找到to_user_id=1的数据,再进行join操作

对于方式一,mysql 是先执行:

LEFT OUTER JOIN auth_user T3 ON (friendships_friendship.from_user_id = T3.id)

至于效率,少量数据的情况下感觉区别不大
数据大的情况下:
方式一的代价主要是表连接,要查两次表,数据大的情况下连接会很慢,且理解成本相对方式二要高一些
方式二的代价主要是拆开查询,查询两次,连接的代价和数据存储的代价要大,而且IN的范围特别大的情况下会不走索引走全表扫描
具体还是造数据跑一下试试就知道了,希望能帮到你

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏