重写oracle查询以避免多表扫描

时间:2015-07-21 15:35:57

标签: sql oracle

我希望每个人都很好并且学到更多。我需要一些关于select语句和微调的建议。我使用的是Oracle 11gR2。请查看下表和数据脚本。

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

我想要相同的结果集,但需要以避免部门表扫描两次的方式重写上面的查询。这只是示例查询,但实时扫描大表两次会产生性能问题。我想以一种更好的方式重写这个查询,并避免两次相同的表扫描。

您能否提供有用的建议或解决方案?我将非常感谢所有回复。

感谢您回答这个问题。

3 个答案:

答案 0 :(得分: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)

答案 1 :(得分: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';

发布您的解释计划以获得更多帮助

答案 2 :(得分: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
  2. manager_level出现的次数
  3. 保留所有记录中manager_level至少出现一次EmpA
  4. 的记录
  5. 排除EmpA个记录本身
  6. 操作中查询的SQL小提琴:http://sqlfiddle.com/#!4/a9e03/7
    您可以验证执行计划仅包含一次全表扫描。