Terry Purcell 談外連接(第二部分)

引言
這是我的專題中的第二部分,在該專題中我想讓您更加容易地理解和使用 SQL 語言強大的外連接功能。

第一部分提供了內連接和外連接之間簡單的比較,並且也介紹了在外連接操作中用來說明表的新術語。最終,我論述了不同的謂詞類型以及在哪個階段 DB2® 可以應用它們。

在這個部分,我將提供在替換 NULL(NULL-supplying)的表上編寫謂詞的背景、DB2 可以如何簡化查詢來改善性能以及要確保獲得期望的結果所能採取的步驟。

左外連接和右外連接替換 NULL 的表謂詞

  • 外連接簡化
  • 保留 NULL
  • 連接前(before-join)謂詞

外連接簡化
回顧我在 第一部分中介紹的一些術語:

  • 保留行(preserved row)表是爲連接操作中不匹配的行保留行的表。
  • 替換 NULL的表是爲連接中不匹配的行提供 NULL 行的表。

 

然而,應用於保留行表的 WHERE 子句謂詞的最重要的屬性是 DB2 可以在連接之前或之後應用謂詞;應用於替換 NULL 的表的 WHERE 子句謂詞的效果卻非常不同,因爲如果該謂詞抵消了由外連接引入的 NULL,那麼它使 DB2 簡化連接。

要說明我說的這些是什麼意思,請參見 圖 13,該圖展示了引用替換 NULL 的表的 WHERE 子句謂詞的示例。


左外連接 - 連接簡化

對一個要限定的行,WHERE 子句謂詞的計算值必須爲 TRUE。如果在連接中出現不匹配的行,那麼來自替換 NULL 的表的列是 NULL。當 DB2 將 WHERE 子句謂詞 D.DEPTNAME NOT LIKE "%CENTER%" 與 NULL 進行比較時,其結果既不是 TRUE 也不是 FALSE,而是 UNKNOWN。因爲該行的計算值不是 TRUE,所以不返回該行。因而,左外連接(left outer join)提供的 NULL 被 WHERE 子句謂詞抵消了。這使 DB2 確定左外連接是不必要的,並使得查詢被重寫爲一個內連接,該內連接可能是或者可能不是您在編寫該查詢時想要的。

DB2 將左外連接重寫爲內連接的好處是可以改善性能。應用到表 D 的謂詞現在可以應用在連接之前(而不是之後),因爲該謂詞現在引用一個不替換 NULL 的表。保留行和替換 NULL 對於內連接是不相關的術語;假定在連接中將不返回不匹配行,即,所有表都是不替換 NULL 的。

保留 NULL
如果該外連接簡化沒有產生您想要的結果 - 即,您要求返回 NULL(或可選)行 - 那麼使用 OR D.DEPTNAME IS NULL 來保留在答案集中的 NULL。

在 圖 14中展示了這樣的一個示例。


左外連接 - 保留 NULL

DB2 必須將 WHERE 子句謂詞應用在連接之後,因爲直到連接之後才知道行是匹配的(因而應用謂詞的第一部分 - D.DEPTNAME NOT LIKE "%CENTER%" )還是不匹配的(因而應用謂詞的第二部分 - OR D.DEPTNAME IS NULL )。

連接前謂詞
如果您選擇編寫 WHERE 子句謂詞,它被 DB2 在連接 之前應用到替換 NULL 的表上,那麼會發生什麼?

如果您這樣做,那麼沒有 WHERE 子句謂詞來限制保留行表上或最終結果中的行。您僅限制了來自替換 NULL 的表的行。 圖 15展示了這樣的結果。


替換 NULL 的表上的謂詞

圖 16展示如果您將連接前謂詞重新編寫爲一個 ON 子句謂詞,那麼返回相同的結果。


替換 NULL 的表上的謂詞 - 簡化的

是在連接前還是連接過程中過濾來自替換 NULL 的表的行只是一個性能問題,DB2 根據所使用的連接方法來決定採用這兩種方法中的哪一種。要產生正確的結果,兩種方法都是有效的。因爲行在連接中不匹配,所以在連接前除去該行不會影響輸出。

DB2 可以(從 V6 開始)合併任何不必要的嵌套表表達式,例如替換 NULL 的表的嵌套表表達式( 圖 15)被重寫爲單個查詢塊並被應用爲連接中或連接前謂詞( 圖 16)。

全外連接替換 NULL 的表謂詞

  • 外連接簡化
  • 保留 NULL
  • 連接前謂詞

外連接簡化
適用於左外連接和右外連接(right outer join)的外連接簡化的規則對全連接也是有效的。應用於替換 NULL 的表並使 NULL 被抵消的 WHERE 子句謂詞使 DB2 簡化連接。因爲兩個表都提供 NULL,所以不管該子句應用於哪個表,它都可以抵消 NULL。

圖 17展示了 WHERE 子句謂詞的一個示例,該 WHERE 子句謂詞應用於全外連接(full outer join),它使 DB2 簡化連接。


全外連接 - 連接簡化

如果不使用連接簡化,該謂詞應用爲完全連接後的(totally-after-join)謂詞。假定優化器可以確定該謂詞抵消 NULL,那麼它可以將查詢重寫爲左外連接。根據選擇的表連接順序,全外連接可以重寫爲左或右外連接。

將查詢重寫爲左外連接意味着 WHERE 謂詞現在可以應用爲連接前謂詞,使得連接的行更少了。如果 WHERE 子句謂詞抵消了來自兩個表的 NULL,那麼簡化使查詢被重寫爲一個內連接。將該查詢作爲內連接執行允許兩個表上的謂詞在連接前被應用。

可以通過 explain(計劃表)輸出的 JOIN_TYPE 列來標識連接簡化。值“l”表明該連接已經被簡化爲左外連接(因爲沒有運行時右外連接),而“空白”指示該連接簡化爲內連接。

保留 NULL
如果您要求在結果集中返回來自兩個表的 NULL,那麼請更改 WHERE 子句謂詞以確保這些 NULL 不被除去。

圖 18展示了 WHERE 子句謂詞的一個示例,該 WHERE 子句謂詞確實保留了 NULL。COALESCE 返回列表中第一個非 NULL 的值。從而該 WHERE 比較始終以一個實際的值爲參照(除非該列定義爲允許 NULL)。


全外連接 - 保留 NULL

在 圖 18中展示的示例中,WHERE 子句謂詞完全地在連接後應用,因爲該 WHERE 謂詞依賴於來自兩個表的列。

還有更好的選擇嗎?

連接前謂詞
通過編寫查詢來將這些謂詞應用在連接前,您可以獲得更好的性能(如 圖 19所示)。


全外連接 - 保留 NULL 的另一個選擇

當編寫多個外連接時要小心

  • 丟失的行
  • 查找丟失的行

丟失的行
在 第一部分中,我提到當編寫任何 SQL 語句時,主要着重於得到正確的結果。當有多個連接涉及到外連接時,很容易就“意外地”丟失基於連接謂詞的來源的行。

當然,我已經說明對於不匹配的行,來自以 NULL 替換的表的列將是 NULL。如果來自以 NULL 替換的表的列在後繼連接中被引用爲連接謂詞,那麼 NULL 將永遠不會滿足等式,從而將不會進行更多的匹配。如果不詢問數據,可能無法指出丟失了這些行,因爲外連接簡化可能不是 DB2 所必需的。外連接簡化至少在計劃表中是可以被識別的(基於 JOIN_TYPE 列)。

圖 20展示一個在後繼連接中使用的以 NULL 替換的列的示例。

多個外連接 - 丟失的行

step 1 應用連接前謂詞。step 2 執行左外連接。在此示例中 department 和 employee 表的左外連接(step 2)不產生匹配行。從而,當執行 step 3 時(到 project 表的後繼左外連接),以前連接的連接謂詞的值是 NULL,因爲它來自以 NULL 替換的表。然而,該行仍然被保留着,因爲它是左外連接。而對於內連接,將不保留該行。

查找丟失的行
如果您確保後繼的連接謂詞始終引用來自保留行表的列,真實的值將對於後續的連接可用。確保在連接中返回正確的行是極其重要的一點。

圖 21展示了一個示例,其中第二個連接引用來自保留行表的連接謂詞。

多個外連接 - 查找丟失的行

在此示例中,我改正了前一個示例( 圖 20)的錯誤。step 2(第一個左外連接)產生的數據包含來自保留行表的實際值“D01”。該值在 step 3(後繼左外連接)中被用來與 project 表作比較,而不是 NULL。

此查詢的連接順序上的相關性要求首先訪問 department 表。DB2 for z/OS™ 優化器(從 V6 開始)能夠確定基於最低成本的剩餘連接順序,而不是編碼順序。突出顯示爲 step 2 和 step 3 的每個連接僅依賴於 department 表,而它們之間並不互相依賴。 

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