2011 年,一位 ITPUB 的网友请求优化如下 SQL。
SELECT *
FROM (SELECT A.INVOICE_ID,
A.VENDOR_ID,
A.INVOICE_NUM,
A.INVOICE_AMOUNT,
A.GL_DATE,
A.INVOICE_CURRENCY_CODE,
SUM(NVL(B.PREPAY_AMOUNT_APPLIED, 0)) PAID_AMOUNT,
A.INVOICE_AMOUNT - SUM(NVL(B.PREPAY_AMOUNT_APPLIED, 0)) REMAIN
FROM ap.AP_INVOICES_ALL A, APPS.AP_UNAPPLY_PREPAYS_V B
WHERE A.INVOICE_ID = B.INVOICE_ID(+)
AND A.ORG_ID = 126 /*:B4*/
AND A.SOURCE = 'OSM IMPORTED' /*:B3*/
AND A.INVOICE_NUM BETWEEN NVL( /*:B2*/ null, A.INVOICE_NUM) AND
NVL( /*:B1*/ null, A.INVOICE_NUM)
GROUP BY A.INVOICE_ID,
A.INVOICE_NUM,
A.INVOICE_AMOUNT,
A.VENDOR_ID,
A.GL_DATE,
A.INVOICE_CURRENCY_CODE)
WHERE REMAIN > 0 ;
该 SQL 要执行 1 个多小时,AP_UNAPPLY_PREPAYS_V 是一个视图,代码如下。
CREATE OR REPLACE VIEW APPS.AP_UNAPPLY_PREPAYS_V AS
SELECT AID1.ROWID ROW_ID,
AID1.INVOICE_ID INVOICE_ID,
AID1.INVOICE_DISTRIBUTION_ID INVOICE_DISTRIBUTION_ID,
AID1.PREPAY_DISTRIBUTION_ID PREPAY_DISTRIBUTION_ID,
AID1.DISTRIBUTION_LINE_NUMBER PREPAY_DIST_NUMBER,
(-1) * AID1.AMOUNT PREPAY_AMOUNT_APPLIED,
nvl(AID2.PREPAY_AMOUNT_REMAINING, AID2.AMOUNT) PREPAY_AMOUNT_REMAINING,
AID1.DIST_CODE_COMBINATION_ID DIST_CODE_COMBINATION_ID,
AID1.ACCOUNTING_DATE ACCOUNTING_DATE,
AID1.PERIOD_NAME PERIOD_NAME,
AID1.SET_OF_BOOKS_ID SET_OF_BOOKS_ID,
AID1.DESCRIPTION DESCRIPTION,
AID1.PO_DISTRIBUTION_ID PO_DISTRIBUTION_ID,
AID1.RCV_TRANSACTION_ID RCV_TRANSACTION_ID,
AID1.ORG_ID ORG_ID,
AI.INVOICE_NUM PREPAY_NUMBER,
AI.VENDOR_ID VENDOR_ID,
AI.VENDOR_SITE_ID VENDOR_SITE_ID,
ATC.TAX_ID TAX_ID,
ATC.NAME TAX_CODE,
PH.SEGMENT1 PO_NUMBER,
PV.VENDOR_NAME VENDOR_NAME,
PV.SEGMENT1 VENDOR_NUMBER,
PVS.VENDOR_SITE_CODE VENDOR_SITE_CODE,
RSH.RECEIPT_NUM RECEIPT_NUMBER
FROM AP_INVOICES AI,
AP_INVOICE_DISTRIBUTIONS AID1,
AP_INVOICE_DISTRIBUTIONS AID2,
AP_TAX_CODES ATC,
PO_VENDORS PV,
PO_VENDOR_SITES PVS,
PO_DISTRIBUTIONS PD,
PO_HEADERS PH,
PO_LINES PL,
PO_LINE_LOCATIONS PLL,
RCV_TRANSACTIONS RTXNS,
RCV_SHIPMENT_HEADERS RSH,
RCV_SHIPMENT_LINES RSL
WHERE AID1.PREPAY_DISTRIBUTION_ID = AID2.INVOICE_DISTRIBUTION_ID
AND AI.INVOICE_ID = AID2.INVOICE_ID
AND AID1.AMOUNT < 0
AND nvl(AID1.REVERSAL_FLAG, 'N') != 'Y'
AND AID1.TAX_CODE_ID = ATC.TAX_ID(+)
AND AID1.LINE_TYPE_LOOKUP_CODE = 'PREPAY'
AND AI.VENDOR_ID = PV.VENDOR_ID
AND AI.VENDOR_SITE_ID = PVS.VENDOR_SITE_ID
AND AID1.PO_DISTRIBUTION_ID = PD.PO_DISTRIBUTION_ID(+)
AND PD.PO_HEADER_ID = PH.PO_HEADER_ID(+)
AND PD.LINE_LOCATION_ID = PLL.LINE_LOCATION_ID(+)
AND PLL.PO_LINE_ID = PL.PO_LINE_ID(+)
AND AID1.RCV_TRANSACTION_ID = RTXNS.TRANSACTION_ID(+)
AND RTXNS.SHIPMENT_LINE_ID = RSL.SHIPMENT_LINE_ID(+)
AND RSL.SHIPMENT_HEADER_ID = RSH.SHIPMENT_HEADER_ID(+);
执行计划如下。
-------------------------------------------------------------------------------------
| Id |Operation | Name |Rows |Bytes|Cost |
-------------------------------------------------------------------------------------
| 0 |SELECT STATEMENT | | 1| 69| 722|
|* 1 | FILTER | | | | |
| 2 | SORT GROUP BY | | 1| 69| 722|
| 3 | NESTED LOOPS OUTER | | 3| 207| 697|
|* 4 | TABLE ACCESS FULL |AP_INVOICES_ALL | 3| 153| 694|
| 5 | VIEW PUSHED PREDICATE |AP_UNAPPLY_PREPAYS_V | 1| 18| 1|
| 6 | NESTED LOOPS | | 1| 372| 3|
| 7 | NESTED LOOPS | | 1| 368| 3|
| 8 | NESTED LOOPS | | 1| 361| 2|
| 9 | NESTED LOOPS | | 1| 347| 1|
| 10 | NESTED LOOPS OUTER | | 1| 334| 1|
| 11 | NESTED LOOPS OUTER | | 1| 321| 1|
| 12 | NESTED LOOPS OUTER | | 1| 295| 1|
| 13 | NESTED LOOPS OUTER | | 1| 269| 1|
| 14 | NESTED LOOPS OUTER | | 1| 243| 1|
| 15 | NESTED LOOPS OUTER | | 1| 197| 1|
| 16 | NESTED LOOPS OUTER | | 1| 157| 1|
| 17 | NESTED LOOPS OUTER | | 1| 98| 1|
|*18 | TABLE ACCESS BY INDEX ROWID|AP_INVOICE_DISTRIBUTIONS_ALL | 1| 72| 1|
|*19 | INDEX FULL SCAN |AP_INVOICE_DISTRIBUTIONS_N20 | 1| | |
|*20 | TABLE ACCESS BY INDEX ROWID|AP_TAX_CODES_ALL | 1| 26| |
|*21 | INDEX UNIQUE SCAN |AP_TAX_CODES_U1 | 1| | |
|*22 | TABLE ACCESS BY INDEX ROWID|PO_DISTRIBUTIONS_ALL | 1| 59| |
|*23 | INDEX UNIQUE SCAN |PO_DISTRIBUTIONS_U1 | 1| | |
|*24 | TABLE ACCESS BY INDEX ROWID |PO_HEADERS_ALL | 1| 40| |
|*25 | INDEX UNIQUE SCAN |PO_HEADERS_U1 | 1| | |
|*26 | TABLE ACCESS BY INDEX ROWID |PO_LINE_LOCATIONS_ALL | 1| 46| |
|*27 | INDEX UNIQUE SCAN |PO_LINE_LOCATIONS_U1 | 1| | |
|*28 | TABLE ACCESS BY INDEX ROWID |PO_LINES_ALL | 1| 26| |
|*29 | INDEX UNIQUE SCAN |PO_LINES_U1 | 1| | |
| 30 | TABLE ACCESS BY INDEX ROWID |RCV_TRANSACTIONS | 1| 26| |
|*31 | INDEX UNIQUE SCAN |RCV_TRANSACTIONS_U1 | 1| | |
| 32 | TABLE ACCESS BY INDEX ROWID |RCV_SHIPMENT_LINES | 1| 26| |
|*33 | INDEX UNIQUE SCAN |RCV_SHIPMENT_LINES_U1 | 1| | |
|*34 | INDEX UNIQUE SCAN |RCV_SHIPMENT_HEADERS_U1 | 1| 13| |
|*35 | TABLE ACCESS BY INDEX ROWID |AP_INVOICE_DISTRIBUTIONS_ALL | 1| 13| |
|*36 | INDEX UNIQUE SCAN |AP_INVOICE_DISTRIBUTIONS_U2 | 1| | |
|*37 | TABLE ACCESS BY INDEX ROWID |AP_INVOICES_ALL | 1| 14| 1|
|*38 | INDEX UNIQUE SCAN |AP_INVOICES_U1 | 1| | |
|*39 | TABLE ACCESS BY INDEX ROWID |PO_VENDOR_SITES_ALL | 1| 7| 1|
|*40 | INDEX UNIQUE SCAN |PO_VENDOR_SITES_U1 | 1| | |
|*41 | INDEX UNIQUE SCAN |PO_VENDORS_U1 | 1| 4| |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("A"."INVOICE_AMOUNT"-SUM(NVL("B"."PREPAY_AMOUNT_APPLIED",0))>0)
4 - filter("A"."ORG_ID"=126 AND "A"."SOURCE"='OSM IMPORTED' AND
"A"."INVOICE_NUM">=NVL(NULL,"A"."INVOICE_NUM") AND "A"."INVOICE_NUM"<=NVL(NULL,"A"."INVOICE_NUM"))
18 - filter("A"."INVOICE_ID"="AP_INVOICE_DISTRIBUTIONS_ALL"."INVOICE_ID" AND
"AP_INVOICE_DISTRIBUTIONS_ALL"."AMOUNT"<0 AND NVL("AP_INVOICE_DISTRIBUTIONS_ALL"."REVERSAL_FLAG",'N')<>'Y'
AND "AP_INVOICE_DISTRIBUTIONS_ALL"."LINE_TYPE_LOOKUP_CODE"='PREPAY' AND
NVL("AP_INVOICE_DISTRIBUTIONS_ALL"."ORG_ID",NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
19 - filter("AP_INVOICE_DISTRIBUTIONS_ALL"."PREPAY_DISTRIBUTION_ID" IS NOT NULL)
20 - filter(NVL("AP_TAX_CODES_ALL"."ORG_ID"(+),NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
21 - access("AP_INVOICE_DISTRIBUTIONS_ALL"."TAX_CODE_ID"="AP_TAX_CODES_ALL"."TAX_ID"(+))
22 - filter(NVL("PO_DISTRIBUTIONS_ALL"."ORG_ID"(+),NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
23 - access("AP_INVOICE_DISTRIBUTIONS_ALL"."PO_DISTRIBUTION_ID"="PO_DISTRIBUTIONS_ALL"."PO_DISTRIBUTION_ID"
(+))
24 - filter(NVL("PO_HEADERS_ALL"."ORG_ID"(+),NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
25 - access("PO_DISTRIBUTIONS_ALL"."PO_HEADER_ID"="PO_HEADERS_ALL"."PO_HEADER_ID"(+))
26 - filter(NVL("PO_LINE_LOCATIONS_ALL"."ORG_ID"(+),NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
27 - access("PO_DISTRIBUTIONS_ALL"."LINE_LOCATION_ID"="PO_LINE_LOCATIONS_ALL"."LINE_LOCATION_ID"(+))
28 - filter(NVL("PO_LINES_ALL"."ORG_ID"(+),NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
29 - access("PO_LINE_LOCATIONS_ALL"."PO_LINE_ID"="PO_LINES_ALL"."PO_LINE_ID"(+))
31 - access("AP_INVOICE_DISTRIBUTIONS_ALL"."RCV_TRANSACTION_ID"="RTXNS"."TRANSACTION_ID"(+))
33 - access("RTXNS"."SHIPMENT_LINE_ID"="RSL"."SHIPMENT_LINE_ID"(+))
34 - access("RSL"."SHIPMENT_HEADER_ID"="RSH"."SHIPMENT_HEADER_ID"(+))
35 - filter(NVL("AP_INVOICE_DISTRIBUTIONS_ALL"."ORG_ID",NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
36 - access("AP_INVOICE_DISTRIBUTIONS_ALL"."PREPAY_DISTRIBUTION_ID"="AP_INVOICE_DISTRIBUTIONS_ALL"."INVOICE
_DISTRIBUTION_ID")
37 - filter(NVL("AP_INVOICES_ALL"."ORG_ID",NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
38 - access("AP_INVOICES_ALL"."INVOICE_ID"="AP_INVOICE_DISTRIBUTIONS_ALL"."INVOICE_ID")
39 - filter(NVL("PO_VENDOR_SITES_ALL"."ORG_ID",NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
40 - access("AP_INVOICES_ALL"."VENDOR_SITE_ID"="PO_VENDOR_SITES_ALL"."VENDOR_SITE_ID")
41 - access("AP_INVOICES_ALL"."VENDOR_ID"="PV"."VENDOR_ID")
Note: cpu costing is off
从执行计划中 Id=5 看到,该 SQL 发生了连接列谓词推入,视图 AP_UNAPPLY_PREPAYS_V 被当作了嵌套循环的被驱动表。原始 SQL 中,两表的关联条件如下。
WHERE A.INVOICE_ID = B.INVOICE_ID(+)
视图中 B.INVOICE_ID 来自于 AID1.INVOICE_ID INVOICE_ID,因此,我们应该检查执行计划中 AID1.INVOICE_ID INVOICE_ID 是否走了索引。我们从执行计划中 Id=18 发现如下。
18 - filter("A"."INVOICE_ID"="AP_INVOICE_DISTRIBUTIONS_ALL"."INVOICE_ID"
这里是将连接列谓词推入到执行计划中 Id=18 进行的过滤操作,并不是将连接列谓词推入视图让表 AP_INVOICE_DISTRIBUTIONS 走 INVOICE_ID 的索引。这显然大错特错了。
因为发生了谓词推入,视图 AP_UNAPPLY_PREPAYS_V 作为嵌套循环被驱动表会被多次扫描。这里的谓词推入的时候只是起的过滤作用,并没有走谓词连接列索引。因此,我们使用 HINT:USE_HASH(A,B),让两表走 HASH 连接,从而避免视图被多次反复扫描。添加 HINT 之后,SQL 能在 1 秒返回结果。
我们也可以调整隐含参数,关闭连接列谓词推入。
ALTER SESSION SET "_push_join_predicate" = FALSE;
禁止连接列谓词推入,也能达到效果。
我们还可以检查表 AP_INVOICE_DISTRIBUTIONS 表的 INVOICE_ID 列是否存在索引,如果没有索引,可以建立一个索引,从而实现真正的连接列谓词推入。但是因为当时使用 USE_HASH 已经优化了 SQL,所以没有继续检查。
最终的 SQL 如下。
SELECT *
FROM (SELECT /*+ use_hash(a,b) */ A.INVOICE_ID,
A.VENDOR_ID,
A.INVOICE_NUM,
A.INVOICE_AMOUNT,
A.GL_DATE,
A.INVOICE_CURRENCY_CODE,
SUM(NVL(B.PREPAY_AMOUNT_APPLIED, 0)) PAID_AMOUNT,
A.INVOICE_AMOUNT - SUM(NVL(B.PREPAY_AMOUNT_APPLIED, 0)) REMAIN
FROM ap.AP_INVOICES_ALL A, APPS.AP_UNAPPLY_PREPAYS_V B
WHERE A.INVOICE_ID = B.INVOICE_ID(+)
AND A.ORG_ID = 126 /*:B4*/
AND A.SOURCE = 'OSM IMPORTED' /*:B3*/
AND A.INVOICE_NUM BETWEEN NVL( /*:B2*/ null, A.INVOICE_NUM) AND
NVL( /*:B1*/ null, A.INVOICE_NUM)
GROUP BY A.INVOICE_ID,
A.INVOICE_NUM,
A.INVOICE_AMOUNT,
A.VENDOR_ID,
A.GL_DATE,
A.INVOICE_CURRENCY_CODE)
WHERE REMAIN > 0 ;
添加 HINT 后的执行计划如下。
-------------------------------------------------------------------------------------
| Id |Operation | Name |Rows|Bytes|Cost|
-------------------------------------------------------------------------------------
| 0 |SELECT STATEMENT | | 1| 69| 723|
|* 1 | FILTER | | | | |
| 2 | SORT GROUP BY | | 1| 69| 723|
|* 3 | HASH JOIN OUTER | | 3| 207| 698|
|* 4 | TABLE ACCESS FULL |AP_INVOICES_ALL | 3| 153| 694|
| 5 | VIEW |AP_UNAPPLY_PREPAYS_V | 1| 18| 3|
| 6 | NESTED LOOPS | | 1| 372| 3|
| 7 | NESTED LOOPS | | 1| 368| 3|
| 8 | NESTED LOOPS | | 1| 361| 2|
| 9 | NESTED LOOPS | | 1| 347| 1|
| 10 | NESTED LOOPS OUTER | | 1| 334| 1|
| 11 | NESTED LOOPS OUTER | | 1| 321| 1|
| 12 | NESTED LOOPS OUTER | | 1| 295| 1|
| 13 | NESTED LOOPS OUTER | | 1| 269| 1|
| 14 | NESTED LOOPS OUTER | | 1| 243| 1|
| 15 | NESTED LOOPS OUTER | | 1| 197| 1|
| 16 | NESTED LOOPS OUTER | | 1| 157| 1|
| 17 | NESTED LOOPS OUTER | | 1| 98| 1|
|*18 | TABLE ACCESS BY INDEX ROWID|AP_INVOICE_DISTRIBUTIONS_ALL| 1| 72| 1|
|*19 | INDEX FULL SCAN |AP_INVOICE_DISTRIBUTIONS_N20| 1| | |
|*20 | TABLE ACCESS BY INDEX ROWID|AP_TAX_CODES_ALL | 1| 26| |
|*21 | INDEX UNIQUE SCAN |AP_TAX_CODES_U1 | 1| | |
|*22 | TABLE ACCESS BY INDEX ROWID|PO_DISTRIBUTIONS_ALL | 1| 59| |
|*23 | INDEX UNIQUE SCAN |PO_DISTRIBUTIONS_U1 | 1| | |
|*24 | TABLE ACCESS BY INDEX ROWID |PO_HEADERS_ALL | 1| 40| |
|*25 | INDEX UNIQUE SCAN |PO_HEADERS_U1 | 1| | |
|*26 | TABLE ACCESS BY INDEX ROWID |PO_LINE_LOCATIONS_ALL | 1| 46| |
|*27 | INDEX UNIQUE SCAN |PO_LINE_LOCATIONS_U1 | 1| | |
|*28 | TABLE ACCESS BY INDEX ROWID |PO_LINES_ALL | 1| 26| |
|*29 | INDEX UNIQUE SCAN |PO_LINES_U1 | 1| | |
| 30 | TABLE ACCESS BY INDEX ROWID |RCV_TRANSACTIONS | 1| 26| |
|*31 | INDEX UNIQUE SCAN |RCV_TRANSACTIONS_U1 | 1| | |
| 32 | TABLE ACCESS BY INDEX ROWID |RCV_SHIPMENT_LINES | 1| 26| |
|*33 | INDEX UNIQUE SCAN |RCV_SHIPMENT_LINES_U1 | 1| | |
|*34 | INDEX UNIQUE SCAN |RCV_SHIPMENT_HEADERS_U1 | 1| 13| |
|*35 | TABLE ACCESS BY INDEX ROWID |AP_INVOICE_DISTRIBUTIONS_ALL| 1| 13| |
|*36 | INDEX UNIQUE SCAN |AP_INVOICE_DISTRIBUTIONS_U2 | 1| | |
|*37 | TABLE ACCESS BY INDEX ROWID |AP_INVOICES_ALL | 1| 14| 1|
|*38 | INDEX UNIQUE SCAN |AP_INVOICES_U1 | 1| | |
|*39 | TABLE ACCESS BY INDEX ROWID |PO_VENDOR_SITES_ALL | 1| 7| 1|
|*40 | INDEX UNIQUE SCAN |PO_VENDOR_SITES_U1 | 1| | |
|*41 | INDEX UNIQUE SCAN |PO_VENDORS_U1 | 1| 4| |
-------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("A"."INVOICE_AMOUNT"-SUM(NVL("B"."PREPAY_AMOUNT_APPLIED",0))>0)
3 - access("A"."INVOICE_ID"="B"."INVOICE_ID"(+))
4 - filter("A"."ORG_ID"=126 AND "A"."SOURCE"='OSM IMPORTED' AND
"A"."INVOICE_NUM">=NVL(NULL,"A"."INVOICE_NUM") AND "A"."INVOICE_NUM"<=NVL(NULL,"A"."INVOICE_NUM"))
18 - filter("AP_INVOICE_DISTRIBUTIONS_ALL"."AMOUNT"<0 AND
NVL("AP_INVOICE_DISTRIBUTIONS_ALL"."REVERSAL_FLAG",'N')<>'Y' AND
"AP_INVOICE_DISTRIBUTIONS_ALL"."LINE_TYPE_LOOKUP_CODE"='PREPAY' AND
NVL("AP_INVOICE_DISTRIBUTIONS_ALL"."ORG_ID",NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
19 - filter("AP_INVOICE_DISTRIBUTIONS_ALL"."PREPAY_DISTRIBUTION_ID" IS NOT NULL)
20 - filter(NVL("AP_TAX_CODES_ALL"."ORG_ID"(+),NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
21 - access("AP_INVOICE_DISTRIBUTIONS_ALL"."TAX_CODE_ID"="AP_TAX_CODES_ALL"."TAX_ID"(+))
22 - filter(NVL("PO_DISTRIBUTIONS_ALL"."ORG_ID"(+),NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
23 - access("AP_INVOICE_DISTRIBUTIONS_ALL"."PO_DISTRIBUTION_ID"="PO_DISTRIBUTIONS_ALL"."PO_DISTRIBUTION_ID"
(+))
24 - filter(NVL("PO_HEADERS_ALL"."ORG_ID"(+),NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
25 - access("PO_DISTRIBUTIONS_ALL"."PO_HEADER_ID"="PO_HEADERS_ALL"."PO_HEADER_ID"(+))
26 - filter(NVL("PO_LINE_LOCATIONS_ALL"."ORG_ID"(+),NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
27 - access("PO_DISTRIBUTIONS_ALL"."LINE_LOCATION_ID"="PO_LINE_LOCATIONS_ALL"."LINE_LOCATION_ID"(+))
28 - filter(NVL("PO_LINES_ALL"."ORG_ID"(+),NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
29 - access("PO_LINE_LOCATIONS_ALL"."PO_LINE_ID"="PO_LINES_ALL"."PO_LINE_ID"(+))
31 - access("AP_INVOICE_DISTRIBUTIONS_ALL"."RCV_TRANSACTION_ID"="RTXNS"."TRANSACTION_ID"(+))
33 - access("RTXNS"."SHIPMENT_LINE_ID"="RSL"."SHIPMENT_LINE_ID"(+))
34 - access("RSL"."SHIPMENT_HEADER_ID"="RSH"."SHIPMENT_HEADER_ID"(+))
35 - filter(NVL("AP_INVOICE_DISTRIBUTIONS_ALL"."ORG_ID",NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
36 - access("AP_INVOICE_DISTRIBUTIONS_ALL"."PREPAY_DISTRIBUTION_ID"="AP_INVOICE_DISTRIBUTIONS_ALL"."INVOICE
_DISTRIBUTION_ID")
37 - filter(NVL("AP_INVOICES_ALL"."ORG_ID",NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
38 - access("AP_INVOICES_ALL"."INVOICE_ID"="AP_INVOICE_DISTRIBUTIONS_ALL"."INVOICE_ID")
39 - filter(NVL("PO_VENDOR_SITES_ALL"."ORG_ID",NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
40 - access("AP_INVOICES_ALL"."VENDOR_SITE_ID"="PO_VENDOR_SITES_ALL"."VENDOR_SITE_ID")
41 - access("AP_INVOICES_ALL"."VENDOR_ID"="PV"."VENDOR_ID")
水平高的读者或许有疑问,执行计划 Id=19 是 INDEX FULL SCAN,然后再回表过滤,这里也有性能问题,全表扫描效率应该也比 INDEX FULL SCAN 再回表效率高!是的,我们也发现了这个地方有性能问题,但是既然 SQL 都执行到 1 秒了,也就没继续优化了,千万别得了优化强迫症。
最后,我们再次强调,如果发生了连接列谓词推入,一定要检查执行计划中是否走了谓词被推入的表的连接列索引。