二者都有利弊吧,要从性能和易读易懂易维护各方面考虑,在什么场景下应该选哪种关联查询方式呢
从性能上看,关联表查询是为了应对大数据查询,减少查询的基数,灵活的配置主键等信息,提高查询的效率;
从维护上看,关联表查询从表名上更容易明白功能的分级,便于阅读和维护,并且模块划分越细,后期越容易扩展;
如果是程序开发初期,使用人群较少,访问量数据量比较少,可以使用单表查询;但是从长远上看,防止访问量飙升,数据量也飙升导致性能变慢的情况发生,应当使用关联表查询。
首先,建表
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 也好,他们的执行速度都是很快的,慢的是他们之间的通信,所以使用子查询的时候减少了他们之间通信的次数,同时需要发送的东西也少了,所以子查询会快一点。
sql运行机制和java运行机制不一样, sql语句只要配置合理,都是秒级的,甚至是毫秒级的,喜欢这个答案。只是看到有很多程序也存在使用第一种方法。不知道他们是怎么考虑的。既然mybatis可以那么方便的配置查询语句,貌似很多业务层都可以直接写到SQL语句里了,程序只是负责传参而已
啊,你说的对,是我把sql语句看错了。
再分析一次,在不考虑缓存的情况下:
先执行username in ("select username from users where enable = 0") 这是1000次,返回500的基数
500 * 1000 = 500000,确实次数已经超过第一种很多了。
但是sql运行机制和java运行机制不一样,如果要比速度,肯定sql要快,根据我的开发经验,java很少写高量级的循环语句。sql语句只要配置合理,都是秒级的,甚至是毫秒级的。所以肯定是第二种最棒,要不我写了这么多年前端,也不会来研究数据了……
我怎么觉得第二种不是1000+500 这么简单,select username from users where enable = 0数据库需要跑1000次,select amount from orders where status =“unpay”这里也是1000次,复合这一句and username in也是要*500次吧,难道我思路错了
我觉得肯定是关键表查询效率要高:
假设username和amount都是1000条数据,第一种查询得到500条结果,第二种也是500条结果。
第一种方式:除去查询效率,二维for循环效率极低,需要对比500*500=250000次,总次数是1000 +1000 + 250000.
第二种:相当于先查询username的500条组成一个新的表,然后从结果500条中查询符合第二个条件的结果。总次数等于1000+500。
这效率一对比,就什么都不用说了。