解释计划成本与执行时间

时间:2020-03-06 14:26:19  来源:igfitidea点击:

以前,我发现执行计划中的"成本"可以很好地指示相对执行时间。为什么这种情况不同?我认为执行计划具有相关性吗?我具体可以尝试提高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花费几秒钟。

谢谢所有回应的人

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

解决方案

低成本-高执行时间的一个方面是,当我们查看大型数据集时,整体上做事通常在整体上效率更高,而如果想要快速获得结果,则做事效率更高尽可能少的工作来获得第一张唱片。在大型设备上工作时,重复进行小操作以使外观快速响应的效果不太可能得到良好的结果。

很多时候,当我们想要快速获得结果时,USE_NL优化器提示会有所帮助。

另外,在测试视图中,它依赖于IS NULL ... IS NULL不能使用索引,也不能在'table-side'参数上使用例如trim的功能。

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

该计划向我们展示了引擎如何执行查询,但是某些步骤可能会导致解决查询的工作量过大。使用" x为null或者x = y"的气味很难闻。如果r和d是大表,我们可能会受到某种组合爆炸的打击,并且请求在大量磁盘块列表中无休止地循环。我想我们在执行过程中会看到很多I / O。

另一方面,联合的选择又短又甜,因此可能会重用较早选择仍然存在的许多磁盘块,并且/或者我们可以从对相同磁盘块的读取中获得一定程度的并行性。

还在各处使用trim()和upper()看起来有点可疑。如果数据非常不干净,则不时需要进行一些定期的房屋清洁,这样我们就可以说" x = y"并知道它可以工作了。

更新:我们询问了一些改进v_test的技巧。清理数据,以便没有必要trim()和upper()。它们可能会阻止使用索引(尽管这也会影响联合的选择版本)。

如果无法摆脱" x为null或者x = y",则y = nvl(x,'does-not-exist')可能具有更好的特性(假设'does-not-exist'是'can' t发生" id值)。

我们是否已在所有基础表上收集了优化器统计信息?没有它们,优化者的估计可能与现实完全背道而驰。

如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))或者更好。)快速查询是快速的。

尽管很痛苦,但是如果我们希望此查询速度很快,则需要像上一个查询一样将其分解。

当我们说"查询计划较低"时,我们是说它较短,还是实际成本估算较低?替换视图的一个明显问题是,与cust_dept_roll_up_tbl的联接几乎只使用不可索引的条件(索引可以满足" is null"测试,但是涉及对每个参数调用trim的条件不能满足),因此计划者必须对表进行至少一次(可能是几次)连续扫描才能满足查询要求。

我不确定Oracle是否有此限制,但是许多数据库只能对包含的表进行一次索引扫描,因此,即使我们将联接条件清理为可索引的,它也只能满足一个索引条件。扫描,其余部分则必须使用顺序扫描。