Mysql高手系列 – 第11篇:深入了解連接查詢及原理

  • 2019 年 10 月 3 日
  • 筆記

這是Mysql系列第11篇。

環境:mysql5.7.25,cmd命令中進行演示。

當我們查詢的數據來源於多張表的時候,我們需要用到連接查詢,連接查詢使用率非常高,希望大家都務必掌握。

本文內容

  1. 笛卡爾積
  2. 內連接
  3. 外連接
  4. 左連接
  5. 右連接
  6. 表連接的原理
  7. 使用java實現連接查詢,加深理解

準備數據

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)

笛卡爾積

介紹連接查詢之前,我們需要先了解一下笛卡爾積。

笛卡爾積簡單點理解:有兩個集合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行記錄。

內連接

語法:

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中使用了組合條件。

方式2:在連接的結果之後再進行過濾,相當於先獲取連接的結果,然後使用where中的條件再對連接結果進行過濾。

方式3:直接在where後面進行過濾。

總結

內連接建議使用第3種語法,簡潔:

select 字段 from 表1, 表2 [where 關聯條件];

外連接

外連接涉及到2個表,分為:主表和從表,要查詢的信息主要來自於哪個表,誰就是主表。

外連接查詢結果為主表中所有記錄。如果從表中有和它匹配的,則顯示匹配的值,這部分相當於內連接查詢出來的結果;如果從表中沒有和它匹配的,則顯示null。

最終:外連接查詢結果 = 內連接的結果 + 主表中有的而內連接結果中沒有的記錄。

外連接分為2種:

左外鏈接:使用left join關鍵字,left join左邊的是主表。

右外連接:使用right join關鍵字,right join右邊的是主表。

左連接

語法

select 列 from 主表 left join 從表 on 連接條件;

示例1:

查詢所有員工信息,並顯示員工所在組,如下:

mysql> SELECT          t1.emp_name,          t2.team_name      FROM          t_employee t1      LEFT JOIN          t_team t2      ON          t1.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> SELECT          t1.emp_name,          t2.team_name      FROM          t_employee t1      LEFT JOIN          t_team t2      ON          t1.team_id = t2.id      WHERE          t2.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> SELECT          t2.team_name,          t1.emp_name      FROM          t_team t2      RIGHT JOIN          t_employee t1      ON          t1.team_id = t2.id;  +-----------+---------------+  | team_name | emp_name      |  +-----------+---------------+  | 架構組    | 路人甲Java    |  | 測試組    | 張三          |  | java組    | 李四          |  | NULL      | 王五          |  | NULL      | 趙六          |  +-----------+---------------+  5 rows in set (0.00 sec)    mysql> SELECT          t2.team_name,          t1.emp_name      FROM          t_team t2      RIGHT JOIN          t_employee t1      ON          t1.team_id = t2.id      WHERE          t2.team_name IS NOT NULL;  +-----------+---------------+  | team_name | emp_name      |  +-----------+---------------+  | 架構組    | 路人甲Java    |  | 測試組    | 張三          |  | java組    | 李四          |  +-----------+---------------+  3 rows in set (0.00 sec)

理解表連接原理

準備數據

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,返回結果為笛卡爾積。

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;          }            @Override          public 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);          }            @Override          public 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;          }            @Override          public 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;      }        @Test      public 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);      }        @Test      public 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的過程。

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;      }        @Test      public 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);      }        @Test      public 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的結果完全一致。

擴展

表連接中還可以使用前面學過的group byhavingorder bylimit

這些關鍵字相當於在表連接的結果上在進行操作,大家下去可以練習一下,加深理解。

Mysql系列目錄

  1. 第1篇:mysql基礎知識
  2. 第2篇:詳解mysql數據類型(重點)
  3. 第3篇:管理員必備技能(必須掌握)
  4. 第4篇:DDL常見操作
  5. 第5篇:DML操作匯總(insert,update,delete)
  6. 第6篇:select查詢基礎篇
  7. 第7篇:玩轉select條件查詢,避免采坑
  8. 第8篇:詳解排序和分頁(order by & limit)
  9. 第9篇:分組查詢詳解(group by & having)
  10. 第10篇:常用的幾十個函數詳解

mysql系列大概有20多篇,喜歡的請關注一下,歡迎大家加我微信itsoku或者留言交流mysql相關技術!