1. 程式人生 > >Mysql高手系列 - 第11篇:深入瞭解連線查詢及原理

Mysql高手系列 - 第11篇:深入瞭解連線查詢及原理

這是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相關技術!