嵌套 SQL - 更快?

Nested SQL - faster?

提问人:Kingsley Obeng 提问时间:7/18/2023 最后编辑:jarlhKingsley Obeng 更新时间:7/19/2023 访问量:52

问:

我有一个 SQL 片段,它需要很长时间才能运行,因为它是嵌套的。我只是想知道,是否有办法简化它,让它运行得更快。

下面是 SQL:

select nid 
from table_A 
where bid in (select bid
              from Table_B
              where xid in (select xid 
                            from Table_C 
                            where name_l in (select name_l
                                             from accs 
                                             where prod like '%arlehen%')))

提前致谢。

SQL Oracle 嵌套

评论

0赞 jarlh 7/18/2023
创建适当的索引。
1赞 NickW 7/18/2023
查看查询的 EXPLAIN PLAN,并确定哪些部分速度较慢
2赞 astentx 7/18/2023
这与嵌套无关,因为我假设 Oracle 对所有四个表进行半连接(其中 semi 代表连接,它返回主表的第一个匹配行),这在某种程度上等于普通 .问题出在错误的估计上,因此请添加实际执行计划,其中包含估计的行数和每个的实际行数joinjoin
3赞 Paul W 7/18/2023
没有一种“更快”的方法来编写 SQL,而不是单独考虑另一种方式。什么最快完全取决于您的数据分布、基数以及 Oracle 根据其拥有的统计数据生成的执行计划。你必须查看计划,确定它把时间花在哪里,然后想出改进它的方法。如果总有一种方法可以重写 SQL,使其始终更快,那么 Oracle 每次都会为您完成。但事实并非如此。
1赞 Paul W 7/18/2023
话虽如此,如果这个谓词 : 相当唯一,那么您将需要索引 table_c.name_l、table_b,xid 和 table_a.bid。但是,如果它相当普遍,那么它应该是哈希连接所有内容,然后你不需要索引。此外,如果 accs 非常大,您可能将大部分时间花在该表的完全扫描上,该表无法使用索引,因为您的 LIKE '%...谓语。prod like '%arlehen%'

答:

1赞 Bogdan Dincescu 7/18/2023 #1

我认为查询可以写成:

select nid 
from table_A 
where bid in (
  select b.bid
  from Table_B b
  join Table_C c on b.xid=c.xid
  join accs on c.name_l=accs.name_l
  where accs.prod like '%arlehen%');

新的统计信息,即不是过时的统计信息,可能有助于优化程序选择最佳执行路径。

或者,重写查询的另一种方法是:

with tba as (
select a.*
from accs accs
where a.prod like '%arlehen%'
)
select nid 
from table_A 
where bid in (
  select b.bid
  from Table_B b
  join Table_C c on b.xid=c.xid
  join tba on c.name_l=tba.name_l)
;

评论

2赞 astentx 7/18/2023
但它没有回答为什么它应该提高查询性能,这可能会导致 XY 问题: dbfiddle.uk/PbzO3WIl
0赞 Bogdan Dincescu 7/18/2023
好吧,查询性能可能会提高,也可能不会。有时,即使统计信息未过时,优化程序也可能无法选择最佳执行计划,或者您可能经常在统计信息过时时运行该查询,在这种情况下,以不同的方式编写查询或使用提示可能会有所帮助。如果性能更好,则可以通过运行每个查询来查看。
1赞 astentx 7/18/2023
然而,优化的基础是查询计划。Oracle 的优化器具有非常复杂的查询转换功能(尤其是当实际是视图时),因此我通常仅使用手动添加的提示(用于连接顺序、访问路径)来手动重写此类(有点简单)的查询,以防止表的重新排序不充分table_x
0赞 p3consulting 7/19/2023 #2

您可以尝试将 IN() 替换为 EXISTS(),这可能会降低 I/O 成本 - 当然,要在您的数据集上进行检查:

select nid 
from table_A a
where exists (
    select 1
    from Table_B b
    where b.bid = a.bid 
    and exists (
        select 1 
        from Table_C c
        where b.xid = c.xid
        and exists (
            select 1
             from accs ac
             where ac.prod like '%arlehen%'
             and ac.name_l = c.name_l
        )
    )
)
;