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

2021-04-13 13:00:59 字數 3765 閱讀 1280

引言

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

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

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

左外連線和右外連線替換 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中展示了這樣的乙個示例。

db2 必須將 where 子句謂詞應用在連線之後,因為直到連線之後才知道行是匹配的(因而應用謂詞的第一部分 -d.deptname not like "%center%")還是不匹配的(因而應用謂詞的第二部分 -or d.deptname is null)。

連線前謂詞

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

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

圖 16展示如果您將連線前謂詞重新編寫為乙個 on 子句謂詞,那麼返回相同的結果。

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

db2 可以(從 v6 開始)合併任何不必要的巢狀表表示式,例如替換 null 的表的巢狀表表示式( 圖 15)被重寫為單個查詢塊並被應用為連線中或連線前謂詞( 圖 16)。

全外連線替換 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)。

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

還有更好的選擇嗎?

連線前謂詞

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

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

丟失的行

在 第一部分中,我提到當編寫任何 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 表,而它們之間並不互相依賴。 

MySQL 表的內連和外連

內連線實際上就是利用 where 子句對兩種表形成的笛卡兒積進行篩選 標準語法 select 字段 from 表1 inner join 表2 on 連線條件 and 其他條件 例如 顯示 ld 的名字和部門名稱 前面練習過的 用標準寫法 select ename,dname from emp in...

MySQL表的內連和外連

內連線實際上就是利用where子句對兩種表形成的笛卡兒積進行篩選,我們前面學習的查詢都是內連線,也是在開發過程中使用的最多的連線查詢。select 字段 from 表1 inner join 表2 on 連線條件 and 其他條件 案例 顯示smith的名字和部門名稱 用前面的寫法 select e...

MySQL 之 表的內連和外連

資料庫中表的連線分為內連 1.內連線 內連線實際上就是利用where子句對兩種表形成的笛卡兒積進行篩選。語法 select 字段 from 表1 inner join 表2 on 連線條件 and 其他條件 eg 顯示smith的名字和部門名稱 用標準的內連線寫法 select ename,dnam...