MaxCompute SQL數據開發最佳實踐

MaxCompute SQL最佳實踐

MaxCompute SQL最佳實踐是在開發文檔的內容基礎上,提供的一些實踐性的使用指南,具體的內容見:MaxCompute SQL最佳實踐文檔,這裏選出了一些重要且常用的內容進行說明。內容包括:

  • 快速掌握SQL寫法
  • 分組取出每組數據的前N條
  • SQL實現多行數據轉一條
  • MaxCompute SQL中的JOIN ON條件

快速掌握SQL寫法

數據集準備

這裏以emp/dept表爲示例數據集。單擊emp表數據文件dept表數據文件下載數據文件。這裏將下載的文件存儲爲:csv格式文件,文件中的數據內容不變。然後在MaxCompute項目上使用如下命令創建相應的表:

創建emp表:

-- 創建emp表
CREATE TABLE IF NOT EXISTS emp (
  EMPNO string ,
  ENAME string ,
  JOB string ,
  MGR bigint ,
  HIREDATE datetime ,
  SAL double ,
  COMM double ,
  DEPTNO bigint
);

創建dept表:

-- 創建dept表
CREATE TABLE IF NOT EXISTS dept (
  DEPTNO bigint ,
  DNAME string ,
  LOC string
);

創建表完成後,上傳數據使用odpscmd客戶端的tunnel命令,將下載的數據文件導入到數據表中,odpscmd客戶端以及MaxCompute Studio中使用odpscmd的開發環境搭建見:MaxCompute Studio數據開發工具的使用 。上傳命令如下:

odps@ YITIAN_BJ_MC>tunnel upload /Users/yitian/Documents/MaxCompute/maxcompute-data/emp.csv emp;

Upload session: 202004212318540c47df0b1829ea42
Start upload:/Users/yitian/Documents/MaxCompute/maxcompute-data/emp.csv
Using \n to split records
Upload in strict schema mode: true
Total bytes:744  Split input to 1 blocks
2020-04-21 23:18:55     scan block: '1'
2020-04-21 23:18:55     scan block complete, block id: 1
2020-04-21 23:18:55     upload block: '1'
2020-04-21 23:18:55     upload block complete, block id: 1
upload complete, average speed is 744 bytes/s
OK

odps@ YITIAN_BJ_MC>tunnel upload /Users/yitian/Documents/MaxCompute/maxcompute-data/dept.csv dept;

上傳完成後,可使用如下命令進行來查看導入數據的正確性:

odps@ YITIAN_BJ_MC>select count(*) from emp;
+------------+
| _c0        |
+------------+
| 14         |
+------------+

常見問題

剛開始學習MaxCompute SQL時,可能會遇到的常見問題如下:

  • 使用Group by時,Select的部分要麼是分組項,要麼是聚合函數。
  • Order by後面必須加Limit n。
  • Select表達式中不能用子查詢,可以改寫爲Join。
  • Join不支持笛卡爾積,以及MapJoin的用法和使用場景。
  • Union all需要改成子查詢的格式。
  • In/Not in語句對應的子查詢只能有一列,而且返回的行數不能超過1000,否則也需要改成Join。

SQL示例

示例一:列出員工人數大於零的所有部門。有如下兩種查詢方法:

-- 列出員工人數大於0的所有部門,方法1
select d.deptno, d.dname, d.loc
from dept as d
join (
  select deptno, count(*) as num from emp
  group by deptno
) as e on d.deptno=e.deptno
where e.num>0;

-- 方法2
select d.*
from dept d
join (
  select distinct deptno as no
  from emp
) e on d.deptno=e.no;

返回的結果均爲:

分析一下上面的兩個方法:

就本查詢來說,方法2的方式更爲簡單,查詢效率應該更高,而且滿足正確的查詢需要。方法1使用了分組的思想來統計emp表中各部門的人數num,在進行join連接,並使用num作爲過濾條件,這種方式的查詢效率低於方法2。但如果查詢需要統計:列出員工人數大於零的所有部門,並列出各部門的總人數,這種情況下方法2就不能滿足需要了,但方法1中可以在第一行的輸出列中加入e.num,即可以完成該查詢需求。

示例二:列出薪金比SMITH多的所有員工(MapJoin使用場景)。這裏依然存在兩種查詢方案:

-- 列出薪資比smith多的所有員工,方法1
select empno, ename, sal from emp
join (
  select max(sal) as smith_sal from emp
  where ename='SMITH'
) s on emp.sal>s.smith_sal;

-- 方法2
select /*+ mapjoin(a)*/ e.empno, e.ename, e.sal
from emp e
join (
  select max(sal) as smith_sal from emp
  where ename='SMITH'
) a on e.sal>a.smith_sal;

返回結果均爲:

 

方法1爲普通的join連接查詢,但其實此時是MapJoin的典型場景:當一個大表和一個或多個小表JOIN時,您可以在SELECT語句中顯式指定MAPJOIN以提升查詢性能。這裏emp相對來說是大表,而子查詢爲小表,因此可以使用mapjoin進行查詢,以提升查詢性能。

示例三:列出所有員工的姓名及其直接上級的姓名。自連接查詢場景:

-- 列出所有員工的姓名及其直接上級的姓名
select t1.ename as emp_name, t2.ename as leader_name
from emp t1
left outer join emp t2 on t1.mgr=t2.empno;

返回結果如下:

示例四:列出基本薪金大於1500的所有工作(having語句使用場景)。下面有兩種查詢方法

-- 列出基本薪金大於1500的所有工作,方法1(錯誤)
select job, min(sal) as sal
from emp
where sal>1500
group by job;

-- 方法2(正確)
select job, min(sal) as sal
from emp
group by job
having min(sal)>1500;

以上的兩個方法,方法1的執行順序是:from-where-select-group by。因此它會先將sal>1500的所有job找到,然後在進行grouop by。此時由於job=SALESMAN的多條數據中,存在sal>1600的數據,因此最終查詢結果會包含SALESMAN。結果如下:

但該查詢的需求中“基本工資”這裏文檔中理解爲最低工資,而SALESMAN的最低工資爲1250,因此它並不應該包含在最後的結果集中,此時就需要使用方法2進行查詢,方法2的查詢結果爲正確的結果。返回的內容如下:

示例五:列出在每個部門工作的員工數量、平均工資和平均服務期限。時間處理上有很多好用的內建函數,如下所示。

-- 列出在每個部門工作的員工數量、平均工資和平均服務期限
select deptno,
       count(empno) as cnt_emp,
       round(avg(sal)) as avg_sal,
       round(avg(datediff(getdate(), hiredate, 'dd')), 2) as avg_hire
from emp
group by deptno;

 返回結果如下:

示例六: 列出每個部門的薪水前3名的人員的姓名以及其排序(Top n的需求非常常見)。

-- 列出每個部門的薪水前3名的人員的姓名以及其排序
select *
from (
  select deptno,
         ename,
         sal,
         ROW_NUMBER() over (partition by deptno order by sal desc) as nums
  from emp
) t1
where t1.nums < 4;

返回結果如下:

 

其中使用了MaxCompute中窗口函數ROW_NUMBER,該函數由於對窗口內分組數據進行排序,並返回排序後的行號,命令格式如下,具體見:MaxCompute窗口函數文檔內容。 

row_number() over(partition by [col1, col2…]
order by [col1[asc|desc], col2[asc|desc]…])

示例七: 用一個SQL寫出每個部門的人數、CLERK(辦事員)的人數佔該部門總人數佔比

-- 用一個SQL寫出每個部門的人數、CLERK(辦事員)的人數佔該部門總人數佔比
SELECT deptno, 
       COUNT(empno) AS cnt, 
       ROUND(SUM(CASE 
        WHEN job = 'CLERK' THEN 1 
        ELSE 0 
        END
       ) / COUNT(empno), 2) AS rate
FROM `EMP`
GROUP BY deptno;

返回結果如下:

 

這裏對clerk人數統計時使用了MaxCompute中一些特有的函數,這部分函數包括CAST、DECODE、LEAST、ARRAY、SPLIT、MAP等,具體見:MaxCompute 其他函數文檔。 

分組取出每組數據的前N條

這部分的內容其實上面已經使用過一次了(上面示例6),就是使用ROW_NUMBER窗口函數進行分組排序後,在將每組的前N條數據進行取出即可。命令如下:

SELECT * FROM (
  SELECT empno
  , ename
  , sal
  , job
  , ROW_NUMBER() OVER (PARTITION BY job ORDER BY sal) AS rn
  FROM emp
) tmp
WHERE rn < 10;

返回結果如下:

此外,可以將所有職工根據部門分組,每個組內根據sal做降序排序,獲得職工自己組內的序號。

select deptno, 
       ename, 
       sal, 
       ROW_NUMBER() over (partition by deptno order by sal desc) as nums
from emp;
--執行結果如下。
+------------+-------+------------+------------+
| deptno     | ename | sal        | nums       |
+------------+-------+------------+------------+
| 10         | JACCKA | 5000.0     | 1          |
| 10         | KING  | 5000.0     | 2          |
| 10         | CLARK | 2450.0     | 3          |
| 10         | WELAN | 2450.0     | 4          |
| 10         | TEBAGE | 1300.0     | 5          |
| 10         | MILLER | 1300.0     | 6          |
| 20         | SCOTT | 3000.0     | 1          |
| 20         | FORD  | 3000.0     | 2          |
| 20         | JONES | 2975.0     | 3          |
| 20         | ADAMS | 1100.0     | 4          |
| 20         | SMITH | 800.0      | 5          |
| 30         | BLAKE | 2850.0     | 1          |
| 30         | ALLEN | 1600.0     | 2          |
| 30         | TURNER | 1500.0     | 3          |
| 30         | MARTIN | 1250.0     | 4          |
| 30         | WARD  | 1250.0     | 5          |
| 30         | JAMES | 950.0      | 6          |
+------------+-------+------------+------------+

SQL實現多行數據轉一條

這部分說明如何使用 SQL 實現多條數據壓縮爲一條。

需求1:將emp表,根據job,獲取該job中的所有ename作爲一列,格式如下:

job ename
job1 ename1,ename2,ename3
job2 ename4,ename5

查詢語句如下:

-- 將emp表,根據job,獲取該job中的所有ename作爲一列
select job, wm_concat(',', ename) as ename_list
from emp
group by job;

返回結果如下:

 

需求2:在emp表中,統計每個部門的總人數,CLERK職位的人數及佔比,MANAGER的總人數及佔比,命令如下:

-- 在emp表中,統計每個部門的總人數,CLERK職位的人數及佔比,MANAGER的總人數及佔比
select deptno,
       count(empno),
       sum(case when job='CLERK' then 1 else 0 end) as clerk_cnt,
       round(sum(case when job='CLERK' then 1 else 0 end)/count(empno), 2) as clerk_rate,
       sum(case when job='MANAGER' then 1 else 0 end) as manager_cnt,
       round(sum(case when job='MANAGER' then 1 else 0 end)/count(empno), 2) as manager_rate
from emp
group by deptno;

返回結果如下:

MaxCompute SQL中的JOIN ON條件

目前MaxCompute提供了以下幾種Join類型:

 
類型 含義
Inner Join 輸出符合關聯條件的數據。
Left Join 輸出左表的所有記錄,對於右表符合關聯的數據,輸出右表,沒有符合的,右表補null。
Right Join 輸出右表的所有記錄,對於左表符合關聯的數據,輸出左表,沒有符合的,左表補null。
Full Join 輸出左表和右表的所有記錄,對於沒有關聯上的數據,未關聯的另一側補null。
Left Semi Join 對於左表中的一條數據,如果右表存在符合關聯條件的行,則輸出左表。
Left Anti Join 對於左表中的一條數據,如果對於右表所有的行,不存在符合關聯條件的數據,則輸出左表。

 根據不同的場景,可以使用不同的Join類型來實現對應的關聯操作。但是在實際使用過程中,存在如下錯誤示例。

A (LEFT/RIGHT/FULL/LEFT SEMI/LEFT ANTI) JOIN B
ON a.key = b.key and A.ds='20180101' and B.ds='20180101';

正確的join語句如下,應該是在A和B中獲取某一個分區的數據,然後在進行JOIN操作:

(SELECT * FROM A WHERE ds='20180101') A
(LEFT/RIGHT/FULL/LEFT SEMI/LEFT ANTI)  JOIN
(SELECT * FROM B WHERE ds='20180101') B
ON a.key = b.key

然而針對不同的Join類型,兩者可能並不等價。

連接條件和執行順序

對於如下包含JOIN和WHERE條件的語句:

(SELECT * FROM A WHERE {subquery_where_condition} A) A
JOIN
(SELECT * FROM B WHERE {subquery_where_condition} B) B
ON {on_condition}
WHERE {where_condition}

計算順序爲:

  1. 子查詢中的{subquery_where_condition}
  2. JOIN的{on_condition}的條件。
  3. JOIN結果集合{where_condition}的計算。

對於不同的JOIN類型,過濾語句放在{subquery_where_condition}{on_condition}{where_condition}中,有時結果是一致的,有時候結果又是不一致的。下面分情況進行討論。

太長不看版

以下實驗的結論總結:

過濾條件放在不同的位置語義可能大不相同,如果只是進行過濾數據後再JOIN的操作,請注意以下幾點:

  • INNER JOIN/LEFT SEMI JOIN兩側的表達式可以隨便寫。
  • LEFT JOIN/LEFT ANTI JOIN左表的過濾條件要放到{subquery_where_condition}或者{where_condition},右表的過濾條件要放到{subquery_where_condition}或者{on_condition}中。
  • RIGHT JOIN和LEFT JOIN相反,右表的過濾條件要放到{subquery_where_condition}或者{where_condition},左表的過濾條件要放到{subquery_where_condition}或者{on_condition}。
  • FULL OUTER JOIN只能放到{subquery_where_condition}中。

如果還是覺得規則比較複雜,最好的方法就是把過濾條件寫到子查詢中。

詳細版:各類join操作實驗示例

數據準備

執行如下的建表語句:

-- JOIN操作示例數據
CREATE TABLE JOIN_A AS
  SELECT * FROM VALUES (1, 20180101),(2, 20180101),(2, 20180102) t (key, ds);

CREATE TABLE JOIN_B AS
  SELECT * FROM VALUES (1, 20180101),(3, 20180101),(2, 20180102) t (key, ds);

表A、表B的示例數據如下:

-- 表A示例數據
key	ds
+----+---+
1	20180101
2	20180101
2	20180102

-- 表B示例數據
key	ds
+----+---+
1	20180101
3	20180101
2	20180102

表A和表B的笛卡爾乘積如下:

 
a.key a.ds b.key b.ds
1 20180101 1 20180101
1 20180101 3 20180101
1 20180101 2 20180102
2 20180101 1 20180101
2 20180101 3 20180101
2 20180101 2 20180102
2 20180102 1 20180101
2 20180102 3 20180101
2 20180102 2 20180102

inner join測試 

Inner Join的處理邏輯是將左右表進行笛卡爾乘積,然後選擇滿足ON表達式的行進行輸出。

1. 第一種情況,在子查詢中過濾:

-- inner join 情況1
select a.*, b.*
from (
  select * from JOIN_A where ds='20180101'
) a join (
  select * from JOIN_B where ds='20180101'
) b on a.key=b.key;

結果如下:

 
a.key a.ds b.key b.ds
1 20180101 1 20180101

2. 第二種情況,在JOIN 條件中過濾:

-- inner join 情況2
select a.*, b.*
from JOIN_A a
join JOIN_B b on a.key=b.key and a.ds='20180101' and b.ds='20180101';

笛卡爾積的結果有9條,滿足ON條件的結果同樣只有1條。

 
a.key a.ds b.key b.ds
1 20180101 1 20180101

3. 第三種情況,在JOIN後的WHERE條件過濾:

-- inner join 情況3
select a.*, b.*
from JOIN_A a
join JOIN_B b on a.key=b.key
where a.ds='20180101' and b.ds='20180101';

笛卡爾積的結果有9條,滿足ON條件a.key = b.key的結果有3條,如下。

 
a.key a.ds b.key b.ds
1 20180101 1 20180101
2 20180102 2 20180102
2 20180101 2 20180102

此時對於這個結果再進行過濾A.ds='20180101' and B.ds='20180101',結果只有1條。

 
a.key a.ds b.key b.ds
1 20180101 1 20180101

可以看到,將過濾條件放在三個不同的地方,得到了相同的結果。僅此該部分的結論如下:

結論:inner join中,過濾條件在{subquery_where_condition}{on_condition}{where_condition}中都是等價的。

Left Join

Left Join的處理邏輯是將左右表進行笛卡爾乘積,然後對於滿足ON表達式的行進行輸出,對於左表中不滿足ON表達式的行,輸出左表,右表補NULL。

1. 第一種情況,在子查詢中過濾:

-- left join 情況1
select a.*, b.*
from (
  select * from JOIN_A where ds='20180101'
) a left join (
  select * from JOIN_B where ds='20180101'
) b on a.key=b.key;

過濾後,左右側有兩條,右側有一條,結果有兩條:

 
a.key a.ds b.key b.ds
1 20180101 1 20180101
2 20180101 NULL NULL

並且,left join等價於left outer join,上面命令等價於如下,查詢結果相同:

select a.*, b.*
from (
  select * from JOIN_A where ds='20180101'
) a left outer join (
  select * from JOIN_B where ds='20180101'
) b on a.key=b.key;

 2. 第二種情況,在JOIN 條件中過濾:

select a.*, b.*
from JOIN_A a
left join JOIN_B b on a.key=b.key and a.ds='20180101' and b.ds='20180101';

笛卡爾積的結果有9條,滿足ON條件的結果同樣只有1條,則對於左表剩餘的兩條輸出左表,右表補NULL。

 
a.key a.ds b.key b.ds
1 20180101 1 20180101
2 20180101 NULL NULL
2 20180102 NULL NULL

3. 第三種情況,JOIN後的WHERE條件過濾:

-- left join 情況3
select a.*, b.*
from JOIN_A a
left join JOIN_B b on a.key=b.key
where a.ds='20180101' and b.ds='20180101';

笛卡爾積的結果有9條,滿足ON條件a.key = b.key的結果有3條。

 
a.key a.ds b.key b.ds
1 20180101 1 20180101
2 20180101 2 20180102
2 20180102 2 20180102

此時對於這個結果再進行過濾A.ds='20180101' and B.ds='20180101',結果只有1條。

 
a.key a.ds b.key b.ds
1 20180101 1 20180101

可以看到,將過濾條件放在三個不同的地方,得到了三種不同的結果。

結論:過濾條件在{subquery_where_condition}{on_condition}{where_condition}不一定等價。

  • 對於左表的過濾條件,放在{subquery_where_condition}{where_condition}是等價的。
  • 對於右表的過濾條件,放在{subquery_where_condition}{on_condition}中是等價的。

Right Join

Right Join和Left Join是類似的,只是左右表的區別,結論如下:

  • 過濾條件在{subquery_where_condition}{on_condition}{where_condition}不一定等價。
  • 對於右表的過濾條件,放在{subquery_where_condition}{where_condition}是等價的。
  • 對於左表的過濾條件,放在{subquery_where_condition}{on_condition}中是等價的。

Full Join

FULL Join的處理邏輯是將左右表進行笛卡爾乘積,然後對於滿足ON表達式的行進行輸出,對於兩側表中不滿足ON表達式的行,輸出有數據的表,另一側補NULL。

1. 第一種情況,子查詢中過濾:

-- full join 情況1
select a.*, b.*
from (
  select * from JOIN_A where ds='20180101'
) a full join (
  select * from JOIN_B where ds='20180101'
) b on a.key=b.key;

過濾後,左右側有兩條,右側有兩條,結果有三條。

 
a.key a.ds b.key b.ds
1 20180101 1 20180101
2 20180101 NULL NULL
NULL NULL 3 20180101

2. 第二種情況,JOIN 條件中過濾:

-- full join 情況2
select a.*, b.*
from JOIN_A a
full join JOIN_B b on a.key=b.key and a.ds='20180101' and b.ds='20180101';

笛卡爾積的結果有9條,滿足ON條件的結果同樣只有1條,則對於左表剩餘的兩條輸出左表,右表補NULL。右表剩餘的兩條輸出右表,左表補NULL。

 
a.key a.ds b.key b.ds
1 20180101 1 20180101
2 20180101 NULL NULL
2 20180102 NULL NULL
NULL NULL 3 20180101
NULL NULL 2 20180102

3. 第三種情況,JOIN後的WHERE條件過濾:

-- full join 情況3
select a.*, b.*
from JOIN_A a
full join JOIN_B b on a.key=b.key
where a.ds='20180101' and b.ds='20180101';

笛卡爾積的結果有9條,滿足ON條件a.key = b.key的結果有3條。

 
a.key a.ds b.key b.ds
1 20180101 1 20180101
2 20180101 2 20180102
2 20180102 2 20180102

再對沒有JOIN上的數據進行輸出,另一側補NULL,得到結果如下。

 
a.key a.ds b.key b.ds
1 20180101 1 20180101
2 20180101 2 20180102
2 20180102 2 20180102
NULL NULL 3 20180101

此時對於這個結果再進行過濾A.ds='20180101' and B.ds='20180101',結果只有1條。

 
a.key a.ds b.key b.ds
1 20180101 1 20180101

可以看到,和LEFT JOIN類似,得到了三種不同的結果。

結論:過濾條件寫在{subquery_where_condition}{on_condition}{where_condition}均不等價。

Left Semi Join

LEFT SEMI Join的處理邏輯是對於左表的每一條記錄,都去和右表進行匹配,如果匹配成功,則輸出左表。這裏需要注意的是由於只輸出左表,所以JOIN後的Where條件中不能寫右側的過濾條件。LEFT SEMI JOIN常用來實現exists的語義

1. 第一種情況,在子查詢中過濾:

-- left semi join 情況1
select a.*
from (
  select * from JOIN_A where ds='20180101'
) a left semi join (
  select * from JOIN_B where ds='20180101'
) b on a.key=b.key;

過濾後,左右側有兩條,最終符合a.key = b.key的只有一條。

 
a.key a.ds
1 20180101

2. 第二種情況,JOIN 條件中過濾:

-- left semi join 情況2
select a.*
from JOIN_A a
left semi join JOIN_B b on a.key=b.key and a.ds='20180101' and b.ds='20180101';

對於左側的三條記錄,滿足ON條件的結果同樣只有1條。

 
a.key a.ds
1 20180101

3. 第三種情況,JOIN後的WHERE條件過濾:

-- left semi join 情況3
select a.*
from JOIN_A a
left semi join (
  select * from JOIN_B where ds='20180101'
) b on a.key=b.key
where a.ds='20180101';

左側能符合ON條件的有一條。

 
a.key a.ds
1 20180101

此時對於這個結果再進行過濾A.ds='20180101',結果仍然保持1條。

 
a.key a.ds
1 20180101

可以看到,LEFT SEMI JOIN和INNER JOIN類似,無論過濾條件放在哪裏,結果都是一致的。

結論:過濾條件寫在{subquery_where_condition}{on_condition}{where_condition}是等價的。

Left Anti Join

LEFT ANTI Join的處理邏輯是對於左表的每一條記錄,都去和右表進行匹配,如果右表所有的記錄都沒有匹配成功,則輸出左表。同樣由於只輸出左表,所以JOIN後的Where條件中不能寫右側的過濾條件。LEFT SEMI JOIN常常用來實現not exists的語義。

1. 第一種情況,子查詢中過:

-- left anti join 情況1
select a.*
from (
  select * from JOIN_A where ds='20180101'
) a left anti join (
  select * from JOIN_B where ds='20180101'
) b on a.key=b.key;

過濾後,左側有兩條,右側有兩條,結果有1條。

 
a.key a.ds
2 20180101

2. 第二種情況,JOIN 條件中過濾:

-- left anti join 情況2
select a.*
from JOIN_A a
  left anti join JOIN_B b on a.key=b.key and a.ds='20180101' and b.ds='20180101';

對於左側的三條記錄,只有第一條有滿足ON條件的結果,所以輸出剩餘的兩條記錄。

 
a.key a.ds
2 20180101
2 20180102

3. 第三種情況,JOIN後的WHERE條件過濾:

-- left anti join 情況3
select a.*
from JOIN_A a
  left anti join (
  select * from JOIN_B where ds='20180101'
  ) b on a.key=b.key
where a.ds='20180101';

左側能通過ON條件的有兩條。

 
a.key a.ds
2 20180101
2 20180102

此時對於這個結果再進行過濾A.ds='20180101',結果爲1條。

 
a.key a.ds
2 20180101

可以看到,LEFT ANTI JOIN中,過濾條件WHERE語句分別放在JOIN ON條件中、條件前和條件後,得到的結果是不相同的。

結論:過濾條件寫在{subquery_where_condition}{on_condition}{where_condition}不一定等價。

  • 對於左表的過濾條件,放在{subquery_where_condition}{where_condition}是等價的。
  • 對於右表的過濾條件,放在{subquery_where_condition}{on_condition}中是等價的,右表表達式不能放在{where_condition}中。

以上內容只是針一個常用場景測試的幾種不同的寫法,沒有具體的推導過程,對於涉及到不等值表達式的場景會更加複雜,如果您有興趣可以嘗試推導一下。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章