如何优化这个嵌套的 SQL SELECT 查询

Posted

技术标签:

【中文标题】如何优化这个嵌套的 SQL SELECT 查询【英文标题】:How to optimize this nested SQL SELECT query 【发布时间】:2019-04-19 13:09:38 【问题描述】:

我有一个非常慢的查询,如下所示:

SELECT  *
FROM    (
         SELECT ..., nn_key_fast(nachname) nnk, ...
         FROM   t1
           JOIN t2 ON
               ...
           JOIN t3 ON
               ...
           JOIN t4 ON
               ...
         WHERE  ...
         AND t4.POSTCODE='1234'
        )
WHERE  ... AND nnk LIKE "N%"

现在,内部选择大约需要 2 分钟。如果我删除最后一个 WHERE 子句(t4.POSTCODE),它会在 ~4 秒内执行。没有这个子句的结果将是

所以,我的想法是:将该子句移到外部 SELECT,然后它只会应用于生成的

但是没有。查询花费的时间完全一样,所以要清楚:

SELECT  *
FROM    (
         SELECT  .....
         FROM    t1
           JOIN  t2 ON
                 ...
           JOIN t3 ON
                 ...
           JOIN t4 ON
                 ...
         WHERE   ... 
        )
WHERE  ...
AND POSTCODE='1234'

与第一个版本一样需要 2 分钟。

这对我来说似乎很疯狂。

直观地说,这必须由查询优化器执行,如下所示: 从内部选择创建一个表,如下所示:

CREATE TABLE res_from_inner AS (
                                SELECT .....
                                FROM t1
                                  JOIN t2 ON
                                       ...
                                  JOIN t3 ON
                                       ...
                                  JOIN t4 ON
                                       ...
                                WHERE ... 
                               )

...然后只在这个表上做外部选择,像这样:

SELECT  *
FROM    res_from_inner
WHERE   POSTCODE='1234'

如果我手动执行此操作,则 CREATE TABLE 查询需要大约 4 秒,而第二个 SELECT 需要,如预期的那样

这怎么可能,怎么办?

【问题讨论】:

我猜你的索引中没有包含邮政编码。 子查询的 select 子句中有什么 --> SELECT ..... ?如果您在 SELECT 子句中使用任何函数或表达式(如 to_char( x ) as yx+y as z),然后您在 WHERE 子句中使用其结果(如 y = '123' or z=22,那么 Oracle 无法将谓词下推到子查询、索引没用,而且一定很慢。没有详细信息,很难在这里提出任何建议,请显示完整的查询,它是execution plan,现在我投票结束你的问题,因为它太宽泛了。 @krokodilko:现在感觉有点傻……你说得对,select子句中有一个函数,计算的字段用在外层select中。但是,当我执行整个查询时,今天需要 13 秒...但是:如果我从该选择创建一个表(创建表为...),则再次需要 2 分钟。结果集中只有大约 1000 条记录。抱歉让您感到困惑,我不确定自己到底在问什么。 【参考方案1】:

您必须查看执行计划才能了解实际情况。事情正在发生变化。

您可以尝试的一件事是 CTE:

with s as (
      <subquery here>
     )
select s.*
from s
where . . .;

Oracle 可能会自动实现这一点。或者你可以给个提示:

with s as (
      select /*+ materialize */ . . .
      . . .
     )
select s.*
from s
where . . .;

【讨论】:

感谢您的提示。以前从未使用过 WITH ,但这似乎相当简单。但也从未听说过 materialize-hint,这似乎从 V10g 开始就变得不必要了(据此:dba-oracle.com/t_materialize_sql_hint.htm)。有趣的东西。【参考方案2】:

我尽量远离嵌套的 select 语句。在这种情况下,我会将内部 select 语句作为子查询,然后从中选择我想要的内容。

子查询为 ( 选择...从...加入...加入...加入...在哪里 )

从子查询中选择*

--希望有所帮助:]

【讨论】:

谢谢。将来会尝试将我的嵌套选择(我们经常使用它们)重构为 WITH CTE。这看起来干净多了。

以上是关于如何优化这个嵌套的 SQL SELECT 查询的主要内容,如果未能解决你的问题,请参考以下文章

如何优化这个嵌套的 SQL 查询

优化sql嵌套查询,使用CTE语法

如何优化执行嵌套在 group-by 子句中的计数的 SQL 查询?

优化嵌套sql查询

优化 Oracle SQL 查询

如何使用嵌套的 SELECT 子查询优化 UPDATE?