Oracle SQL如何优化IN不相关的子查询

时间:2017-12-20 13:16:51

标签: sql oracle query-optimization

我有以下查询表现不佳:

select 
    distinct 
    u.uuid
    u.user_name,
    u.key

    from request req 
         join int_user u on u.uuid = req.user_uuid
         join int_right r on r.uuid = req.right_uuid

    where r.uuid in (
            select r2.uuid from int_right r2
                    where 
                            (
                                lower(r2.right_name) like '%keyword%'
                                or lower(r2.right_key) like '%keyword%'
                            )

                    )

子查询是不相关的,它通常会返回几行,有时只返回一行。 现在我不明白为什么如果我采取子查询并单独执行它然后获取结果列表并使用 IN 运算符静态添加到外部查询然后它将执行得非常好,从3- 6s执行时间降至0.05s。

r.uuid in ('value1', 'value2', 'value3')

我如何告诉oracle首先执行我的子查询然后将结果集应用于外部查询?

很少注意到:

  • 请求表很大 - 大约7毫升行
  • Int_right表 - 大约10K行
  • Int_user表 - 大约100K行

从执行计划来看,oracle似乎在所有表中都进行了全面扫描。请求表上的成本和基数非常大。 有趣的是,即使我的子查询为某个搜索条件返回单行,查询仍然很慢,但是如果我用等于(=)替换 IN 运算符然后查询变得非常快并且成本低。在这种情况下,oracle似乎只会在 int_right 表中执行完整扫描,而对于其他表,它将执行唯一或范围扫描。

我也尝试过这个查询的其他变体,比如直接向外部查询添加条件,使用存在或相关的子查询,但无论如何它仍然很慢。

2 个答案:

答案 0 :(得分:2)

为什么需要子查询?

可以通过两种不同的方式应用相同的条件:

加入

select 
    distinct 
    u.uuid
    u.user_name,
    u.key

    from request req 
         join int_user u on u.uuid = req.user_uuid
         join int_right r on r.uuid = req.right_uuid 
         And (lower(r.right_name) like '%keyword%' or lower(r.right_key) like '%keyword%')

在哪里

select 
    distinct 
    u.uuid
    u.user_name,
    u.key

    from request req 
         join int_user u on u.uuid = req.user_uuid
         join int_right r on r.uuid = req.right_uuid 
    Where (lower(r.right_name) like '%keyword%' or lower(r.right_key) like '%keyword%')

虽然我不是100%确定哪一个会更快,但两者都会导致更快的查询。根据我的理解,加入中的那个会更快......

答案 1 :(得分:0)

您通常无法通过查看文本来调整SQL语句(除非代码中存在基本缺陷,例如缺少连接条件等)。对于Oracle,最有效的方法之一是:

1)使用下面的附加提示执行有问题的陈述

select /*+ gather_plan_statistics */ ... <rest of query>

2)运行以下内容以获取执行计划指标

select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'))

那样你会:

a)查看使用的真实执行计划,

b)获取计划中每个步骤的估计/实际行来源计数。如果估计值与实际值之间存在偏差,那么这通常是集中注意力的地方,因为优化程序很可能没有足够或准确的信息来处理。

例如

SQL> select /*+ gather_plan_statistics */ count(dname)
  2  from  scott.emp e, scott.dept d
  3  where e.sal <= 1500
  4  and  d.deptno = e.deptno;

COUNT(DNAME)
------------
           7

1 row selected.

SQL> select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'));

PLAN_TABLE_OUTPUT
---------------------------------------------------------------------------------------------------
SQL_ID  c1cb4s8b141h8, child number 0
-------------------------------------
select /*+ gather_plan_statistics */ count(dname) from  scott.emp e,
scott.dept d where e.sal <= 1500 and  d.deptno = e.deptno

Plan hash value: 3037575695

---------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name    | Starts | E-Rows | A-Rows |   A-Time   | Buffers |
---------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |         |      1 |        |      1 |00:00:00.01 |       9 |
|   1 |  SORT AGGREGATE               |         |      1 |      1 |      1 |00:00:00.01 |       9 |
|   2 |   MERGE JOIN                  |         |      1 |      3 |      7 |00:00:00.01 |       9 |
|   3 |    TABLE ACCESS BY INDEX ROWID| DEPT    |      1 |      4 |      4 |00:00:00.01 |       2 |
|   4 |     INDEX FULL SCAN           | DEPT_PK |      1 |      4 |      4 |00:00:00.01 |       1 |
|*  5 |    SORT JOIN                  |         |      4 |      3 |      7 |00:00:00.01 |       7 |
|*  6 |     TABLE ACCESS FULL         | EMP     |      1 |      3 |      7 |00:00:00.01 |       7 |
---------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   5 - access("D"."DEPTNO"="E"."DEPTNO")
       filter("D"."DEPTNO"="E"."DEPTNO")
   6 - filter("E"."SAL"<=1500)

在第6行,您可以优化估计 3行,但实际上已经返回7.大的差异表示需要调查的区域。