为什么这两个查询的性能之间有如此大的差异?

时间:2019-10-09 14:09:16

标签: sql oracle query-performance

我有一项任务是根据一些规则获取特定帐户和交易。值得注意的是,帐户和交易记录在不同的表中,我的查询返回的大部分是帐户-比例约为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秒,并且查询不再阻塞数据库。它还返回了更多的结果,这很奇怪,但不是问题。

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

3 个答案:

答案 0 :(得分:2)

实际上,这完全有道理...让我们看一下第一个查询的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”帐户表测试的任何其他条件中为已打开,已关闭或冻结的帐户添加“或”。

由于(交易)“或”(帐户),您正在对最多“所有交易”(由于“或”)进行查询,因为您在比较最终“或”子句的冻结条件而不关心日期/时间。

由于将在一个帐户上进行交易以打开或关闭一个帐户,因此您知道该帐户将继续进行活动。如果在冻结帐户的交易中出现ATTEMPT,也将予以考虑。

这是我如何调整您的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(开放,关闭,冻结)的交易

答案 1 :(得分:2)

您的第二个查询将返回accounts表中的多个结果,其中单行已重复并带有不同的注释(其中UNION上方和下方的子查询都将其匹配)

但是,在第二个查询中,每个子查询的效率更高,因为第一个子查询可以在transaction表上进行过滤,并且仅在过滤器已匹配且第二个子查询可以在JOIN表上进行过滤,并且不需要account,因此,在两者之间,不需要不必要的JOIN。对于第一个查询,情况并非如此,因为JOIN语句同时使用CASEaccount表中的值,因此无论使用哪个过滤器,所有行都必须transaction被赋值条件匹配。

另一种替代方法可能是不使用JOIN而是使用JOIN而是依靠EXISTS语句的ELSE子句来与{{1 }}过滤器已匹配,其他CASE过滤器未匹配。

EXISTS

注意:这在OR语句中具有不同的优先级,因此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 ) 可能会为您的查询提供不同的值-它们应该是第二个查询的返回行的子集,但是可能与第一个查询不匹配。

答案 2 :(得分:1)

查询的第一种形式仅在accounts表上使用带有条件的OR,因此,即使没有使用匹配的帐户,它也必须读取每个具有匹配帐户的交易。

在查询的第二种形式中,交易查询可以使用transaction_typetransaction_date上的索引(如果存在)(似乎在给定的域中),并且可以跳过不匹配的交易完全。

没有解释,这是猜测,但是第一个查询需要读取更多数据,因此可能无法使用索引,而第二个查询则不能使用索引。

您说查询返回的帐户多于交易。可能是这样,但是我怀疑总的交易量比帐户多吗?而且由于查询的第一种形式可能需要扫描事务表,因此不管它们是否匹配,都关系到表的大小。