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

Sql 重新编写oracle查询以避免多表扫描,sql,oracle,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 v

我希望大家都很好,学习更多。我需要一些关于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\u名称返回给我部门id。我需要那些部门id,其中经理级别与传递的emp名称相同,但不需要在结果数据集中具有相同的emp名称

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';
我想要相同的结果集,但需要重写上面的查询,以避免部门表扫描两次。这只是一个示例查询,但实时扫描大表两次会带来性能问题。我想以一种更好的方式重新编写这个查询,避免同一个表扫描两次

你能帮忙提供你的建议或解决方案吗?我将非常感谢所有的回复


感谢您仔细阅读此问题。

如果希望查询更高效,请使用索引:

create index idx_department_name_level on department(emp_name, manager_level)


此外,您还有一个冗余的空检查,这可能会避免索引

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';
发布解释计划以获得更多帮助

这应该可以:

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
;
查询执行以下操作:

计算EmpA在给定经理级别中出现的次数 将所有记录保存在至少出现一次EmpA的经理级 不包括EmpA记录本身 正在运行的查询的SQL FIDLE:
您可以验证执行计划是否只包含一次完整的表扫描。

第一个建议是子查询中的DISTINCT根本不起作用。此外,除了=,您不需要检查是否不为NULL从不与空匹配。你想用那个NVL做什么?嗨,兰迪,谢谢你的回复。NULL检查是必需的,因为我也需要考虑EMPNENEX的NULL值。这只是一个示例场景,解释计划在这种情况下没有帮助,因为我无法在这些示例表上创建索引。我正在寻找查询重写,以避免表扫描两次。谢谢戈登的建议。我将尝试实现这一点,并检查性能差异。谢谢你,骆驼。这个查询很有帮助。我将检查这方面的性能。
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';
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
;