【问题标题】:Optimization of Oracle QueryOracle查询优化
【发布时间】:2013-10-28 11:40:51
【问题描述】:

我有以下查询:

SELECT distinct A1 ,sum(total) as sum_total  FROM 
    (   
       SELECT  A1, A2,A3,A4,A5,A6,COUNT(A7) AS total,A8
       FROM (
            select a.*  from table1 a 
            left join (select * from table_reject where name = 'smith') b on A.A3 = B.B3 and A.A9 =B.B2
            where B.ID is null
            ) t1
       WHERE A8 >= NEXT_DAY ( trunc(to_date('17/09/2013 12:00:00','dd/mm/yyyy hh24:mi:ss')) ,'SUN' )     
       GROUP BY 
       CUBE(A1, A2,A3,A4,A5,A6,A8)
    )INN
    WHERE 
     INN.A1 IS NOT NULL AND
     INN.A2 IS NULL AND 
     INN.A3 IS NULL AND 
     INN.A4 IS NULL AND
     INN.A5 IS NULL AND 
     INN.A6 is NULL AND
     INN.A8 IS NOT NULL
    GROUP BY A1
    ORDER BY sum_total DESC ;

table1 中的记录总数约为 800 万。 我的问题是我需要以最佳方式优化上述查询。我确实尝试在 table1 的 A8 列上创建索引并创建索引帮助我降低查询成本,但查询的执行时间或多或少相同在 table1 上没有索引。

任何帮助将不胜感激。 谢谢

【问题讨论】:

  • 你可以发布表结构和列数据类型,列可为空的类型。您是否考虑过 CUBE 如何为任何维度列取 NULL 值。

标签: oracle11g


【解决方案1】:

对大型数据集的 CUBE 操作非常昂贵,因此您需要检查您是否真的需要内部查询中的所有数据。因为我看到你在内部做 COUNT,然后在外部查询中你有计数的总和。所以换句话说,给我所有组合 A1-A8 (-A7) 的 A7 的行数。然后只获取由 WHERE 子句过滤的选定组合的 SUM。我们可以肯定地通过在某些列本身上限制 CUBE 来优化这一点,但到目前为止我注意到的非常明显的事情如下。

如果您使用下面的查询并且有正确的索引 o Table1 和 Table_reject 那么两个查询都可以利用索引并减少需要连接和进一步处理的数据集。 我不是 100% 确定,但是可以进行部分 CUBE 处理,需要检查一下。

聚集索引 --> A8 上需要 Table1 而 Table_Reject 需要 NAME 上的聚集索引。

非聚集索引--> A3,A9 需要 Table1,B3,B2 需要 Table_reject

    SELECT qry1.
    (
        SELECT  A1, A2,A3,A4,A5,A6,A7,A8
        FROM table1
        WHERE A8 >= NEXT_DAY ( trunc(to_date('17/09/2013 12:00:00','dd/mm/yyyy hh24:mi:ss')) ,'SUN' )
    )qry1
    LEFT JOIN
    (
        select B3,B2,ID
        from table_reject 
        where name = 'smith'
    )qry2
        ON qry1.A3 = qry2.B3 and qry1.A9=qry2.B2
    WHERE qry2.ID IS NULL

EDIT1:

我试图找出如果您对所有列执行此操作或仅对结果集中需要它的列执行此操作,CUBE 运算符的结果会有什么不同。我发现的是 CUBE 函数的工作方式,您不需要在所有列上执行 CUBE。因为最后你只关心由 CUBE 生成的组合,其中 A1 和 A8 不为空。 试试这个链接看看输出。

enter link description here

Query 1 和 Query2 只是用于比较 CUBE 结果集的最内部查询。

Query3 和 Query4 是您尝试的相同查询,并且您看到两种情况下的结果都相同。

    DECLARE @NEXT_DAY DATE = NEXT_DAY ( trunc(to_date('17/09/2013 12:00:00','dd/mm/yyyy hh24:mi:ss')) ,'SUN' )

    SELECT distinct A1 ,sum(total) as sum_total  FROM 
    (   
       SELECT  A1,COUNT(A7) AS total,A8
       FROM (
                select a.a1,a.a7,a.a8  
                from table1 a
                left join  (select * from table_reject where name = 'smith') b 
                on A.A3 = B.B3 and A.A9 =B.B2
                where B.ID is null
            ) t1
       WHERE A8 >= @NEXT_DAY
       GROUP BY 
       CUBE(A1,A8)
    )INN
    WHERE   INN.A1 IS NOT NULL AND
            INN.A8 IS NOT NULL
    GROUP BY A1
    ORDER BY sum_total DESC ;

EDIT3

正如我在评论中提到的,这是第 3 轮更新。我无法更改评论,但我的意思是 Edit3 而不是 Round3。

您查询中的新变化是在最里面的左连接选择where A8 >= @NEXT_DAY AND B.ID is null 中添加WHERE A8 >= @NEXT_DAY 条件。这极大地改进了选择。

在您上一条评论中,您提到查询需要 30-35 秒,并且随着您更改 A8 的值,它会不断增加。现在有了执行时间,您没有提到结果集中有多少数据。 为什么这很重要?因为如果我的查询返回 500 万行作为最终结果集,那么将花费 90% 的时间将该数据放到 UI 上,或者输出文件,无论您使用哪种输出方法正在使用。但实际性能应该衡量查询开始提供前几行的时间。因为到那时优化器已经决定了执行计划,而数据库正在执行该计划。但我同意,如果查询返回 100 行并花费 10 秒,那么执行计划可能有问题。

为了演示我所做的是我创建了虚拟数据。并针对它执行您的查询。 我有表 Test_CubeData,其中有 9M 行,列号和数据类型与您为表 1 解释的相同。我有第二个表 Table_Reject ,它有 80K 行,列数和我从查询中得出的数据类型。测试该表的极端情况; name 列只有一个值“smith”,所有 80K 行的 ID 为空。因此,可能影响内左连接结果的列值将是 B2 和 B3。

在这些测试中,我在两个表上都没有任何索引。两者都是堆。并且您会在几秒钟内看到结果,结果集中的数据范围可以接受。随着我的结果数据集增加,完成时间增加。如果我创建解释索引,那么它会给我 Index Seek 操作 对于所有这些测试用例。但在某些时候,该索引也会耗尽并成为索引扫描。 一个肯定的例子是,如果我的 A8 列的过滤器值是该列中存在的最小日期值。在这种情况下,优化器将看到所有 9M 行都需要参与内部选择和 CUBE,并且大量数据将在内存中进行处理。这是预期的。另一方面,让我们看看另一个查询示例。我在 A8 列中有唯一的 32873 个值,这些值几乎平均分布在 9M 行中。所以每个 A8 值有 260 到 300 行。现在,如果我对任何单个值最小、最大或介于两者之间的任何事物执行查询,则查询执行时间不应改变。

注意下面每张图片中突出显示的文本,表明选择了 A8 过滤器的值, 重要的列只在选择列表中而不是使用 *,在左内连接查询中添加了 A8 过滤器,执行计划显示对两个表的 TableScan 操作,查询执行时间以秒为单位,以及查询返回的总行数。

我希望这将消除您对查询性能的一些疑问,并帮助您设定正确的期望。

**Table Row Counts**

**TableScan_InnerLeftJoin**

**TableScan_FullQuery_248Rows**

**TableScan_FullQuery_5K**

**TableScan_FullQuery_56K**

**TableScan_FullQuery_480k**

【讨论】:

  • 您好 Anup,我尝试按照您的建议使用部分立方体操作的概念。事实上,我已经实现了您给我的相同查询,并且查询的输出是相同的。但是,查询的执行时间仍然超过 30-35 秒,并且当我更改他的值列时它会继续增加即A8(Date_column)。当我看到table1全表扫描的解释计划成本约为80000。
  • 另外,您建议我创建“Table1 上的非聚集索引需要 A3、A9 和 Table_reject 需要 B3、B2”,但在上述列上创建索引并没有帮助我降低查询的执行时间和成本。正如我提到的table1中的数据数超过800万,可能是table1中的数据量太大影响查询成本。表结构如下:表1:A1列- A7(Varchar2,30 bytes),A8列(日期)
  • 你能给我两个表的结构吗?您已经为 Table1 提供了数据类型,还为 Table_reject 提供了 Null-Ability 和相同的方式结构、列名、dataType 和 NULL/Not NULL。
  • 检查 Round3 编辑。我用另一个 WHERE 修改了查询并创建了虚拟数据来向您显示示例结果。我没有 Oracle 环境。所以我确实在 SQL SERVER 2008R2 版本上进行了复制。尽管这应该是了解查询执行时间而不是查询完成时间的性能的良好基准。如果我的查询在结果集中返回 5M 行,那么 DB 引擎将花费的大部分时间只是选择这些行并将其发送到输出模式所在的用户/UI/文件。这不是您可以通过在 DB 或 APP 中分页解决的查询性能问题。
【解决方案2】:

您正在计算一个包含七列的可能非常大的多维数据集结果,然后丢弃所有结果,除了那些在逻辑上只是列 A1 上的 group_by 的结果。

我建议您将查询重写为仅按 A1 分组。

【讨论】:

    猜你喜欢
    • 2015-02-26
    • 2011-01-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多