2015-07-21 43 views
3

我希望大家都很好,學習更多。我需要一些關於選擇聲明和微調的建議。我正在使用Oracle 11gR2。請查找下表和數據腳本。重寫oracle查詢以避免多表掃描

create table employee (emp_id number, emp_name varchar2(50), manager_id number); 
create table department (dept_id number, dept_name varchar2(50), emp_name varchar2(50), manager_level varchar2(20)); 
create table manager_lookup (manager_level_id number, manager_level varchar2(20)); 
insert into employee values (1, 'EmpA',3); 
insert into employee values (2, 'EmpB',1); 
insert into employee values (3, 'EmpC',1); 
insert into employee values (4, 'EmpD',2); 
insert into employee values (5, 'EmpE',1); 
insert into employee values (6, 'EmpF',3); 
insert into department values (1, 'DeptA','EmpD','Level3'); 
insert into department values (2, 'DeptB','EmpC','Level2'); 
insert into department values (3, 'DeptC','EmpA','Level1'); 
insert into department values (4, 'DeptD','EmpF','Level1'); 
insert into department values (5, 'DeptD','EmpA','Level3'); 
insert into department values (6, 'DeptA',NULL,'Level3'); 
insert into manager_lookup values (1, 'Level1'); 
insert into manager_lookup values (2, 'Level2'); 
insert into manager_lookup values (3, 'Level3'); 
commit; 

下面的查詢通過傳遞一些emp_name返回給我dept_id。我需要dept_id,其中manager_level與emp_name相同,但不需要在結果數據集中具有相同的emp_name。

SELECT b.dept_id 
    FROM (SELECT DISTINCT manager_level 
      FROM department dpt 
     WHERE emp_name = 'EmpA' 
     and emp_name is not null) a, 
     department b 
WHERE  a.manager_level = b.manager_level 
     AND NVL (b.emp_name, 'ABC') <> 'EmpA'; 

上面的查詢將返回我的數據設置如下:

dept_id 
-------- 
1 
4 
6 

我想同樣的結果集,但需要重寫上述查詢的方式,以避免部門表掃描兩次。這只是示例查詢,但實時掃描大表兩次會導致性能問題。我想以一種更好的方式重寫這個查詢,並避免兩次相同的表掃描。

你能幫忙提供你的精彩建議或解決方案嗎?我會非常感謝所有迴應。

謝謝你回答這個問題。

+0

第一個建議是,子查詢中的「DISTINCT」什麼都不做。 –

+0

另外,除了'='以外,您不需要檢查'IS NOT NULL'。 '='永遠不會匹配'NULL'。 –

+0

你想用這個'NVL'做什麼? –

回答

1

如果您想查詢更有效,然後使用索引:

create index idx_department_name_level on department(emp_name, manager_level) 

create index idx_department_name_level on department(manager_level, emp_name, dept_id) 
+0

謝謝Gordon的建議。我會盡力實現這一點,並檢查性能差異。 – Keen2Learn

1

也,你有多餘的空檢查可避免指數...

SELECT b.dept_id 
    FROM (SELECT manager_level 
      FROM department dpt 
     WHERE emp_name = 'EmpA') a, 
     department b 
WHERE  a.manager_level = b.manager_level 
     AND NVL (b.emp_name, 'ABC') <> 'EmpA'; 

發佈您的解釋計劃以獲取更多幫助

+0

嗨蘭迪,謝謝你的回覆。需要空檢查,因爲我需要考慮emp_name的空值。這只是一個示例場景,解釋計劃在這種情況下不起作用,因爲我在這些示例表上創建了索引。我正在查詢重寫,以避免表掃描兩次。 – Keen2Learn

1

這應該工作:

SELECT a.* 
FROM 
(
    SELECT d.*, 
     SUM(CASE WHEN emp_name = 'EmpA' THEN 1 ELSE 0 END) 
      OVER (PARTITION BY manager_level) AS hits 
    FROM department d 
) a 
WHERE hits > 0 
    AND NVL(emp_name, 'Dummy') <> 'EmpA' 
ORDER BY dept_id 
; 

查詢執行以下操作:

  1. 計算EmpA多少次出現在一個給定的manager_level
  2. 保留所有記錄與manager_level有至少一處EmpA其中
  3. 不包括EmpA自己記錄

行動查詢的SQL小提琴:http://sqlfiddle.com/#!4/a9e03/7
您可以驗證的執行計劃只包含一個全表掃描。

+0

謝謝你的美洲駝。這個查詢非常有幫助。我會檢查這方面的表現。 – Keen2Learn