2015-07-21 69 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