【问题标题】:Oracle Performance with 'NOT EXISTS'“不存在”的 Oracle 性能
【发布时间】:2018-04-17 16:49:53
【问题描述】:

我正在将一些 SQL 服务器过程移植到 Oracle 并发现一个有趣的情况,即 Oracle SQL 语句比使用游标的相同逻辑慢得多。

经过调查,我认为“不存在”可能存在特殊问题(也许?)。

在这里,我将 100k 潜在客户放入 TMP_TXN,然后将它们用作过滤器,以从没有交易的 Payments 中提取记录(请参阅下面的 SQL 构造)。

  INSERT INTO tmp_txn ....
  SELECT ....
  FROM   txn, 
         customers
  WHERE  txn.customer_id = customers.customer_id
         AND customers.customer_status LIKE 'A%'
         AND txn.txn_date BETWEEN start_date AND end_date;

然后插入到 tmp_leads 中,它们在付款中,但不在 TMP_TXN 中。

   INSERT INTO tmp_leads ....
    SELECT ....
    FROM  payments eap, customers
    WHERE eap.customer_id = customers.customer_id
           AND customers.customer_status LIKE 'A%'
           AND NOT EXISTS (SELECT TMP_TXN.CUSTOMER_ID
                           FROM   TMP_TXN
                           WHERE  tmp_txn.customer_id = eap.customer_id
                                  AND  ....;

解释计划是:

Plan hash value: 67643415

-----------------------------------------------------------------------------------------------------------
| Id  | Operation                        | Name                   | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------------------
|   0 | INSERT STATEMENT                 |                        |   665 |   138K| 15450   (1)| 00:03:06 |
|   1 |  LOAD TABLE CONVENTIONAL         | TMP_LEADS              |       |       |            |          |
|*  2 |   FILTER                         |                        |       |       |            |          |
|*  3 |    HASH JOIN                     |                        |   665 |   138K| 14785   (1)| 00:02:58 |
|*  4 |     TABLE ACCESS FULL            | CUSTOMER_TYPES         |     6 |    36 |     3   (0)| 00:00:01 |
|*  5 |     HASH JOIN                    |                        |   726 |   146K| 14781   (1)| 00:02:58 |
|*  6 |      TABLE ACCESS FULL           | EDM_SEGMENTS_EVENTS    |    23 |   414 |     5   (0)| 00:00:01 |
|   7 |      NESTED LOOPS                |                        |       |       |            |          |
|   8 |       NESTED LOOPS               |                        |  1297 |   239K| 14776   (1)| 00:02:58 |
|*  9 |        TABLE ACCESS FULL         | EDM_AGREEMENT_PAYMENTS |  1297 |   158K| 12180   (1)| 00:02:27 |
|* 10 |        INDEX UNIQUE SCAN         | PK_CUSTOMERS           |     1 |       |     1   (0)| 00:00:01 |
|* 11 |       TABLE ACCESS BY INDEX ROWID| CUSTOMERS              |     1 |    64 |     2   (0)| 00:00:01 |
|* 12 |    TABLE ACCESS BY INDEX ROWID   | TMP_TXN                |     1 |    81 |     2   (0)| 00:00:01 |
|* 13 |     INDEX RANGE SCAN             | IX_TMP_TXN_TXN_CODE    |     1 |       |     2   (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter( NOT EXISTS (SELECT 0 FROM "TMP_TXN" "TMP_TXN" WHERE "TMP_TXN"."TXN_CODE"=:B1 AND 
              "TMP_TXN"."CUSTOMER_ID"=:B2 AND "TMP_TXN"."AGREEMENT_ID"=:B3 AND ("TMP_TXN"."AMOUNT"<0 AND 
              "TMP_TXN"."AMOUNT">=:B4*1.1 AND "TMP_TXN"."AMOUNT"<=:B5*.9 OR "TMP_TXN"."AMOUNT">0 AND 
              "TMP_TXN"."AMOUNT">=:B6*0.9 AND "TMP_TXN"."AMOUNT"<=:B7*1.1)))
   3 - access("CUSTOMERS"."CUSTOMER_TYPE"="CUSTOMER_TYPES"."CUSTOMER_TYPE")
   4 - filter("CUSTOMER_TYPES"."ACTIVE"=U'1')
   5 - access("CUSTOMERS"."CUSTOMER_SEGMENT"="EDM_SEGMENTS_EVENTS"."SEGMENT")
   6 - filter("EDM_SEGMENTS_EVENTS"."EVENT_ID"=607 AND "EDM_SEGMENTS_EVENTS"."ACTIVE"=U'1')
   9 - filter(ROUND("EAP"."PMNT_DAY",0)>=19 AND ROUND("EAP"."PMNT_DAY",0)<=31 AND 
              "EAP"."PERIODICITY"=U'M' AND "EAP"."EVENT_ID"=607)
  10 - access("EAP"."CUSTOMER_ID"="CUSTOMERS"."CUSTOMER_ID")
  11 - filter("CUSTOMERS"."CUSTOMER_STATUS" LIKE U'A%')
  12 - filter("TMP_TXN"."CUSTOMER_ID"=:B1 AND "TMP_TXN"."AGREEMENT_ID"=:B2 AND 
              ("TMP_TXN"."AMOUNT"<0 AND "TMP_TXN"."AMOUNT">=:B3*1.1 AND "TMP_TXN"."AMOUNT"<=:B4*.9 OR 
              "TMP_TXN"."AMOUNT">0 AND "TMP_TXN"."AMOUNT">=:B5*0.9 AND "TMP_TXN"."AMOUNT"<=:B6*1.1))
  13 - access("TMP_TXN"."TXN_CODE"=:B1)

tmp_txn(customer_id)上有一个索引,表里有大约100k条记录。 Oracle 有 20gb SGA 和 20gb PGA,所以应该很容易缓存。

Resource plan screenshot 在这里你可以看到脚本正在运行,但没有使用任何资源(数据读取

(可能的)问题似乎在 NOT EXISTS 中,因为此选择需要 >1000 秒,几乎无法访问数据表(资源监视器)。 Stats view in OM showing 898 seconds and 100% cpu

我在 Oracle 中做了一些愚蠢的事情吗?这在 SQL Server 中运行良好(而且速度很快)。

【问题讨论】:

  • 你为什么要为这个逻辑使用一个临时表——在任一数据库中?
  • 能否提供查询计划?
  • 最初存储过程使用游标循环遍历大表(TXN 160m)和支付(20m),将实际数据(100k-300k)提取到子集中进行操作更容易/更快.有很多没有显示的铁道部操作。
  • 对不起。我不知道如何提取查询计划。 SQL 开发人员中的解释和企业管理器中的计划似乎都不能以任何可读的形式导出(我缺乏 Oracle 知识)。
  • 我同意@VladimirAleshin。不看计划就很难知道发生了什么。

标签: sql oracle performance not-exists


【解决方案1】:

如果内部查询结果集很大,应该使用NOT EXISTS。请尝试以下方法

INSERT INTO tmp_leads ....
SELECT ....
FROM  payments eap, customers
WHERE eap.customer_id = customers.customer_id
       AND customers.customer_status LIKE 'A%'
       AND customers.customer_id NOT IN (SELECT TMP_TXN.CUSTOMER_ID
                       FROM   TMP_TXN
                       WHERE  
                       --tmp_txn.customer_id = eap.customer_id
                              --AND  
                          ....;

【讨论】:

  • 感谢@PSARAJ12。我会试试这个。希望这会有所帮助。干杯。
  • 遗憾的是这并没有真正的区别。
  • 评论了一个条件立即尝试
【解决方案2】:

尝试使用左连接而不是创建和加载 temptxn 然后加入

Insert into tmpleads
Select ...
From payments eap 
Inner join customers cust
    On cust.customerid = eap.customerid   
LEFT JOIN temptxn txn
    On txn.customerid = eap.customerid
Where txn.customerid IS NULL

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-11-10
    • 2019-08-01
    • 2013-07-15
    • 2018-12-20
    • 1970-01-01
    • 2013-09-29
    • 2013-03-26
    • 1970-01-01
    相关资源
    最近更新 更多