1. 程式人生 > >Oracle 查詢優化器 -- 表連線方法

Oracle 查詢優化器 -- 表連線方法

-- Start

迴圈巢狀連線(Nested Loop Joins)

首先,看看下面的 SQL 語句。

SELECT e.first_name, e.last_name, e.salary, d.department_name
FROM hr.employees e, hr.departments d
WHERE d.department_name IN ('Marketing', 'Sales')
AND e.department_id = d.department_id;

對於迴圈巢狀,你可以把表想象成陣列,Oracle 會採用如下的方式執行查詢。
String[] departments = {};
String[] employees = {};

// 外層迴圈
for(String dep: departments) {
	// 內層迴圈
	for(String emp: employees) {
		
	}
}

很明顯,如果 employees 很大且沒有索引,外層迴圈每執行一次都需要全表掃描 employees,這是不可接受的。所以迴圈巢狀表連線方式適合那些內層迴圈資料量少且有索引的情形。

當然,你也可以通過下面的方式建議 Oracle 採用迴圈巢狀連線方式。

-- USE_NL
SELECT /*+ USE_NL(e d) */ e.first_name, e.last_name, e.salary, d.department_name
FROM hr.employees e, hr.departments d
WHERE d.department_name IN ('Marketing', 'Sales')
AND e.department_id = d.department_id;

--USE_NL_WITH_INDEX,指定 e 為內層迴圈表
SELECT /*+ USE_NL_WITH_INDEX(e) */ e.first_name, e.last_name, e.salary, d.department_name
FROM hr.employees e, hr.departments d
WHERE d.department_name IN ('Marketing', 'Sales')
AND e.department_id = d.department_id;

--USE_NL_WITH_INDEX,指定 e 為內層迴圈表,同時指定索引
SELECT /*+ USE_NL_WITH_INDEX(e emp_dep_id_idx) */ e.first_name, e.last_name, e.salary, d.department_name
FROM hr.employees e, hr.departments d
WHERE d.department_name IN ('Marketing', 'Sales')
AND e.department_id = d.department_id;

你還可以通過下面的方式建議 Oracle 不要採用迴圈巢狀連線方式。
SELECT /*+ NO_USE_NL(e d) */ e.first_name, e.last_name, e.salary, d.department_name
FROM hr.employees e, hr.departments d
WHERE d.department_name IN ('Marketing', 'Sales')
AND e.department_id = d.department_id;

雜湊連線(Hash Joins)

首先,看看下面的 SQL 語句。

SELECT o.customer_id, l.unit_price * l.quantity
FROM orders o ,order_items l
WHERE l.order_id = o.order_id;

雜湊連線會採用如下步驟。

第一步,根據小表在記憶體中建立一個 Hash 表。類似下面的 Java 程式碼。

// Hash 表
Map hash_join = new HashMap();

// 訂單表
Integer[] order_ids = {};

// 填充 Hash 表
for(Integer id: order_ids) {
	hash_join.put(id, -999);
}

第二步,掃描大表,建立對應關係。
// Hash 表
Map hash_join = new HashMap();

// order_items 表
Integer[] order_items = {};

// 掃描 order_items 表,建立對應關係
for(Integer id: order_items) {
	
	// 注意這裡是重點:根據 Hash 演算法檢索資料
	Integer value = hash_join.get(id);
	if(value == null) {
		continue;
	} else {
		hash_join.put(id, value); // 建立對應關係
	}
}

第三步,根據 Hash 表的對應關係返回資料。

雜湊連線適合大表連線,但是連線條件必須是等價條件。你可以通過下面的方式建議 Oracle 採用雜湊連線方式。

SELECT /*+ USE_HASH(o l) */ o.customer_id, l.unit_price * l.quantity
FROM orders o ,order_items l
WHERE l.order_id = o.order_id;

你也可以通過下面的方式建議 Oracle 不要採用雜湊連線方式。
SELECT /*+ NO_USE_HASH(o l) */ o.customer_id, l.unit_price * l.quantity
FROM orders o ,order_items l
WHERE l.order_id = o.order_id;

排序合併連線(Sort Merge Joins)

排序合併連線分兩步,第一步把兩個表根據連線條件排序,第二步把排序後的表合併。通常,雜湊連線的效能要比排序合併好,但是雜湊連線條件必須是等價條件,而排序合併連線條件可以是任何條件。此外,在下面的情況下會考慮使用排序合併連線。

1.要連線的兩個集合已經排序了
2.結果集需要排序

你可以通過下面的方式建議 Oracle 採用排序合併連線。

SELECT /*+ USE_MERGE(employees departments) */ *
FROM employees, departments
WHERE employees.department_id = departments.department_id;

你也可以通過下面的方式建議 Oracle 不要採用排序合併連線。
SELECT /*+ NO_USE_MERGE(employees departments) */ *
FROM employees, departments
WHERE employees.department_id = departments.department_id;

笛卡兒連線(Cartesian Joins)

如果兩個表沒有指定連線條件,Oracle 會使用笛卡兒連線。結果集是兩個錶行數的乘積。

外連線(Outer Joins)

外連線包括左外連線,右外連線,全外連線。對於左外連線和右外連線,Oracle 根據統計資訊來決定連線方式,包括迴圈巢狀外連線(Nested Loop Outer Joins),雜湊外連線(Hash Join Outer Joins), 排序合併外連線(Sort Merge Outer Joins)。但是對於全外連線,從11g開始,Oracle 預設採用基於雜湊演算法的本地化執行方法(native execution method),你也可以通過下面的方式提示 Oracle 採用或不採用本地化執行方法。

NATIVE_FULL_OUTER_JOIN
NO_NATIVE_FULL_OUTER_JOIN

-- 宣告:轉載請註明出處

-- Last edited on 2015-07-21

-- Created by ShangBo on 2015-07-21

-- End