【问题标题】:how to improve the execution time of this query?如何提高此查询的执行时间?
【发布时间】:2014-01-26 12:10:09
【问题描述】:

我有一个包含大约 11.000.000 条记录的维度,4 个表在 ETL 过程中相互连接以填充我的维度(dimtst)。

insert into dimtst
select .... from  tdpst left outer join fhist 
left outer join dp2act
left outer join dp2cust

上面的查询持续了很多(tdpst 有 11 条记录,上面的查询持续了大约 15 分钟),因此我创建了一个临时表加入 tdpst 和 fhist 并将结果存储在 tmpDpsInf(我创建的另一个临时表)中。

然后tmpDpsInf 将与C 和D 一起加入另一个有足够执行时间的查询。

create table TMPFHIST
(
  abrnchcod NUMBER(4) not null,
  tbdptype  NUMBER(3) not null,
  cfcifno   NUMBER(8) not null,
  tdserial  NUMBER(3) not null,
  aistate   NUMBER(1)
)

--------------
create table TMPDPSINF
(
  abrnchcod     NUMBER(4) not null,
  tbdptype      NUMBER(3) not null,
  cfcifno       NUMBER(8) not null,
  tdserial      NUMBER(3) not null,
  ausrcode      NUMBER(4) not null,
  tdtitle       VARCHAR2(82),
  tdopndat      DATE,
  tdrnwdat      DATE,
  tdclsdat      DATE,
  acurrcode     CHAR(3) not null,
  abu_abrnchcod NUMBER(4) not null,
  aistate       NUMBER(1)
)
create table tdpst  
(
  abrnchcod     NUMBER(4) not null,
  tbdptype      NUMBER(3) not null,
  cfcifno       NUMBER(8) not null,
  tdserial      NUMBER(3) not null,
  ausrcode      NUMBER(4) not null,
  tdtitle       VARCHAR2(82),
  tdopndat      DATE,
  tdrnwdat      DATE,
  tdclsdat      DATE,
  acurrcode     CHAR(3) not null,
  abu_abrnchcod NUMBER(4) not null
)
--------

tmpDpsInf fills with below query:
    insert into tmpDpsInf
      select /*+parallel(12)*/
       d.ABRNCHCOD,
       d.TBDPTYPE,
       d.CFCIFNO,
       d.TDSERIAL,
       d.AUSRCODE AUSRCODE,
       d.tdtitle,
       trunc(d.tdopndat) TDOPNDAT,
       nvl(d.tdrnwdat, d.tdopndat),
       nvl(d.tdclsdat, to_date('1500/01/01', 'yyyy/mm/dd')) tdclsdat,
       d.acurrcode acurrcode,
       d.ABU_ABRNCHCOD ABU_ABRNCHCOD,
       tmp.aistate
        from tdpst  d
        left outer join fhist tmp
          on d.ABRNCHCOD = tmp.ABRNCHCOD
         and d.TBDPTYPE =  tmp.TBDPTYPE
         and d.CFCIFNO =   tmp.CFCIFNO
         and d.TDSERIAL =  tmp.TDSERIAL
       where d.TDOPNDAT <= currdate

1 计划哈希值:3720425100 2
3 ------------------------------------------------- -------------------------------------------------- ---------------------- 4 |身份证 |操作 |姓名 |行 |字节 |成本 (%CPU)|时间 | TQ |进出| PQ 分配 | 5 ------------------------------------------------- -------------------------------------------------- ---------------------- 6 | 0 |选择声明 | | 12M| 1994M| 4248 (3)| 00:01:17 | | | | 7 | 1 | PX 协调员 | | | | | | | | | 8 | 2 | PX 发送 QC(随机) | :TQ10002 | 12M| 1994M| 4248 (3)| 00:01:17 | Q1,02 | P->S |质量控制 (兰德) | 9 |* 3 |哈希连接右外缓冲| | 12M| 1994M| 4248 (3)| 00:01:17 | Q1,02 | PCWP | | 10 | 4 | PX 接收 | | 3730K| 67M| 178 (3)| 00:00:04 | Q1,02 | PCWP | | 11 | 5 | PX 发送哈希 | :TQ10000 | 3730K| 67M| 178 (3)| 00:00:04 | Q1,00 | P->P |哈希 | 12 | 6 | PX 块迭代器 | | 3730K| 67M| 178 (3)| 00:00:04 | Q1,00 | PCWC | | 13 | 7 |表访问完全 | TMPFHIST | 3730K| 67M| 178 (3)| 00:00:04 | Q1,00 | PCWP | | 14 | 8 | PX 接收 | | 12M| 1774M| 4059 (2)| 00:01:14 | Q1,02 | PCWP | | 15 | 9 | PX 发送哈希 | :TQ10001 | 12M| 1774M| 4059 (2)| 00:01:14 | Q1,01 | P->P |哈希 | 16 | 10 | PX 块迭代器 | | 12M| 1774M| 4059 (2)| 00:01:14 | Q1,01 | PCWC | | 17 |* 11 |表访问完全 | TDPST | 12M| 1774M| 4059 (2)| 00:01:14 | Q1,01 | PCWP | | 18 -------------------------------------------------- -------------------------------------------------- ---------------------- 19
20 谓词信息(由操作 id 标识): 21 ------------------------------------------------- -- 22
23 3 - 访问("D"."TDSERIAL"="TMP"."TDSERIAL"(+) AND "D"."CFCIFNO"="TMP"."CFCIFNO"(+) AND 24 "D"."TBDPTYPE"="TMP"."TBDPTYPE"(+) AND "D"."ABRNCHCOD"="TMP"."ABRNCHCOD"(+)) 25 11 - 过滤器("D"."TDOPNDAT" 27 注意 28 ----- 29 - 用于此语句的动态采样(级别 = 4) 30 - 由于提示,并行度为 10


我用 /+leading (d,tmp)/hint 执行了上面的查询。计划如下:


计划哈希值:1033900074


|身份证 |操作 |姓名 |行 |字节 |成本 (%CPU)|时间 | TQ |进出| PQ 分发 |

| 0 |选择声明 | | 12M| 1994M| 4255 (3)| 00:01:17 | | | | | 1 | PX 协调员 | | | | | | | | | | 2 | PX 发送 QC(随机) | :TQ10002 | 12M| 1994M| 4255 (3)| 00:01:17 | Q1,02 | P->S |质量控制 (兰德) | |* 3 |哈希连接外部缓冲| | 12M| 1994M| 4255 (3)| 00:01:17 | Q1,02 | PCWP | | | 4 | PX 接收 | | 12M| 1774M| 4059 (2)| 00:01:14 | Q1,02 | PCWP | | | 5 | PX 发送哈希 | :TQ10000 | 12M| 1774M| 4059 (2)| 00:01:14 | Q1,00 | P->P |哈希 | | 6 | PX 块迭代器 | | 12M| 1774M| 4059 (2)| 00:01:14 | Q1,00 | PCWC | | |* 7 |表访问完全 | TDPST | 12M| 1774M| 4059 (2)| 00:01:14 | Q1,00 | PCWP | | | 8 | PX 接收 | | 3730K| 67M| 178 (3)| 00:00:04 | Q1,02 | PCWP | | | 9 | PX 发送哈希 | :TQ10001 | 3730K| 67M| 178 (3)| 00:00:04 | Q1,01 | P->P |哈希 | | 10 | PX 块迭代器 | | 3730K| 67M| 178 (3)| 00:00:04 | Q1,01 | PCWC | |

| 11 |表访问完全 | TMPFHIST | 3730K| 67M| 178 (3)| 00:00:04 | Q1,01 | PCWP | |

谓词信息(由操作id标识):

3 - 访问("D"."TDSERIAL"="TMP"."TDSERIAL"(+) AND "D"."CFCIFNO"="TMP"."CFCIFNO"(+) AND "D"."TBDPTYPE"="TMP"."TBDPTYPE"(+) AND "D"."ABRNCHCOD"="TMP"."ABRNCCHOD"(+)) 7 - 过滤器("D"."TDOPNDAT"

注意

  • 用于此语句的动态采样 (level=4)
  • 由于提示,并行度为 10

我在d.TDOPNDAT 上创建了一个索引,在 (tmp.ABRNCHCOD, tmp.TBDPTYPE, tmp.CFCIFNO,tmp.TDSERIAL) 上创建了一个索引

.另外,优化器没有使用任何索引,我强制优化器使用创建的索引,但是查询成本成倍增加! 完成所有提到的工作后,查询时间仍然很长!

有没有人建议减少这个查询时间? 谢谢

【问题讨论】:

    标签: sql oracle join


    【解决方案1】:

    不必创建中间表,因为如果提供正确的信息,查询优化器应该能够在一个步骤中就最快的方式做出正确的决定,而无需您计算中间步骤.与索引类似,通常查询优化器将正确决定是否使用它们 - 只要它具有正确的输入(以统计/约束等形式)。

    索引并不总是好的——它们在需要查找表中数据的一小部分的情况下很强大,但在您阅读整个表的情况下,它们对您的帮助不大。

    首先,询问 Oracle 它使用什么计划来执行您的长期运行的查询。这可以通过以下方式完成:

    explain plan for 
    select .... from  tdpst left outer join fhist 
    left outer join dp2act
    left outer join dp2cust;
    

    那么,

    select * from table(dbms_xplan.display);
    

    注意 - 我在这台笔记本电脑上没有数据库(现在是周末),所以上面的命令可能有一些拼写错误,如果它们不能按预期工作,请参阅文档。

    查看结果并考虑: 它是如何连接表的——是使用散列、合并连接还是嵌套循环? 它是否以正确的顺序加入表格。

    我的猜测是它会在连接顺序或连接类型上做出错误的决定。根据这个假设,我接下来要检查的是基数估计。使用 /*+ GATHER_PLAN_STATISTICS */ 提示再次运行查询(您需要使用此提示实际运行它 - 而不仅仅是再次解释计划)并使用以下命令检查估计行号和实际行号之间的差异:

    选择 * FROM 表(DBMS_XPLAN.DISPLAY_CURSOR(FORMAT=>'ALLSTATS LAST'));

    检查估计行和实际行之间的巨大差异。巨大的差异意味着导致优化器误入歧途的统计问题。从这点可以考虑。

    • 收集表的其他统计信息,例如,如果其中一个表的连接中存在倾斜列(请参阅 DBMS_STATS)
    • 如果您要加入两个或更多相关列,则收集扩展统计信息

    然后重复,看看你是否获得了性能提升

    祝你好运..

    【讨论】:

    • 很好的建议。我要补充一点,如果 OP 对临时表使用并行性,那么她可能还想将它用于单 SQL 方法。并将其添加到 INSERT 语句中,而不仅仅是 SELECT 部分。
    • 我们在操作环境中选择和插入都使用了16的并行度,与我们的测试环境相比它有所改进,但查询时间仍然太长!
    • 首先,感谢您的快速回答:) 请让我们一一考虑建议并在一些句子中添加一些 cmets: 1.据我所知,Oracle 存储查询使用的表数据。由于 oracle 缓存的容量有限,我们必须管理我们的数据量以获得更好的执行时间和更好的性能。因此,除了检查顺序连接之外,我们还必须注意查询中使用的数据量。临时表是解决此问题的理想选择。
    • 2.我在where子句中使用了'left outer join',因此oracle不应该完全访问表'tdpst',它应该考虑where子句并从tdpst表中删除不满足的行在这种情况下,它应该对 tdpst 和 tmpfhist 的剩余数据进行左外连接。因此,在这种情况下,字段 opndat 上的索引将很有用。
    • 3.感谢“检查加入顺序”的建议。在左外连接中,最好将大表放在左侧,将小表放在右侧。 Tmfhist 表有大约 350 万条记录,小于 tdpst 大约 1100 万条记录。我在问题部分添加了计划的详细信息。
    【解决方案2】:

    首先,让引擎处理索引选择通常是最佳实践。如果未使用索引,通常是因为连接条件不允许正确使用索引。这就是您的执行成本增加的原因。

    如果您的 IO 最少且临时数据库位于 SSD 驱动器上,那么临时表是一个不错的选择。环境是这里的一个因素。如果您的数据选择很大,请考虑分页。

    否则我会推荐一个索引视图。您的列将与您的连接一起指定,并允许预先计算索引以获得最佳结果。我认为甲骨文称它们为物化视图。如果您从每个表中索引字段,它还应该强制计算连接。

    如果我的回答有误,请随时纠正我。我的大部分经验来自 MSSQL。我的 Oracle 经验有点有限

    【讨论】:

    • 我认为当您想提高报告或应用程序中使用的查询的性能时,分页很有用。我的案例是一个临时表,我想在下一步中使用它的所有数据ETL 过程,请告诉我是否可以在这种情况下使用分页并提供更多详细信息?
    • 如果它在 ETL 过程中,我不会实现分页。我会看看为什么没有使用索引。引擎会知道它是否可以使用索引。一旦纠正,您应该会看到重大改进。如果您想在 ETL 过程中进行更多过滤,您还可以考虑将此查询移动到可以再次索引的视图中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-11
    • 1970-01-01
    • 2015-10-18
    • 1970-01-01
    • 1970-01-01
    • 2020-12-27
    • 1970-01-01
    相关资源
    最近更新 更多