4

之前,我发现执行计划中的“成本”是相对执行时间的一个很好的指标。为什么这个案例不同?认为执行计划具有相关性,我是个傻瓜吗?我可以具体尝试什么来提高 v_test 性能?

谢谢你。

使用 Oracle 10g 我在下面定义了一个简单的查询视图

  create or replace view v_test as
  select distinct u.bo_id as bo_id, upper(trim(d.dept_id)) as dept_id
  from
      cust_bo_users u
  join cust_bo_roles r on u.role_name=r.role_name
  join cust_dept_roll_up_tbl d on 
                            (r.region is null or trim(r.region)=trim(d.chrgback_reg)) and 
                            (r.prod_id is null or trim(r.prod_id)=trim(d.prod_id)) and
                            (r.div_id is null or trim(r.div_id)=trim(d.div_id )) and
                            (r.clus_id is null or trim(r.clus_id )=trim( d.clus_id)) and
                            (r.prod_ln_id is null or trim(r.prod_ln_id)=trim(d.prod_ln_id)) and
                            (r.dept_id is null or trim(r.dept_id)=trim(d.dept_id))

定义为替换以下视图

        create or replace view v_bo_secured_detail
  select distinct Q.BO_ID, Q.DEPT_ID
  from (select U.BO_ID BO_ID, UPPER(trim(D.DEPT_ID)) DEPT_ID
        from CUST_BO_USERS U, CUST_BO_ROLES R, CUST_DEPT_ROLL_UP_TBL D
        where U.ROLE_NAME = R.ROLE_NAME and
                R.ROLE_LEVEL = 'REGION' and
                trim(R.REGION) = UPPER(trim(D.CHRGBACK_REG))
        union all
        select U.BO_ID BO_ID, UPPER(trim(D.DEPT_ID)) DEPT_ID
        from CUST_BO_USERS U, CUST_BO_ROLES R, CUST_DEPT_ROLL_UP_TBL D
        where U.ROLE_NAME = R.ROLE_NAME and
                R.ROLE_LEVEL = 'RG_PROD' and
                trim(R.REGION) = UPPER(trim(D.CHRGBACK_REG)) and
                trim(R.PROD_ID) = UPPER(trim(D.PROD_ID))
        union all
        select U.BO_ID BO_ID, UPPER(trim(D.DEPT_ID)) DEPT_ID
        from CUST_BO_USERS U, CUST_BO_ROLES R, CUST_DEPT_ROLL_UP_TBL D
        where U.ROLE_NAME = R.ROLE_NAME and
                R.ROLE_LEVEL = 'PROD' and
                trim(R.PROD_ID) = UPPER(trim(D.PROD_ID))
        union all
        select U.BO_ID BO_ID, UPPER(trim(D.DEPT_ID)) DEPT_ID
        from CUST_BO_USERS U, CUST_BO_ROLES R, CUST_DEPT_ROLL_UP_TBL D
        where U.ROLE_NAME = R.ROLE_NAME and
                R.ROLE_LEVEL = 'DIV' and
                trim(R.DIV_ID) = UPPER(trim(D.DIV_ID))
        union all
        select U.BO_ID BO_ID, UPPER(trim(D.DEPT_ID)) DEPT_ID
        from CUST_BO_USERS U, CUST_BO_ROLES R, CUST_DEPT_ROLL_UP_TBL D
        where U.ROLE_NAME = R.ROLE_NAME and
                R.ROLE_LEVEL = 'RG_DIV' and
                trim(R.REGION) = UPPER(trim(D.CHRGBACK_REG)) and
                trim(R.DIV_ID) = UPPER(trim(D.DIV_ID))
        union all
        select U.BO_ID BO_ID, UPPER(trim(D.DEPT_ID)) DEPT_ID
        from CUST_BO_USERS U, CUST_BO_ROLES R, CUST_DEPT_ROLL_UP_TBL D
        where U.ROLE_NAME = R.ROLE_NAME and
                R.ROLE_LEVEL = 'CLUS' and
                trim(R.CLUS_ID) = UPPER(trim(D.CLUS_ID))
        union all
        select U.BO_ID BO_ID, UPPER(trim(D.DEPT_ID)) DEPT_ID
        from CUST_BO_USERS U, CUST_BO_ROLES R, CUST_DEPT_ROLL_UP_TBL D
        where U.ROLE_NAME = R.ROLE_NAME and
                R.ROLE_LEVEL = 'RG_CLUS' and
                trim(R.REGION) = UPPER(trim(D.CHRGBACK_REG)) and
                trim(R.CLUS_ID) = UPPER(trim(D.CLUS_ID))
        union all
        select U.BO_ID BO_ID, UPPER(trim(D.DEPT_ID)) DEPT_ID
        from CUST_BO_USERS U, CUST_BO_ROLES R, CUST_DEPT_ROLL_UP_TBL D
        where U.ROLE_NAME = R.ROLE_NAME and
                R.ROLE_LEVEL = 'PROD_LN' and
                trim(R.PROD_LN_ID) = UPPER(trim(D.PROD_LN_ID))
        union all
        select U.BO_ID BO_ID, UPPER(trim(R.DEPT_ID)) DEPT_ID
        from CUST_BO_USERS U, CUST_BO_ROLES R
        where U.ROLE_NAME = R.ROLE_NAME and
                R.ROLE_LEVEL = 'DEPT') Q

目的是消除对 ROLE_LEVEL 列的依赖。

v_test 的执行计划明显低于 v_bo_secured_detail 简单

select * from <view> where bo_id='value'

查询。并且在实际查询中使用时显着降低

  select CT_REPORT.RPT_KEY,
         CT_REPORT_ENTRY.RPE_KEY,
         CT_REPORT_ENTRY.CUSTOM16,
         Exp_Sub_Type.value,
         min(CT_REPORT_PAYMENT_CONF.PAY_DATE),
         CT_REPORT.PAID_DATE
  from CT_REPORT,
      <VIEW> SD,
      CT_REPORT_ENTRY,
      CT_LIST_ITEM_LANG Exp_Sub_Type,
      CT_REPORT_PAYMENT_CONF,
      CT_STATUS_LANG Payment_Status
  where (CT_REPORT_ENTRY.RPT_KEY = CT_REPORT.RPT_KEY) and
        (Payment_Status.STAT_KEY = CT_REPORT.PAY_KEY) and
        (Exp_Sub_Type.LI_KEY = CT_REPORT_ENTRY.CUSTOM9 and Exp_Sub_Type.LANG_CODE = 'en') and
        (CT_REPORT.RPT_KEY = CT_REPORT_PAYMENT_CONF.RPT_KEY) and
        (SD.BO_ID = 'JZHU9') and
        (SD.DEPT_ID = UPPER(CT_REPORT_ENTRY.CUSTOM5)) and
        (Payment_Status.name = 'Payment Confirmed' and (Payment_Status.LANG_CODE = 'en') and
        CT_REPORT.PAID_DATE > to_date('01/01/2008', 'mm/dd/yyyy') and Exp_Sub_Type.value != 'Korea')
  group by CT_REPORT.RPT_KEY,
            CT_REPORT_ENTRY.RPE_KEY,
            CT_REPORT_ENTRY.CUSTOM16,
            Exp_Sub_Type.value,
            CT_REPORT.PAID_DATE

执行时间完全不同。v_test 视图需要 15 个小时,而 v_bo_secured_detail 需要几秒钟。


谢谢所有回复的人

这是我要记住的一个。表达式的理论和数学与基于硬件的执行现实相结合的地方。哎哟。

4

6 回答 6

4

执行计划是理论,执行时间是现实。

该计划向您展示了引擎如何执行您的查询,但某些步骤可能会导致解决查询的工作量过多。使用“x is null or x = y”闻起来很糟糕。如果 r 和 d 是大表,您可能会遇到某种组合爆炸,并且请求会在大量磁盘块列表中无休止地循环。我想您在执行期间会看到很多 I/O。

另一方面,联合选择又短又甜,因此可能会重用许多仍然存在于早期选择中的磁盘块,和/或您有一定程度的并行性受益于对相同磁盘块的读取。

同样到处使用 trim() 和 upper() 看起来有点可疑。如果您的数据如此不干净,则可能值得不时运行一些定期的房屋清洁,以便您可以说“x = y”并知道它有效。

更新:您要求提供改进 v_test 的提示。清理你的数据,这样 trim() 和 upper() 就没有必要了。它们可能会阻止使用索引(尽管这也会影响联合选择版本)。

如果你不能摆脱 "x is null or x = y" 那么 y = nvl(x,'does-not-exist') 可能有更好的特征(假设 'does-not-exist' 是一个“可以” t发生” id值)。

于 2008-09-19T15:22:03.983 回答
3

正如Oracle 文档所说,成本是相对于特定执行计划的估计成本。当您调整查询时,计算成本的特定执行计划可能会发生变化。有时戏剧性的。

v_test 性能的问题在于,Oracle 想不出除了执行嵌套循环之外的其他方法来执行它,对于每个 cust_bo_roles,扫描所有 cust_dept_roll_up_tbl 以找到匹配项。如果表的大小为 n 和 m,则需要 n*m 时间,这对于大型表来说很慢。相比之下, v_bo_secured_detail 被设置为一系列查询,每个查询都可以通过其他一些机制完成。(Oracle 有一个可能使用的数字,包括使用索引、动态构建散列或对数据集进行排序和合并。这些操作都是 O(n*log(n)) 或更好。)快速查询很快。

尽管很痛苦,但如果您希望此查询更快,那么您需要像之前的查询一样将其拆分。

于 2008-09-19T15:23:35.663 回答
0

低成本 - 高执行时间的一个方面是,当您查看大型数据集时,整体上批量处理通常更有效,而如果您想要快速结果,则更有效做尽可能少的工作来获得第一条记录。在处理大型集合时,重复进行看似快速响应的小操作不太可能产生好的结果。

很多时候,当您想要一个快速的结果时,USE_NL 优化器提示会有所帮助。

此外,在您的测试视图中,它依赖于 IS NULL ... IS NULL 不能使用索引,也不能在“table-side”参数上使用诸如 trim 之类的函数。

于 2008-09-19T15:21:12.813 回答
0

您是否收集了所有基础表的优化器统计信息?没有它们,优化者的估计可能与现实大相径庭。

于 2008-09-19T15:22:58.740 回答
0

当您说“查询计划较低”时,您的意思是它更短,还是实际成本估算更低?替换视图的一个明显问题是与 cust_dept_roll_up_tbl 的联接几乎完全使用不可索引的标准(索引可以满足“为空”测试,但涉及对每个参数调用 trim 的测试不能满足),所以规划器必须至少对表进行一次,并且可能是多次顺序扫描才能满足查询。

我不确定Oracle是否有这个限制,但是许多数据库只能对每个包含的表进行一次索引扫描,所以即使你清理你的连接条件是可索引的,它也可能只能满足一个索引条件扫描并且必须对其余部分使用顺序扫描。

于 2008-09-19T15:28:15.667 回答
0

稍微详细说明一下成本。

在 Oracle 9/10g 中,稍微简化一下,成本由公式确定:

成本 = (SrCount * SrTime + MbrCount * MbrTime + CpuCyclesCount * CpuCycleTime) / SrTime

其中 SrCount - 单块读取总数,SrTime - 根据收集的系统统计信息,单个块读取的平均时间,MbrCount 和 MbrTime,对应的多块读取相同(在全表扫描和索引快速全扫描期间使用),Cpu相关指标是不言自明的......并且全部除以单个块读取时间。

于 2011-11-18T03:41:45.687 回答