【问题标题】:Oracle DB: Why does rewriting this query a bit make it many times faster?Oracle DB:为什么稍微重写这个查询会使它快很多倍?
【发布时间】:2018-08-31 11:14:23
【问题描述】:

我有一个查询需要大约 90 秒才能执行。以不同的方式稍微重写查询后,查询运行时间为 1.5 秒。你能解释为什么它运行得更快吗?

上下文

我有一个查询,它获取记录列表 - 将这些记录连接到资产列表,并将这些资产连接到人员列表,并将这些人员连接到另一个人员列表(他们的经理)。

为了便于说明,假设我们正在讨论一个新的销售线索列表,并且我们希望将这些线索加入到我们正在销售的服务中,以及这些服务的所有者和他们的经理.

表格

我们要从中提取 3 个对象:

1) 'sales_leads' 表 - 一个简单的本地表,只有一个索引:

  • 它有外键“asset_id”和“is_live”等。

  • asset_id 已编入索引

  • 它有大约 5k 行

2) 'services' 表 - 一个简单的、本地的其他东西表:

  • 它有被索引的主键“asset_id”(以及其他一些列)
  • 它有“可用性”和“状态”等列
  • 它具有“asset_owner”的值 -
  • 此表有大约 50,000 行,其中大部分此查询不会返回

3) 'vw_people_Data' 视图 - 包装远程表的视图:

  • vw_people_Data 本质上只是(创建视图 vw_people_Data 作为 select * from our_people_table@our_database_link)

  • 此表同时包含所有者和管理者(以及其他所有人)

  • 此表有大约 80,000 行,其中大部分此查询不会返回

慢查询

此查询在约 90 秒内返回 557 行:

    SELECT * 
    FROM sales_leads leads
    LEFT OUTER JOIN services srvc ON (leads.asset_id = srvc.asset_id)
    LEFT OUTER JOIN 
      (
        SELECT 
            c.emp_id,
            c.display_name,
            c.primary_email_address,
            c.functional_manager_emp_id,
        FROM vw_people_Data c
      )m1 ON (m1.emp_id = srvc.asset_owner)
    LEFT OUTER JOIN 
      (
        SELECT 
            c.emp_id, 
            c.primary_email_address 
        FROM vw_people_Data c
      )m2 ON m1.functional_manager_emp_id = m2.emp_id

    WHERE srvc.availability like 'A%'
    AND srvc.status = 'true'
    AND leads.is_live like 'Live'
    ;

“快速”查询:

此查询在 1.5 秒内返回 557 行:

    SELECT * 
    FROM sales_leads leads
    LEFT OUTER JOIN services srvc ON (leads.asset_id = srvc.asset_id)
    LEFT OUTER JOIN 
          (
            SELECT 
              m1.emp_id m1_emp_id,
              m1.display_name m1_display_name,
              m1.primary_email_address m1_email,
              m2.emp_id m2_emp_id,
              m2.primary_email_address m2_email
            FROM vw_people_Data m1
            LEFT OUTER JOIN 
              (
                    SELECT 
                      c.emp_id,
                      c.primary_email_address
                    FROM vw_people_Data c
              ) m2 ON (m1.functional_manager_emp_id = m2.emp_id)
          ) m1_m2 ON (srvc.asset_owner = m1_m2.m1_emp_id)

    WHERE srvc.availability like 'A%'
    AND srvc.status = 'true'
    AND leads.is_live like 'Live'
    ;

问题:

为什么?为什么对查询进行如此小的重写会大大缩短查询时间?优化器在做什么如此不同?

我查看了执行计划,它们看起来一模一样。

【问题讨论】:

  • 您需要查看执行计划来回答这个问题。当您使用没有定义的视图时,所有优化选项的赌注都将被取消。
  • @GordonLinoff 唯一的视图是定义为 select * from tbl@our_db_link 的 vw_people_Data ,如上所述
  • 您可以使用提示 /*+ collect_plan_statistics */ 创建一个包含行源统计信息的计划,例如,获取一个具有实际执行时间的计划。这至少应该显示时间花在哪里。
  • 这些时间是否可重复 - 或者第二个快速查询是否仅受益于第一个慢速查询执行期间缓存的数据?
  • 顺便说一句:有时即使是相同的执行计划也可能会因为运行时引擎的决定而导致不同的性能。例如,用于全表扫描的serial direct path access 在计划中将不可见,但可能会导致执行速度更快或更慢(尽管问题并未表明这种差异可以解释给定情况下的某些问题)

标签: sql oracle relational-database sqlperformance


【解决方案1】:

在没有看到这两个查询的解释计划的情况下调整建议是无稽之谈。但是让我们猜测一下。

看来重点是这样的:

唯一的视图是定义为
select *from tbl@our_db_link
................^

的vw_people_Data

对查询的“次要” 更改是对该视图的访问路径的重写,该视图查询远程数据库上的表。跨数据库链接的性能调整连接很棘手。基本上,远程数据库将其所有候选数据发送到本地数据库,在该数据库中评估连接并丢弃不匹配。

在您的第一个查询中,您在远程视图上有两个子查询,因此您可以将两个远程结果传送到本地数据库。在您的第二个版本中,您有一个在远程数据库中应用连接的子查询。因此,发送到本地数据库的结果集要小得多,因为所有不匹配的vw_people 记录都已被丢弃。

【讨论】:

    猜你喜欢
    • 2020-03-19
    • 1970-01-01
    • 2014-04-28
    • 1970-01-01
    • 1970-01-01
    • 2021-10-06
    • 1970-01-01
    • 2012-06-11
    • 2013-06-04
    相关资源
    最近更新 更多