【问题标题】:Why is there such a big difference between performance of those two queries?为什么这两个查询的性能有如此大的差异?
【发布时间】:2019-10-09 14:09:16
【问题描述】:

我的任务是根据一些规则获取特定帐户和交易。值得注意的是,账户和交易位于不同的表中,我的查询主要返回账户 - 比例约为 70 个账户与 1 笔交易。为了方便起见,我想将它们放在一个查询中 - 这是更大过程中的一个阶段。

原始查询:

SELECT DISTINCT 
    CASE 
        WHEN (a.transaction_type IN ('500', '501', '502', '920') AND a.transaction_date >= '#DATE#' AND to_char(a.transaction_date,'HH24') >= 16) THEN 'Transaction time'
        WHEN b.closing_date >= '#DATE#' THEN 'Closing time'
        WHEN b.opening_date >= '#DATE#'    THEN 'Opening time'  
        WHEN (b.type = 'X' AND b.active = 'NO') THEN 'Frozen account'   
    END AS "comment"
    ,b.branch 
    ,b.basic 
    ,b.lmt 
FROM 
    VDS.transactions a
    JOIN VDS.accounts b ON a.acct_no = b.acct_no
WHERE
    (a.transaction_type IN ('500', '501', '502', '920')
    AND a.transaction_date  >= '#DATE#'
    AND to_char(a.transaction_date,'HH24') >= 16)
    OR
    (b.closing_date >= '#DATE#'
    OR b.opening_date >= '#DATE#'   
    OR (b.type = 'X' AND b.active = 'NO'))

似乎工作正常,即使有点慢 - 它的执行时间通常在 12 秒左右。问题是 - 有时根本无法完成。看起来数据库完全停留在查询上。由于我不是 Oracle 管理员,我无法证实我怀疑这是该查询的错,但多项测试表明确实如此。

所以我准备了另一种变体,考虑到交易比账户少得多。

带有子查询的变体:

SELECT 
    'Transaction time' AS "comment"
    ,b.branch 
    ,b.basic 
    ,b.lmt 
FROM 
    VDS.transactions a
    JOIN VDS.accounts b ON a.acct_no = b.acct_no
WHERE
    a.transaction_type IN ('500', '501', '502', '920')
    AND a.transaction_date  >= '#DATE#'
    AND to_char(a.transaction_date,'HH24') >= 16

UNION

SELECT 
    CASE 
        WHEN closing_date >= '#DATE#' THEN 'Closing time'
        WHEN opening_date >= '#DATE#'    THEN 'Opening time'    
        WHEN (type = 'X' AND active = 'NO') THEN 'Frozen account'   
    END AS "comment"
    ,branch 
    ,basic 
    ,lmt 
FROM 
    VDS.accounts 
WHERE
    closing_date >= '#DATE#'
    OR opening_date >= '#DATE#' 
    OR (type = 'X' AND active = 'NO'))

你瞧 - 执行时间缩短到大约 3-5 秒,查询不再阻塞数据库。它还返回了更多的结果,这很奇怪,但不是问题。

所以我的最后一个问题是:有人可以向我解释数据库的内部可能发生了什么,它很高兴地接受了带有子查询的变体,而原始的变体却变得不稳定?我可以更好地理解子查询的性能,但我不知道为什么查询有时会起作用,有时会完全挂断。

【问题讨论】:

  • 您是否制定了解释计划并查看了结果?
  • 我对 SQL 比较陌生,所以我什至不知道查询计划。感谢您提出!

标签: sql oracle query-performance


【解决方案1】:

实际上,这完全有道理...让我们看看您的第一个查询的 WHERE 子句

WHERE
    (a.transaction_type IN ('500', '501', '502', '920')
    AND a.transaction_date  >= '#DATE#'
    AND to_char(a.transaction_date,'HH24') >= 16)
    OR
    (b.closing_date >= '#DATE#'
    OR b.opening_date >= '#DATE#'   
    OR (b.type = 'X' AND b.active = 'NO'))

第一部分很简单...>= 给定日期和特定交易类型的交易。没问题。

现在,您在“B”帐户表测试已开、已关闭或冻结帐户的任何其他条件中添加 OR。

由于(交易)OR(帐户),您在比较不关心日期的冻结条件的最终“OR”子句时打开了最多所有交易(由于 OR)的查询/时间。

由于账户上会发生开立或关闭账户的交易,因此您知道该账户将进行该活动。如果尝试针对冻结帐户进行交易,也将被计算在内。

这是我将如何调整你的 where 子句...

WHERE
        a.transaction_date  >= '#DATE#'
    AND to_char(a.transaction_date,'HH24') >= 16)
    AND 
        (  a.transaction_type IN ('500', '501', '502', '920')
             OR
           (   b.closing_date >= '#DATE#'
            OR b.opening_date >= '#DATE#'   
            OR (b.type = 'X' AND b.active = 'NO')
        )

因此,您只考虑在预期日期期间内进行交易......其中,只有那些交易类型为 OR(打开、关闭、冻结)的交易

【讨论】:

    【解决方案2】:

    您的第二个查询将返回来自 accounts 表的多个结果,其中单行已与不同的 cmets 重复(该表已被 UNION 上方和下方的子查询匹配)。

    但是,在第二个查询中,每个子查询的效率更高,因为第一个子查询可以过滤transaction 表,并且只有在匹配过滤器和第二个子查询时才执行JOIN可以过滤account 表并且不需要JOIN,因此,在两者之间,不需要不必要的JOINs。第一个查询不是这种情况,因为CASE 语句使用来自accounttransaction 表的值,因此无论匹配哪个过滤条件,所有行都必须是JOINed。

    另一种选择可能是不使用JOIN 而是使用EXISTS 并依赖CASE 语句的ELSE 子句来对应EXISTS 过滤器何时匹配以及另一个@987654337 @过滤器不匹配。

    SELECT CASE 
           WHEN closing_date >= '#DATE#'
           THEN 'Closing time'
           WHEN opening_date >= '#DATE#'
           THEN 'Opening time'    
           WHEN (type = 'X' AND active = 'NO')
           THEN 'Frozen account'
           ELSE 'Transaction time'
           END AS comment,
           branch,
           basic,
           lmt 
    FROM   VDS.accounts a
    WHERE  closing_date >= '#DATE#'
    OR     opening_date >= '#DATE#' 
    OR     ( type = 'X' AND active = 'NO')
    OR     EXISTS (
             SELECT 1
             FROM   VDS.transactions t
             WHERE  a.acct_no = t.acct_no
             AND    t.transaction_type IN ('500', '501', '502', '920')
             AND    t.transaction_date  >= '#DATE#'
             AND    to_char(t.transaction_date,'HH24') >= 16
           )
    

    注意:这在 CASE 语句中具有不同的优先级,因此 comment 可能会为您的查询提供不同的值 - 它们应该是第二个查询返回行的子集,但可能与第一个查询。

    【讨论】:

      【解决方案3】:

      查询的第一种形式只对账户表使用带有条件的 OR,因此它必须读取每个具有匹配账户的交易,即使它们没有被使用。

      在查询的第二种形式中,事务查询可以使用 transaction_typetransaction_date 上的索引(如果有的话)(这似乎很可能给定域),并且可以完全跳过不匹配的事务。

      没有解释是猜测,但第一个查询需要读取更多数据,并且可能无法像第二个查询那样使用索引。

      您说查询返回的帐户多于交易。可能是这样,但我怀疑交易总数比账户多得多?由于查询的第一种形式可能需要扫描事务表,它们是否匹配并不重要,重要的是表有多大。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-12-03
        • 1970-01-01
        • 2012-02-18
        • 2021-10-27
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-03-30
        相关资源
        最近更新 更多