文章目录

  • MySQL连接查询及原理
    • 1、本文内容
    • 2、准备数据
    • 3、笛卡尔积
      • sql中笛卡尔积语法
    • 4、内连接
      • 示例1:有连接条件
      • 示例2:无连接条件
      • 示例3:组合条件进行查询
      • 总结
    • 5、外连接
      • 左连接
      • 右连接
    • 6、理解表连接原理
      • 示例1:内连接
      • 示例2:左连接
    • 7、java代码实现连接查询
    • 8、java代码改进版本
    • 8、扩展

MySQL连接查询及原理

1、本文内容

笛卡尔积

内连接

外连接

左连接

右连接

表连接的原理

使用java实现连接查询,加深理解

2、准备数据

2张表:

t_team:组表。

t_employee:员工表,内部有个team_id引用组表的id。

drop table if exists t_team;
create table t_team(id int not null AUTO_INCREMENT PRIMARY KEY comment '组id',team_name varchar(32) not null default '' comment '名称'
) comment '组表';drop table if exists t_employee;
create table t_employee(id int not null AUTO_INCREMENT PRIMARY KEY comment '部门id',emp_name varchar(32) not null default '' comment '员工名称',team_id int not null default 0 comment '员工所在组id'
) comment '员工表表';insert into t_team values (1,'架构组'),(2,'测试组'),(3,'java组'),(4,'前端组');
insert into t_employee values (1,'路人甲Java',1),(2,'张三',2),(3,'李四',3),(4,'王五',0),(5,'赵六',0);

t_team表4条记录,如下:

mysql> select * from t_team;
+----+-----------+
| id | team_name |
+----+-----------+
|  1 | 架构组   |
|  2 | 测试组   |
|  3 | java组   |
|  4 | 前端组   |
+----+-----------+
4 rows in set (0.00 sec)

t_employee表5条记录,如下:

mysql> select * from t_employee;
+----+---------------+---------+
| id | emp_name    | team_id |
+----+---------------+---------+
|  1 | 路人甲Java   |    1 |
|  2 | 张三      |    2 |
|  3 | 李四      |    3 |
|  4 | 王五      |    0 |
|  5 | 赵六      |    0 |
+----+---------------+---------+
5 rows in set (0.00 sec)

3、笛卡尔积

介绍连接查询之前,我们需要先了解一下笛卡尔积。

笛卡尔积简单点理解:有两个集合A和B,笛卡尔积表示A集合中的元素和B集合中的元素任意相互关联产生的所有可能的结果。

假如A中有m个元素,B中有n个元素,A、B笛卡尔积产生的结果有m*n个结果,相当于循环遍历两个集合中的元素,任意组合。

java伪代码表示如下:

for(Object eleA : A){for(Object eleB : B){System.out.print(eleA+","+eleB);}
}

过程:拿A集合中的第1行,去匹配集合B中所有的行,然后再拿集合A中的第2行,去匹配集合B中所有的行,最后结果数量为m*n。

sql中笛卡尔积语法

select 字段 from 表1,表2[,表N];
或者
select 字段 from 表1 join 表2 [join 表N];

示例:

mysql> select * from t_team,t_employee;
+----+-----------+----+---------------+---------+
| id | team_name | id | emp_name    | team_id |
+----+-----------+----+---------------+---------+
|  1 | 架构组   |  1 | 路人甲Java   |    1 |
|  2 | 测试组   |  1 | 路人甲Java   |    1 |
|  3 | java组   |  1 | 路人甲Java   |    1 |
|  4 | 前端组   |  1 | 路人甲Java   |    1 |
|  1 | 架构组   |  2 | 张三      |    2 |
|  2 | 测试组   |  2 | 张三      |    2 |
|  3 | java组   |  2 | 张三      |    2 |
|  4 | 前端组   |  2 | 张三      |    2 |
|  1 | 架构组   |  3 | 李四      |    3 |
|  2 | 测试组   |  3 | 李四      |    3 |
|  3 | java组   |  3 | 李四      |    3 |
|  4 | 前端组   |  3 | 李四      |    3 |
|  1 | 架构组   |  4 | 王五      |    0 |
|  2 | 测试组   |  4 | 王五      |    0 |
|  3 | java组   |  4 | 王五      |    0 |
|  4 | 前端组   |  4 | 王五      |    0 |
|  1 | 架构组   |  5 | 赵六      |    0 |
|  2 | 测试组   |  5 | 赵六      |    0 |
|  3 | java组   |  5 | 赵六      |    0 |
|  4 | 前端组   |  5 | 赵六      |    0 |
+----+-----------+----+---------------+---------+
20 rows in set (0.00 sec)

t_team表4条记录,t_employee表5条记录,笛卡尔积结果输出了20行记录。

4、内连接

语法:

select 字段 from 表1 inner join 表2 on 连接条件;
或
select 字段 from 表1 join 表2 on 连接条件;
或
select 字段 from 表1, 表2 [where 关联条件];

内连接相当于在笛卡尔积的基础上加上了连接的条件。

当没有连接条件的时候,内连接上升为笛卡尔积。

过程用java伪代码如下:

for(Object eleA : A){for(Object eleB : B){if(连接条件是否为true){System.out.print(eleA+","+eleB);}}
}

示例1:有连接条件

查询员工及所属部门

mysql> select t1.emp_name,t2.team_name from t_employee t1 inner join t_team t2 on t1.team_id = t2.id;
+---------------+-----------+
| emp_name    | team_name |
+---------------+-----------+
| 路人甲Java   | 架构组   |
| 张三      | 测试组   |
| 李四      | java组   |
+---------------+-----------+
3 rows in set (0.00 sec)mysql> select t1.emp_name,t2.team_name from t_employee t1 join t_team t2 on t1.team_id = t2.id;
+---------------+-----------+
| emp_name    | team_name |
+---------------+-----------+
| 路人甲Java   | 架构组   |
| 张三      | 测试组   |
| 李四      | java组   |
+---------------+-----------+
3 rows in set (0.00 sec)mysql> select t1.emp_name,t2.team_name from t_employee t1, t_team t2 where t1.team_id = t2.id;
+---------------+-----------+
| emp_name    | team_name |
+---------------+-----------+
| 路人甲Java   | 架构组   |
| 张三      | 测试组   |
| 李四      | java组   |
+---------------+-----------+
3 rows in set (0.00 sec)

上面相当于获取了2个表的交集,查询出了两个表都有的数据。

示例2:无连接条件

无条件内连接,上升为笛卡尔积,如下:

mysql> select t1.emp_name,t2.team_name from t_employee t1 inner join t_team t2;
+---------------+-----------+
| emp_name    | team_name |
+---------------+-----------+
| 路人甲Java   | 架构组   |
| 路人甲Java   | 测试组   |
| 路人甲Java   | java组   |
| 路人甲Java   | 前端组   |
| 张三      | 架构组   |
| 张三      | 测试组   |
| 张三      | java组   |
| 张三      | 前端组   |
| 李四      | 架构组   |
| 李四      | 测试组   |
| 李四      | java组   |
| 李四      | 前端组   |
| 王五      | 架构组   |
| 王五      | 测试组   |
| 王五      | java组   |
| 王五      | 前端组   |
| 赵六      | 架构组   |
| 赵六      | 测试组   |
| 赵六      | java组   |
| 赵六      | 前端组   |
+---------------+-----------+
20 rows in set (0.00 sec)

示例3:组合条件进行查询

查询架构组的员工,3种写法

mysql> select t1.emp_name,t2.team_name from t_employee t1 inner join t_team t2 on t1.team_id = t2.id and t2.team_name = '架构组';
+---------------+-----------+
| emp_name    | team_name |
+---------------+-----------+
| 路人甲Java   | 架构组   |
+---------------+-----------+
1 row in set (0.00 sec)mysql> select t1.emp_name,t2.team_name from t_employee t1 inner join t_team t2 on t1.team_id = t2.id where t2.team_name = '架构组';
+---------------+-----------+
| emp_name    | team_name |
+---------------+-----------+
| 路人甲Java   | 架构组   |
+---------------+-----------+
1 row in set (0.00 sec)mysql> select t1.emp_name,t2.team_name from t_employee t1, t_team t2 where t1.team_id = t2.id and t2.team_name = '架构组';
+---------------+-----------+
| emp_name    | team_name |
+---------------+-----------+
| 路人甲Java   | 架构组   |
+---------------+-----------+
1 row in set (0.00 sec)

上面3中方式解说:

  • 方式1:on中使用了组合条件。(select 字段 from 表1 inner join 表2 on 连接条件 and 条件)
  • 方式2:在连接的结果之后再进行过滤,相当于先获取连接的结果,然后使用where中的条件再对连接结果进行过滤。(select 字段 from 表1 inner join 表2 on 连接条件 where 关联条件)
  • 方式3:直接在where后面进行过滤。(select 字段 from 表1,表2 where 关联条件 and 条件)

总结

内连接建议使用第3种语法,简洁:(但用的多的还是: 表1inner join表2 on 连接条件)

select 字段 from 表1, 表2 [where 关联条件];

5、外连接

外连接涉及到2个表,分为:主表和从表,要查询的信息主要来自于哪个表,谁就是主表。

外连接查询结果为主表中所有记录。如果从表中有和它匹配的,则显示匹配的值,这部分相当于内连接查询出来的结果;如果从表中没有和它匹配的,则显示null。

最终:外连接查询结果 = 内连接的结果 + 主表中有的而内连接结果中没有的记录。

外连接分为2种:

  • 左外链接:使用left join关键字,left join左边的是主表。
  • 右外连接:使用right join关键字,right join右边的是主表。

左连接

语法:

select 列 from 主表 left join 从表 on 连接条件;

示例1:

查询所有员工信息,并显示员工所在组,如下:

mysql> SELECTt1.emp_name,t2.team_nameFROMt_employee t1LEFT JOINt_team t2ONt1.team_id = t2.id;
+---------------+-----------+
| emp_name    | team_name |
+---------------+-----------+
| 路人甲Java   | 架构组   |
| 张三      | 测试组   |
| 李四      | java组   |
| 王五      | NULL    |
| 赵六      | NULL    |
+---------------+-----------+
5 rows in set (0.00 sec)

上面查询出了所有员工,员工team_id=0的,team_name为NULL。

示例2:

查询员工姓名、组名,返回组名不为空的记录,如下:

mysql> SELECTt1.emp_name,t2.team_nameFROMt_employee t1LEFT JOINt_team t2ONt1.team_id = t2.idWHEREt2.team_name IS NOT NULL;
+---------------+-----------+
| emp_name    | team_name |
+---------------+-----------+
| 路人甲Java   | 架构组   |
| 张三      | 测试组   |
| 李四      | java组   |
+---------------+-----------+
3 rows in set (0.00 sec)

上面先使用内连接获取连接结果,然后再使用where对连接结果进行过滤。

右连接

语法:

select 列 from 从表 right join 主表 on 连接条件;

示例:

我们使用右连接来实现上面左连接实现的功能,如下:

mysql> SELECTt2.team_name,t1.emp_nameFROMt_team t2RIGHT JOINt_employee t1ONt1.team_id = t2.id;
+-----------+---------------+
| team_name | emp_name    |
+-----------+---------------+
| 架构组   | 路人甲Java   |
| 测试组   | 张三      |
| java组   | 李四      |
| NULL    | 王五      |
| NULL    | 赵六      |
+-----------+---------------+
5 rows in set (0.00 sec)mysql> SELECTt2.team_name,t1.emp_nameFROMt_team t2RIGHT JOINt_employee t1ONt1.team_id = t2.idWHEREt2.team_name IS NOT NULL;
+-----------+---------------+
| team_name | emp_name    |
+-----------+---------------+
| 架构组   | 路人甲Java   |
| 测试组   | 张三      |
| java组   | 李四      |
+-----------+---------------+
3 rows in set (0.00 sec)

6、理解表连接原理

准备数据:

drop table if exists test1;
create table test1(a int
);
drop table if exists test2;
create table test2(b int
);
insert into test1 values (1),(2),(3);
insert into test2 values (3),(4),(5);
mysql> select * from test1;
+------+
| a    |
+------+
|    1 |
|    2 |
|    3 |
+------+
3 rows in set (0.00 sec)mysql> select * from test2;
+------+
| b    |
+------+
|    3 |
|    4 |
|    5 |
+------+
3 rows in set (0.00 sec)

我们来写几个连接,看看效果。

示例1:内连接

mysql> select * from test1 t1,test2 t2;
+------+------+
| a   | b   |
+------+------+
|   1 |   3 |
|   2 |   3 |
|   3 |   3 |
|   1 |   4 |
|   2 |   4 |
|   3 |   4 |
|   1 |   5 |
|   2 |   5 |
|   3 |   5 |
+------+------+
9 rows in set (0.00 sec)mysql> select * from test1 t1,test2 t2 where t1.a = t2.b;
+------+------+
| a   | b   |
+------+------+
|   3 |   3 |
+------+------+
1 row in set (0.00 sec)

9条数据正常。

示例2:左连接

mysql> select * from test1 t1 left join test2 t2 on t1.a = t2.b;
+------+------+
| a   | b   |
+------+------+
|   3 |   3 |
|   1 | NULL |
|   2 | NULL |
+------+------+
3 rows in set (0.00 sec)mysql> select * from test1 t1 left join test2 t2 on t1.a>10;
+------+------+
| a   | b   |
+------+------+
|   1 | NULL |
|   2 | NULL |
|   3 | NULL |
+------+------+
3 rows in set (0.00 sec)mysql> select * from test1 t1 left join test2 t2 on 1=1;
+------+------+
| a   | b   |
+------+------+
|   1 |   3 |
|   2 |   3 |
|   3 |   3 |
|   1 |   4 |
|   2 |   4 |
|   3 |   4 |
|   1 |   5 |
|   2 |   5 |
|   3 |   5 |
+------+------+
9 rows in set (0.00 sec)

上面的左连接第一个好理解。

第2个sql连接条件t1.a>10,这个条件只关联了test1表,再看看结果,是否可以理解?不理解的继续向下看,我们用java代码来实现连接查询。

第3个sql中的连接条件1=1值为true,返回结果为笛卡尔积。

7、java代码实现连接查询

下面是一个简略版的实现

package com.itsoku.sql;import org.junit.Test;import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;public class Test1 {public static class Table1 {int a;public int getA() {return a;}public void setA(int a) {this.a = a;}public Table1(int a) {this.a = a;}@Overridepublic String toString() {return "Table1{" +"a=" + a +'}';}public static Table1 build(int a) {return new Table1(a);}}public static class Table2 {int b;public int getB() {return b;}public void setB(int b) {this.b = b;}public Table2(int b) {this.b = b;}public static Table2 build(int b) {return new Table2(b);}@Overridepublic String toString() {return "Table2{" +"b=" + b +'}';}}public static class Record<R1, R2> {R1 r1;R2 r2;public R1 getR1() {return r1;}public void setR1(R1 r1) {this.r1 = r1;}public R2 getR2() {return r2;}public void setR2(R2 r2) {this.r2 = r2;}public Record(R1 r1, R2 r2) {this.r1 = r1;this.r2 = r2;}@Overridepublic String toString() {return "Record{" +"r1=" + r1 +", r2=" + r2 +'}';}public static <R1, R2> Record<R1, R2> build(R1 r1, R2 r2) {return new Record(r1, r2);}}public static enum JoinType {innerJoin, leftJoin}public static interface Filter<R1, R2> {boolean accept(R1 r1, R2 r2);}public static <R1, R2> List<Record<R1, R2>> join(List<R1> table1, List<R2> table2, JoinType joinType, Filter<R1, R2> onFilter, Filter<R1, R2> whereFilter) {if (Objects.isNull(table1) || Objects.isNull(table2) || joinType == null) {return new ArrayList<>();}List<Record<R1, R2>> result = new CopyOnWriteArrayList<>();for (R1 r1 : table1) {List<Record<R1, R2>> onceJoinResult = joinOn(r1, table2, onFilter);result.addAll(onceJoinResult);}if (joinType == JoinType.leftJoin) {List<R1> r1Record = result.stream().map(Record::getR1).collect(Collectors.toList());List<Record<R1, R2>> leftAppendList = new ArrayList<>();for (R1 r1 : table1) {if (!r1Record.contains(r1)) {leftAppendList.add(Record.build(r1, null));}}result.addAll(leftAppendList);}if (Objects.nonNull(whereFilter)) {for (Record<R1, R2> record : result) {if (!whereFilter.accept(record.r1, record.r2)) {result.remove(record);}}}return result;}public static <R1, R2> List<Record<R1, R2>> joinOn(R1 r1, List<R2> table2, Filter<R1, R2> onFilter) {List<Record<R1, R2>> result = new ArrayList<>();for (R2 r2 : table2) {if (Objects.nonNull(onFilter) ? onFilter.accept(r1, r2) : true) {result.add(Record.build(r1, r2));}}return result;}@Testpublic void innerJoin() {List<Table1> table1 = Arrays.asList(Table1.build(1), Table1.build(2), Table1.build(3));List<Table2> table2 = Arrays.asList(Table2.build(3), Table2.build(4), Table2.build(5));join(table1, table2, JoinType.innerJoin, null, null).forEach(System.out::println);System.out.println("-----------------");join(table1, table2, JoinType.innerJoin, (r1, r2) -> r1.a == r2.b, null).forEach(System.out::println);}@Testpublic void leftJoin() {List<Table1> table1 = Arrays.asList(Table1.build(1), Table1.build(2), Table1.build(3));List<Table2> table2 = Arrays.asList(Table2.build(3), Table2.build(4), Table2.build(5));join(table1, table2, JoinType.leftJoin, (r1, r2) -> r1.a == r2.b, null).forEach(System.out::println);System.out.println("-----------------");join(table1, table2, JoinType.leftJoin, (r1, r2) -> r1.a > 10, null).forEach(System.out::println);}}

代码中的 innerJoin() 方法模拟了下面的sql:

mysql> select * from test1 t1,test2 t2;
+------+------+
| a    | b    |
+------+------+
|    1 |    3 |
|    2 |    3 |
|    3 |    3 |
|    1 |    4 |
|    2 |    4 |
|    3 |    4 |
|    1 |    5 |
|    2 |    5 |
|    3 |    5 |
+------+------+
9 rows in set (0.00 sec)mysql> select * from test1 t1,test2 t2 where t1.a = t2.b;
+------+------+
| a    | b    |
+------+------+
|    3 |    3 |
+------+------+
1 row in set (0.00 sec)

运行一下innerJoin()输出如下:

Record{r1=Table1{a=1}, r2=Table2{b=3}}
Record{r1=Table1{a=1}, r2=Table2{b=4}}
Record{r1=Table1{a=1}, r2=Table2{b=5}}
Record{r1=Table1{a=2}, r2=Table2{b=3}}
Record{r1=Table1{a=2}, r2=Table2{b=4}}
Record{r1=Table1{a=2}, r2=Table2{b=5}}
Record{r1=Table1{a=3}, r2=Table2{b=3}}
Record{r1=Table1{a=3}, r2=Table2{b=4}}
Record{r1=Table1{a=3}, r2=Table2{b=5}}
\-----------------
Record{r1=Table1{a=3}, r2=Table2{b=3}}

对比一下sql和java的结果,输出的结果条数、数据基本上一致,唯一不同的是顺序上面不一样,顺序为何不一致,下面介绍。

代码中的leftJoin()方法模拟了下面的sql:

mysql> select * from test1 t1 left join test2 t2 on t1.a = t2.b;
+------+------+
| a   | b   |
+------+------+
|   3 |   3 |
|   1 | NULL |
|   2 | NULL |
+------+------+
3 rows in set (0.00 sec)mysql> select * from test1 t1 left join test2 t2 on t1.a>10;
+------+------+
| a   | b   |
+------+------+
|   1 | NULL |
|   2 | NULL |
|   3 | NULL |
+------+------+
3 rows in set (0.00 sec)

运行leftJoin(),结果如下:

Record{r1=Table1{a=3}, r2=Table2{b=3}}
Record{r1=Table1{a=1}, r2=null}
Record{r1=Table1{a=2}, r2=null}
-----------------
Record{r1=Table1{a=1}, r2=null}
Record{r1=Table1{a=2}, r2=null}
Record{r1=Table1{a=3}, r2=null}

效果和sql的效果完全一致,可以对上。

现在我们来讨论java输出的顺序为何和sql不一致?

上面java代码中两个表的连接查询使用了嵌套循环,外循环每执行一次,内循环的表都会全部遍历一次,如果放到mysql中,就相当于内表(被驱动表)全部扫描了一次(一次全表io读取操作),主表(外循环)如果有n条数据,那么从表就需要全表扫描n次,表的数据是存储在磁盘中,每次全表扫描都需要做io操作,io操作是最耗时间的,如果mysql按照上面的java方式实现,那效率肯定很低。

那mysql是如何优化的呢?

msql内部使用了一个内存缓存空间,就叫他join_buffer吧,先把外循环的数据放到join_buffer中,然后对从表进行遍历,从表中取一条数据和join_buffer的数据进行比较,然后从表中再取第2条和join_buffer数据进行比较,直到从表遍历完成,使用这方方式来减少从表的io扫描次数,当join_buffer足够大的时候,大到可以存放主表所有数据,那么从表只需要全表扫描一次(即只需要一次全表io读取操作)。

mysql中这种方式叫做Block Nested Loop。

java代码改进一下,来实现join_buffer的过程。

8、java代码改进版本

package com.itsoku.sql;import org.junit.Test;import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.stream.Collectors;import com.itsoku.sql.Test1.*;public class Test2 {public static int joinBufferSize = 10000;public static List<?> joinBufferList = new ArrayList<>();public static <R1, R2> List<Record<R1, R2>> join(List<R1> table1, List<R2> table2, JoinType joinType, Filter<R1, R2> onFilter, Filter<R1, R2> whereFilter) {if (Objects.isNull(table1) || Objects.isNull(table2) || joinType == null) {return new ArrayList<>();}List<Test1.Record<R1, R2>> result = new CopyOnWriteArrayList<>();int table1Size = table1.size();int fromIndex = 0, toIndex = joinBufferSize;toIndex = Integer.min(table1Size, toIndex);while (fromIndex < table1Size && toIndex <= table1Size) {joinBufferList = table1.subList(fromIndex, toIndex);fromIndex = toIndex;toIndex += joinBufferSize;toIndex = Integer.min(table1Size, toIndex);List<Record<R1, R2>> blockNestedLoopResult = blockNestedLoop((List<R1>) joinBufferList, table2, onFilter);result.addAll(blockNestedLoopResult);}if (joinType == JoinType.leftJoin) {List<R1> r1Record = result.stream().map(Record::getR1).collect(Collectors.toList());List<Record<R1, R2>> leftAppendList = new ArrayList<>();for (R1 r1 : table1) {if (!r1Record.contains(r1)) {leftAppendList.add(Record.build(r1, null));}}result.addAll(leftAppendList);}if (Objects.nonNull(whereFilter)) {for (Record<R1, R2> record : result) {if (!whereFilter.accept(record.r1, record.r2)) {result.remove(record);}}}return result;}public static <R1, R2> List<Record<R1, R2>> blockNestedLoop(List<R1> joinBufferList, List<R2> table2, Filter<R1, R2> onFilter) {List<Record<R1, R2>> result = new ArrayList<>();for (R2 r2 : table2) {for (R1 r1 : joinBufferList) {if (Objects.nonNull(onFilter) ? onFilter.accept(r1, r2) : true) {result.add(Record.build(r1, r2));}}}return result;}@Testpublic void innerJoin() {List<Table1> table1 = Arrays.asList(Table1.build(1), Table1.build(2), Table1.build(3));List<Table2> table2 = Arrays.asList(Table2.build(3), Table2.build(4), Table2.build(5));join(table1, table2, JoinType.innerJoin, null, null).forEach(System.out::println);System.out.println("-----------------");join(table1, table2, JoinType.innerJoin, (r1, r2) -> r1.a == r2.b, null).forEach(System.out::println);}@Testpublic void leftJoin() {List<Table1> table1 = Arrays.asList(Table1.build(1), Table1.build(2), Table1.build(3));List<Table2> table2 = Arrays.asList(Table2.build(3), Table2.build(4), Table2.build(5));join(table1, table2, JoinType.leftJoin, (r1, r2) -> r1.a == r2.b, null).forEach(System.out::println);System.out.println("-----------------");join(table1, table2, JoinType.leftJoin, (r1, r2) -> r1.a > 10, null).forEach(System.out::println);}
}

执行innerJoin(),输出:

Record{r1=Table1{a=1}, r2=Table2{b=3}}
Record{r1=Table1{a=2}, r2=Table2{b=3}}
Record{r1=Table1{a=3}, r2=Table2{b=3}}
Record{r1=Table1{a=1}, r2=Table2{b=4}}
Record{r1=Table1{a=2}, r2=Table2{b=4}}
Record{r1=Table1{a=3}, r2=Table2{b=4}}
Record{r1=Table1{a=1}, r2=Table2{b=5}}
Record{r1=Table1{a=2}, r2=Table2{b=5}}
Record{r1=Table1{a=3}, r2=Table2{b=5}}
-----------------
Record{r1=Table1{a=3}, r2=Table2{b=3}}

执行leftJoin(),输出:

Record{r1=Table1{a=3}, r2=Table2{b=3}}
Record{r1=Table1{a=1}, r2=null}
Record{r1=Table1{a=2}, r2=null}
-----------------
Record{r1=Table1{a=1}, r2=null}
Record{r1=Table1{a=2}, r2=null}
Record{r1=Table1{a=3}, r2=null}

结果和sql的结果完全一致。

8、扩展

表连接中还可以使用前面学过的group by、having、order by、limit。

这些关键字相当于在表连接的结果上在进行操作,可以练习一下。

MySQL联表查询【详解】相关推荐

  1. mysql 联表比对,MySQL联表查询详解/超详细mysql left join,right join,inner join用法分析比较...

    超详细mysql left join,right join,inner join用法分析 下面是例子分析 表A记录如下: aID aNum 1 a20050111 2 a20050112 3 a200 ...

  2. mysql多表查询详解_MySQL多表查询详解上

    时光在不经意间,总是过得出奇的快.小暑已过,进入中暑,太阳更加热烈的绽放着ta的光芒,...在外面被太阳照顾的人们啊,你们都是勤劳与可爱的人啊.在房子里已各种姿势看我这篇这章的你,既然点了进来,那就由 ...

  3. Mysql 多表查询详解

    Mysql 多表查询详解 一.前言  二.示例 三.注意事项 一.前言  上篇讲到Mysql中关键字执行的顺序,只涉及了一张表:实际应用大部分情况下,查询语句都会涉及到多张表格 : 1.1 多表连接有 ...

  4. 七、MySQL 多表查询详解(附练习题及答案----超详细)

    文章目录 一.笛卡尔积(或交叉连接)的理解 二.多表查询分类讲解 2.1 分类1:等值连接 vs 非等值连接 2.2 分类2:自连接 vs 非自连接 2.3 分类3:内连接 vs 外连接 2.4 SQ ...

  5. mysql单表查询详解

    文章目录 一.单表查询 1.1 创建数据库 1.2 单表查询 1.2.1 查询所有字段 1.2.2 使用通配符*查询所有字段 1.2.3 查询指定字段 1.2.4 去重distinct的使用 1.2. ...

  6. mysql多表查询详解

    在mysql中把查询结果去除重复记录[distinct] 如:select distinct job from emp;// 去除员工表工作字段中的重复记录 需要注意的是distinct只能出现在所有 ...

  7. mysql联表查询语句示例

    mysql联表查询语句示例 ps:本人亲测,阿里云2核4G5M的服务器性价比很高,新用户一块多一天,老用户三块多一天,最高可以买三年,感兴趣的可以戳一下:阿里云折扣服务器 示例: "sele ...

  8. (转)MySQL联表查询

    资料源于网络 一.内联结.外联结.左联结.右联结的含义及区别 在SQL标准中规划的(Join)联结大致分为下面四种: 1.内联结:将两个表中存在联结关系的字段符合联结关系的那些记录形成记录集的联结. ...

  9. 【explain】MySQL联表查询中的驱动表

    写在前面 1.不要求每个人一定理解 联表查询(join/left join/inner join等)时的mysql运算过程 2.不要求每个人一定知道线上(现在或未来)哪张表数据量大,哪张表数据量小 3 ...

  10. MySQL联表查询(连接查询)

    联表查询 增删改查 7种join理论 图源网络,若侵权请联系删除 MySQL中没有full outer join操作,所以第6种是1 union 3:第7种是4 union 5 查询步骤 分析查询字段 ...

最新文章

  1. Codeforces Round #712 Div.2(A ~ F) 超高质量题解(每日训练 Day.15 )
  2. 454. 4Sum II
  3. Shell 数组中 @ 跟 * 的区别
  4. div超出不换行_一日一技:XPath不包含应该怎么写?
  5. h5页面不可 移动_七大h5创建工具
  6. 一起谈.NET技术,发布NGuestBook(一个基于.NET平台的分层架构留言本小系统)
  7. Java实现词频统计(Wordcount)-Map或Hashtable的value排序
  8. 适用于 ESXi 6.x 中的 OpenSLP 安全漏洞 (CVE-2019-5544) 的权宜措施 (76372)
  9. xcode5切换IOS7,IOS6,IOS5模拟器
  10. STM32F091 can通信配置,can波特率计算方法
  11. win7美化_桌面美化之 windows10
  12. 滑动拼图验证码 免费 java,原生js+canvas实现滑动拼图验证码
  13. Rosalind第16题——ros_bio16_MPRT
  14. PID原理(图形并茂)
  15. 建立手机平台的产品线
  16. 开博第一文:成为软件奇才的五要素
  17. 华为手机打开图片很慢是怎么回事_华为手机打开应用很慢怎么办
  18. JSP房地产门户管理系统myeclipse开发mysql数据库BS模式java编程网页结构详细设计
  19. 【图解数据结构与算法】视频教程正式上线B站,持续更新中......
  20. 无线有线统一认证计费管理平台

热门文章

  1. 动力节点——Java学习路线图
  2. 【博主推荐】html后台管理系统框架(附源码)
  3. Centos7系统中wget安装方法
  4. matlab中回归分析案例,matlab 回归分析中 regstats怎么用?
  5. 各厂内推整理 | 第三期
  6. 联合循环——32 TN-C系统与TN-C-S接地系统和TN-S系统(二)
  7. 木姐说副业利用短视频,操作驾考赚钱项目副业,轻松实现财务自由
  8. web应用越权问题的三个解决思路
  9. 用第三人称写日记减压
  10. jsp网上书城图书销售系统