T-Sql

具有多個連接的 SQL 查詢的執行計劃的邏輯順序

  • October 28, 2019

我知道 SQL 查詢的執行邏輯順序是:

FROM
ON
JOIN
WHERE
GROUP BY
WITH CUBE or WITH ROLLUP
HAVING
SELECT
DISTINCT
ORDER BY
TOP

如果查詢中有多個連接會發生什麼,例如,如果我們有這樣的查詢:

SELECT *
FROM user_branch T1
INNER JOIN dimcustomer2 T2
  ON T1.BRANCH_CODE = T2.BRANCH_CODE
INNER JOIN customer_guarantee T3
  ON T3.CUSTOMER_NUM = T2.CUSTOMER_NUM

一些範例數據:

customer_guarantee:    CUSTOMER_NUM      BRANCH_CODE
                     -------------------------------
                          A                X
                          B                X
                          C                Y
                          D                Z



user_branch:           USER_ID          BRANCH_CODE    
                     --------------------------------
                          U1               Y



dimcustomer2:         CUSTOMER_NUM      BRANCH_CODE      
                     --------------------------------
                          A                Y
                          B                Y
                          C                Y
                          D                Z

這將如何執行?哪個聯接將首先執行?如果查詢中有不同類型的連接怎麼辦?在這種情況下執行連接的順序是什麼?提前致謝。

確定連接邏輯順序的一種方法是將範例中的第一個內連接替換為左外連接:

SELECT *
FROM user_branch T1
**LEFT**  JOIN dimcustomer2 T2
  ON T1.BRANCH_CODE = T2.BRANCH_CODE
INNER JOIN customer_guarantee T3
  ON T3.CUSTOMER_NUM = T2.CUSTOMER_NUM

讓我們假設 中的某些行在 中T1沒有匹配項T2。更具體地說,讓我們假設這些是三個表:

   T1                         T2                           T3
                                                  
BRANCH_CODE          BRANCH_CODE  CUSTOMER_NUM          CUSTOMER_NUM
-----------          -----------  ------------          ------------
11                   11           230                   120
12                   12           235                   170
13                   15           260                   230
14                                                      235
15                                                      245
                                                       250
                                                       260
                                                       270

這裡有兩個連接以及它們執行順序的兩種可能性。

1. 左連接,然後內連接

如果左連接首先計算,那麼它的結果將在行不匹配的T2列中包含空值:T1

T1.BRANCH_CODE T2.BARNCH_CODE T2.CUSTOMER_NUM
-------------- -------------- ---------------
11 11 230
12 12 235
13 *(空)*          *(空)* 
14 *(空)*          *(空)*
15 15 260

T3通過在使用列的條件上使用內部連接進一步連接該結果T2將消除不匹配 - 因此,相應T1的行 - 因為 null 不能滿足連接的等於條件:

T1.BRANCH_CODE T2.BARNCH_CODE T2.CUSTOMER_NUM T3.CUSTOMER_NUM
-------------- -------------- --------------- ---------------
11 11 230 230
12 12 235 235
15 15 260 260

這樣,一些T1行將從最終結果集中排除。

2. 內連接,然後左連接

現在,如果首先執行內部連接,那麼它將生成一個包含來自T2T3匹配內部連接條件的行的結果集:

T2.BARNCH_CODE T2.CUSTOMER_NUM T3.CUSTOMER_NUM
-------------- --------------- ---------------
11 230 230
12 235 235
15 260 260

當此結果集外連接到T1時,T1在外側,您將獲得一個最終結果,其中包含與外連接條件匹配的 - 內連接中T1T2所有行:T3

T1.BRANCH_CODE T2.BARNCH_CODE T2.CUSTOMER_NUM T3.CUSTOMER_NUM
-------------- -------------- --------------- ---------------
11 11 230 230
12 12 235 235
13 *(空)*          *(空)*           *(空)* 
14 *(空)*          *(空)*           *(空)*
15 15 260 260

因此,第二種解釋意味著所有T1行都應該出現在結果中。


由於這兩種解釋給出瞭如此不同的結果,很明顯只有一種是正確的。執行查詢,您會看到它實際上是第一個。這意味著從邏輯上講,連接按照它們在子句****中指定的順序執行FROM

語法變化

請注意,上面的結論適用於最傳統的連接語法,即:

FROM
 T1
 ... JOIN T2 ON ...
 ... JOIN T3 ON ...
 ...

您的範例與該模式匹配,因此結論也適用於它。然而,有一些變化值得一提,我們的結論不適用,或者至少不那麼直截了當。

1.嵌套JOIN語法

從語法上講,可以在另一個連接中指定連接,如下所示:

FROM
 T1
 JOIN
   T2
   JOIN T3 ON ..
 ON ...

在上述情況下,JOIN T2是之前遇到的JOIN T3。然而,前一個連接的聲明在這一點上並不完整:它的ON子句是末尾的那個,並且僅在該JOIN T3 ON ...部分之後進行邏輯評估。所以在這種情況下,T2首先連接到T3,然後連接的結果連接到T1

您仍然可以爭辯說我們的結論站在這裡,儘管在這種情況下它並不那麼明確。我們得出結論,連接是按照它們在子句中指定的順序進行評估的。FROM在這個例子中,我們在解析FROM子句時遇到的第一個連接,到第二個連接的時候還沒有完全指定。

2.混合逗號連接和正常連接

在引入顯式JOIN語法之前,連接是這樣指定的:

FROM
 T1,
 T2,
 T3
WHERE
 <joining conditions, filter conditions>

大多數(如果不是全部)平台(包括 SQL Server)仍然支持這種類型的連接,有時稱為逗號連接。

如果沒有連接條件,逗號連接本質上是交叉連接。連接條件使其成為內部連接。但是,您可以看到,在這種情況下,連接條件出現在一個完全不同的子句中,即WHERE子句。

現在,SQL Server 允許您在同一FROM個子句中混合使用逗號連接和正常連接。當您混合使用這樣的連接時:

FROM
 T1,
 T2
 JOIN T3 ON ... ,
 T4 ...

SQL Server 將在將它們全部交叉連接在一起之前獨立評估每個單獨的逗號分隔項。因此,在上述情況下,T2 JOIN T3 ON ...連接將在其結果被交叉連接之前進行評估T1(通過可能在WHERE子句中找到的任何連接條件進一步過濾)。我們的結論在這裡根本不適用。但是,您可以看到在這種情況下使用了非常不同的語法。

我在 Stack Overflow: The multi-part identifier could not be bound 的回答中更詳細地討論了混合語法。

引用自:https://dba.stackexchange.com/questions/251988