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

问题描述 投票:1回答: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

    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表中执行完整扫描,而对于其他表,它将执行唯一或范围扫描。

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

sql oracle query-optimization
2个回答
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.大的差异表示要调查的区域。


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%确定哪一个会更快。根据我的理解,加入中的那个会更快......

© www.soinside.com 2019 - 2024. All rights reserved.