【发布时间】: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