问答详情
源自:5-4 常用标签

关联表查询是单表查询出结果后再用程序过滤还是直接用SQL语句查询最终结果

二者都有利弊吧,要从性能和易读易懂易维护各方面考虑,在什么场景下应该选哪种关联查询方式呢

提问者:qq_双子星深蓝_0 2018-11-01 11:26

个回答

  • 热爱学习的好孩子
    2018-11-27 17:41:13
    已采纳

    从性能上看,关联表查询是为了应对大数据查询,减少查询的基数,灵活的配置主键等信息,提高查询的效率;

    从维护上看,关联表查询从表名上更容易明白功能的分级,便于阅读和维护,并且模块划分越细,后期越容易扩展;

    如果是程序开发初期,使用人群较少,访问量数据量比较少,可以使用单表查询;但是从长远上看,防止访问量飙升,数据量也飙升导致性能变慢的情况发生,应当使用关联表查询。

  • 慕无忌2563680
    2018-12-14 11:11:00

    首先,建表

    create table users (
      id int unsigned not null auto_increment,
      username char not null ,
      enable tinyint not null default 0,
      primary key (id)
    )ENGINE=InnoDB default charset=utf8;
    alter table users
      add key key_username_enable(username, enable);
    
    create table orders (
      id int unsigned not null auto_increment,
      username char not null ,
      status char(10) not null default "unpay",
      amount char(30) not null ,
      primary key (id)
    )ENGINE=InnoDB default charset=utf8;

    可以看到 我这里给 users 加的索引是 (username,enable) 而不是 (enable)。

    然后当我们用 explain 去分析你给的第一条 SQL 语句的时候,

    select amount 
      from orders 
    where 
      status="unpay" 
        and 
      username in (select username from users where enable = 0);

    就会发现 users 表上面是使用了 `key_username_enable` 的,然后这个时候,根据索引的最左匹配原则,可以知道, mysql 把 username 这个条件下推了,所以这个时候,MySQL 对于上面的 SQL 语句的执行应该是类似于以下方式

    select amount 
      from orders 
    where 
      status="unpay" 
        and 
      exists 
        (select orders.username from users 
          where enable=0 and users.username=orders.username);

    因此把它分成两部分来理解应该是

    # 1.
    select amount,username from orders where status='unpay';
    # 2. 
    select username from users where enable=0 and username=?; #这里的?代表上一层传过来的参数

    ------------------------------------------------ 分割线 -------------------------------------------------------------------------------------------

    好了, 其实上面的都是废话来的。

    如果你是分开执行上面那两个 SQL 语句,对于 MySQL 来说,他所需要的时间是差不多的,

    因为你如果是子查询,其实他也是先执行外部的找出一条数据,然后在进去子查询里面查询的,和在 Java 里面分开查询是一样的。

    但是问题在于,如果是使用了子查询的话,那么你需要一次进程间通信,即 Java 给 MySQL 发送一次请求,然后 MySQL 查询完成之后返回数据。 但是如果是分开两次查询的话,那么网络通信的次数就变成了 1+(第一次结果集次),对于 MySQL 也好,Java 也好,他们的执行速度都是很快的,慢的是他们之间的通信,所以使用子查询的时候减少了他们之间通信的次数,同时需要发送的东西也少了,所以子查询会快一点。

  • qq_双子星深蓝_0
    2018-12-14 09:51:26

    sql运行机制和java运行机制不一样, sql语句只要配置合理,都是秒级的,甚至是毫秒级的,喜欢这个答案。只是看到有很多程序也存在使用第一种方法。不知道他们是怎么考虑的。既然mybatis可以那么方便的配置查询语句,貌似很多业务层都可以直接写到SQL语句里了,程序只是负责传参而已

  • 热爱学习的好孩子
    2018-12-11 17:52:34

    啊,你说的对,是我把sql语句看错了。

    再分析一次,在不考虑缓存的情况下:

    先执行username in ("select username from users where enable = 0") 这是1000次,返回500的基数

    500 * 1000 = 500000,确实次数已经超过第一种很多了。

    但是sql运行机制和java运行机制不一样,如果要比速度,肯定sql要快,根据我的开发经验,java很少写高量级的循环语句。sql语句只要配置合理,都是秒级的,甚至是毫秒级的。所以肯定是第二种最棒,要不我写了这么多年前端,也不会来研究数据了……


  • qq_双子星深蓝_0
    2018-12-10 11:51:43

    我怎么觉得第二种不是1000+500 这么简单,select username from users where enable = 0数据库需要跑1000次,select amount from orders where status =“unpay”这里也是1000次,复合这一句and username in也是要*500次吧,难道我思路错了

  • 热爱学习的好孩子
    2018-12-07 18:25:43

    我觉得肯定是关键表查询效率要高:

    假设username和amount都是1000条数据,第一种查询得到500条结果,第二种也是500条结果。

    第一种方式:除去查询效率,二维for循环效率极低,需要对比500*500=250000次,总次数是1000 +1000 + 250000.

    第二种:相当于先查询username的500条组成一个新的表,然后从结果500条中查询符合第二个条件的结果。总次数等于1000+500。

    这效率一对比,就什么都不用说了。