2011 年,一位 ITPUB 的网友请求优化如下 SQL。

    1. SELECT *
    2. FROM (SELECT A.INVOICE_ID,
    3. A.VENDOR_ID,
    4. A.INVOICE_NUM,
    5. A.INVOICE_AMOUNT,
    6. A.GL_DATE,
    7. A.INVOICE_CURRENCY_CODE,
    8. SUM(NVL(B.PREPAY_AMOUNT_APPLIED, 0)) PAID_AMOUNT,
    9. A.INVOICE_AMOUNT - SUM(NVL(B.PREPAY_AMOUNT_APPLIED, 0)) REMAIN
    10. FROM ap.AP_INVOICES_ALL A, APPS.AP_UNAPPLY_PREPAYS_V B
    11. WHERE A.INVOICE_ID = B.INVOICE_ID(+)
    12. AND A.ORG_ID = 126 /*:B4*/
    13. AND A.SOURCE = 'OSM IMPORTED' /*:B3*/
    14. AND A.INVOICE_NUM BETWEEN NVL( /*:B2*/ null, A.INVOICE_NUM) AND
    15. NVL( /*:B1*/ null, A.INVOICE_NUM)
    16. GROUP BY A.INVOICE_ID,
    17. A.INVOICE_NUM,
    18. A.INVOICE_AMOUNT,
    19. A.VENDOR_ID,
    20. A.GL_DATE,
    21. A.INVOICE_CURRENCY_CODE)
    22. WHERE REMAIN > 0 ;

    该 SQL 要执行 1 个多小时,AP_UNAPPLY_PREPAYS_V 是一个视图,代码如下。

    1. CREATE OR REPLACE VIEW APPS.AP_UNAPPLY_PREPAYS_V AS
    2. SELECT AID1.ROWID ROW_ID,
    3. AID1.INVOICE_ID INVOICE_ID,
    4. AID1.INVOICE_DISTRIBUTION_ID INVOICE_DISTRIBUTION_ID,
    5. AID1.PREPAY_DISTRIBUTION_ID PREPAY_DISTRIBUTION_ID,
    6. AID1.DISTRIBUTION_LINE_NUMBER PREPAY_DIST_NUMBER,
    7. (-1) * AID1.AMOUNT PREPAY_AMOUNT_APPLIED,
    8. nvl(AID2.PREPAY_AMOUNT_REMAINING, AID2.AMOUNT) PREPAY_AMOUNT_REMAINING,
    9. AID1.DIST_CODE_COMBINATION_ID DIST_CODE_COMBINATION_ID,
    10. AID1.ACCOUNTING_DATE ACCOUNTING_DATE,
    11. AID1.PERIOD_NAME PERIOD_NAME,
    12. AID1.SET_OF_BOOKS_ID SET_OF_BOOKS_ID,
    13. AID1.DESCRIPTION DESCRIPTION,
    14. AID1.PO_DISTRIBUTION_ID PO_DISTRIBUTION_ID,
    15. AID1.RCV_TRANSACTION_ID RCV_TRANSACTION_ID,
    16. AID1.ORG_ID ORG_ID,
    17. AI.INVOICE_NUM PREPAY_NUMBER,
    18. AI.VENDOR_ID VENDOR_ID,
    19. AI.VENDOR_SITE_ID VENDOR_SITE_ID,
    20. ATC.TAX_ID TAX_ID,
    21. ATC.NAME TAX_CODE,
    22. PH.SEGMENT1 PO_NUMBER,
    23. PV.VENDOR_NAME VENDOR_NAME,
    24. PV.SEGMENT1 VENDOR_NUMBER,
    25. PVS.VENDOR_SITE_CODE VENDOR_SITE_CODE,
    26. RSH.RECEIPT_NUM RECEIPT_NUMBER
    27. FROM AP_INVOICES AI,
    28. AP_INVOICE_DISTRIBUTIONS AID1,
    29. AP_INVOICE_DISTRIBUTIONS AID2,
    30. AP_TAX_CODES ATC,
    31. PO_VENDORS PV,
    32. PO_VENDOR_SITES PVS,
    33. PO_DISTRIBUTIONS PD,
    34. PO_HEADERS PH,
    35. PO_LINES PL,
    36. PO_LINE_LOCATIONS PLL,
    37. RCV_TRANSACTIONS RTXNS,
    38. RCV_SHIPMENT_HEADERS RSH,
    39. RCV_SHIPMENT_LINES RSL
    40. WHERE AID1.PREPAY_DISTRIBUTION_ID = AID2.INVOICE_DISTRIBUTION_ID
    41. AND AI.INVOICE_ID = AID2.INVOICE_ID
    42. AND AID1.AMOUNT < 0
    43. AND nvl(AID1.REVERSAL_FLAG, 'N') != 'Y'
    44. AND AID1.TAX_CODE_ID = ATC.TAX_ID(+)
    45. AND AID1.LINE_TYPE_LOOKUP_CODE = 'PREPAY'
    46. AND AI.VENDOR_ID = PV.VENDOR_ID
    47. AND AI.VENDOR_SITE_ID = PVS.VENDOR_SITE_ID
    48. AND AID1.PO_DISTRIBUTION_ID = PD.PO_DISTRIBUTION_ID(+)
    49. AND PD.PO_HEADER_ID = PH.PO_HEADER_ID(+)
    50. AND PD.LINE_LOCATION_ID = PLL.LINE_LOCATION_ID(+)
    51. AND PLL.PO_LINE_ID = PL.PO_LINE_ID(+)
    52. AND AID1.RCV_TRANSACTION_ID = RTXNS.TRANSACTION_ID(+)
    53. AND RTXNS.SHIPMENT_LINE_ID = RSL.SHIPMENT_LINE_ID(+)
    54. AND RSL.SHIPMENT_HEADER_ID = RSH.SHIPMENT_HEADER_ID(+);

    执行计划如下。

    1. -------------------------------------------------------------------------------------
    2. | Id |Operation | Name |Rows |Bytes|Cost |
    3. -------------------------------------------------------------------------------------
    4. | 0 |SELECT STATEMENT | | 1| 69| 722|
    5. |* 1 | FILTER | | | | |
    6. | 2 | SORT GROUP BY | | 1| 69| 722|
    7. | 3 | NESTED LOOPS OUTER | | 3| 207| 697|
    8. |* 4 | TABLE ACCESS FULL |AP_INVOICES_ALL | 3| 153| 694|
    9. | 5 | VIEW PUSHED PREDICATE |AP_UNAPPLY_PREPAYS_V | 1| 18| 1|
    10. | 6 | NESTED LOOPS | | 1| 372| 3|
    11. | 7 | NESTED LOOPS | | 1| 368| 3|
    12. | 8 | NESTED LOOPS | | 1| 361| 2|
    13. | 9 | NESTED LOOPS | | 1| 347| 1|
    14. | 10 | NESTED LOOPS OUTER | | 1| 334| 1|
    15. | 11 | NESTED LOOPS OUTER | | 1| 321| 1|
    16. | 12 | NESTED LOOPS OUTER | | 1| 295| 1|
    17. | 13 | NESTED LOOPS OUTER | | 1| 269| 1|
    18. | 14 | NESTED LOOPS OUTER | | 1| 243| 1|
    19. | 15 | NESTED LOOPS OUTER | | 1| 197| 1|
    20. | 16 | NESTED LOOPS OUTER | | 1| 157| 1|
    21. | 17 | NESTED LOOPS OUTER | | 1| 98| 1|
    22. |*18 | TABLE ACCESS BY INDEX ROWID|AP_INVOICE_DISTRIBUTIONS_ALL | 1| 72| 1|
    23. |*19 | INDEX FULL SCAN |AP_INVOICE_DISTRIBUTIONS_N20 | 1| | |
    24. |*20 | TABLE ACCESS BY INDEX ROWID|AP_TAX_CODES_ALL | 1| 26| |
    25. |*21 | INDEX UNIQUE SCAN |AP_TAX_CODES_U1 | 1| | |
    26. |*22 | TABLE ACCESS BY INDEX ROWID|PO_DISTRIBUTIONS_ALL | 1| 59| |
    27. |*23 | INDEX UNIQUE SCAN |PO_DISTRIBUTIONS_U1 | 1| | |
    28. |*24 | TABLE ACCESS BY INDEX ROWID |PO_HEADERS_ALL | 1| 40| |
    29. |*25 | INDEX UNIQUE SCAN |PO_HEADERS_U1 | 1| | |
    30. |*26 | TABLE ACCESS BY INDEX ROWID |PO_LINE_LOCATIONS_ALL | 1| 46| |
    31. |*27 | INDEX UNIQUE SCAN |PO_LINE_LOCATIONS_U1 | 1| | |
    32. |*28 | TABLE ACCESS BY INDEX ROWID |PO_LINES_ALL | 1| 26| |
    33. |*29 | INDEX UNIQUE SCAN |PO_LINES_U1 | 1| | |
    34. | 30 | TABLE ACCESS BY INDEX ROWID |RCV_TRANSACTIONS | 1| 26| |
    35. |*31 | INDEX UNIQUE SCAN |RCV_TRANSACTIONS_U1 | 1| | |
    36. | 32 | TABLE ACCESS BY INDEX ROWID |RCV_SHIPMENT_LINES | 1| 26| |
    37. |*33 | INDEX UNIQUE SCAN |RCV_SHIPMENT_LINES_U1 | 1| | |
    38. |*34 | INDEX UNIQUE SCAN |RCV_SHIPMENT_HEADERS_U1 | 1| 13| |
    39. |*35 | TABLE ACCESS BY INDEX ROWID |AP_INVOICE_DISTRIBUTIONS_ALL | 1| 13| |
    40. |*36 | INDEX UNIQUE SCAN |AP_INVOICE_DISTRIBUTIONS_U2 | 1| | |
    41. |*37 | TABLE ACCESS BY INDEX ROWID |AP_INVOICES_ALL | 1| 14| 1|
    42. |*38 | INDEX UNIQUE SCAN |AP_INVOICES_U1 | 1| | |
    43. |*39 | TABLE ACCESS BY INDEX ROWID |PO_VENDOR_SITES_ALL | 1| 7| 1|
    44. |*40 | INDEX UNIQUE SCAN |PO_VENDOR_SITES_U1 | 1| | |
    45. |*41 | INDEX UNIQUE SCAN |PO_VENDORS_U1 | 1| 4| |
    46. -------------------------------------------------------------------------------------
    47. Predicate Information (identified by operation id):
    48. ---------------------------------------------------
    49. 1 - filter("A"."INVOICE_AMOUNT"-SUM(NVL("B"."PREPAY_AMOUNT_APPLIED",0))>0)
    50. 4 - filter("A"."ORG_ID"=126 AND "A"."SOURCE"='OSM IMPORTED' AND
    51. "A"."INVOICE_NUM">=NVL(NULL,"A"."INVOICE_NUM") AND "A"."INVOICE_NUM"<=NVL(NULL,"A"."INVOICE_NUM"))
    52. 18 - filter("A"."INVOICE_ID"="AP_INVOICE_DISTRIBUTIONS_ALL"."INVOICE_ID" AND
    53. "AP_INVOICE_DISTRIBUTIONS_ALL"."AMOUNT"<0 AND NVL("AP_INVOICE_DISTRIBUTIONS_ALL"."REVERSAL_FLAG",'N')<>'Y'
    54. AND "AP_INVOICE_DISTRIBUTIONS_ALL"."LINE_TYPE_LOOKUP_CODE"='PREPAY' AND
    55. NVL("AP_INVOICE_DISTRIBUTIONS_ALL"."ORG_ID",NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
    56. ',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
    57. 19 - filter("AP_INVOICE_DISTRIBUTIONS_ALL"."PREPAY_DISTRIBUTION_ID" IS NOT NULL)
    58. 20 - filter(NVL("AP_TAX_CODES_ALL"."ORG_ID"(+),NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
    59. ',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
    60. 21 - access("AP_INVOICE_DISTRIBUTIONS_ALL"."TAX_CODE_ID"="AP_TAX_CODES_ALL"."TAX_ID"(+))
    61. 22 - filter(NVL("PO_DISTRIBUTIONS_ALL"."ORG_ID"(+),NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
    62. ',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
    63. 23 - access("AP_INVOICE_DISTRIBUTIONS_ALL"."PO_DISTRIBUTION_ID"="PO_DISTRIBUTIONS_ALL"."PO_DISTRIBUTION_ID"
    64. (+))
    65. 24 - filter(NVL("PO_HEADERS_ALL"."ORG_ID"(+),NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
    66. ',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
    67. 25 - access("PO_DISTRIBUTIONS_ALL"."PO_HEADER_ID"="PO_HEADERS_ALL"."PO_HEADER_ID"(+))
    68. 26 - filter(NVL("PO_LINE_LOCATIONS_ALL"."ORG_ID"(+),NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
    69. ',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
    70. 27 - access("PO_DISTRIBUTIONS_ALL"."LINE_LOCATION_ID"="PO_LINE_LOCATIONS_ALL"."LINE_LOCATION_ID"(+))
    71. 28 - filter(NVL("PO_LINES_ALL"."ORG_ID"(+),NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
    72. ',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
    73. 29 - access("PO_LINE_LOCATIONS_ALL"."PO_LINE_ID"="PO_LINES_ALL"."PO_LINE_ID"(+))
    74. 31 - access("AP_INVOICE_DISTRIBUTIONS_ALL"."RCV_TRANSACTION_ID"="RTXNS"."TRANSACTION_ID"(+))
    75. 33 - access("RTXNS"."SHIPMENT_LINE_ID"="RSL"."SHIPMENT_LINE_ID"(+))
    76. 34 - access("RSL"."SHIPMENT_HEADER_ID"="RSH"."SHIPMENT_HEADER_ID"(+))
    77. 35 - filter(NVL("AP_INVOICE_DISTRIBUTIONS_ALL"."ORG_ID",NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
    78. ',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
    79. 36 - access("AP_INVOICE_DISTRIBUTIONS_ALL"."PREPAY_DISTRIBUTION_ID"="AP_INVOICE_DISTRIBUTIONS_ALL"."INVOICE
    80. _DISTRIBUTION_ID")
    81. 37 - filter(NVL("AP_INVOICES_ALL"."ORG_ID",NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
    82. ',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
    83. 38 - access("AP_INVOICES_ALL"."INVOICE_ID"="AP_INVOICE_DISTRIBUTIONS_ALL"."INVOICE_ID")
    84. 39 - filter(NVL("PO_VENDOR_SITES_ALL"."ORG_ID",NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
    85. ',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
    86. 40 - access("AP_INVOICES_ALL"."VENDOR_SITE_ID"="PO_VENDOR_SITES_ALL"."VENDOR_SITE_ID")
    87. 41 - access("AP_INVOICES_ALL"."VENDOR_ID"="PV"."VENDOR_ID")
    88. Note: cpu costing is off

    从执行计划中 Id=5 看到,该 SQL 发生了连接列谓词推入,视图 AP_UNAPPLY_PREPAYS_V 被当作了嵌套循环的被驱动表。原始 SQL 中,两表的关联条件如下。

    1. WHERE A.INVOICE_ID = B.INVOICE_ID(+)

    视图中 B.INVOICE_ID 来自于 AID1.INVOICE_ID INVOICE_ID,因此,我们应该检查执行计划中 AID1.INVOICE_ID INVOICE_ID 是否走了索引。我们从执行计划中 Id=18 发现如下。

    1. 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 秒返回结果。

    我们也可以调整隐含参数,关闭连接列谓词推入。

    1. ALTER SESSION SET "_push_join_predicate" = FALSE;

    禁止连接列谓词推入,也能达到效果。

    我们还可以检查表 AP_INVOICE_DISTRIBUTIONS 表的 INVOICE_ID 列是否存在索引,如果没有索引,可以建立一个索引,从而实现真正的连接列谓词推入。但是因为当时使用 USE_HASH 已经优化了 SQL,所以没有继续检查。

    最终的 SQL 如下。

    1. SELECT *
    2. FROM (SELECT /*+ use_hash(a,b) */ A.INVOICE_ID,
    3. A.VENDOR_ID,
    4. A.INVOICE_NUM,
    5. A.INVOICE_AMOUNT,
    6. A.GL_DATE,
    7. A.INVOICE_CURRENCY_CODE,
    8. SUM(NVL(B.PREPAY_AMOUNT_APPLIED, 0)) PAID_AMOUNT,
    9. A.INVOICE_AMOUNT - SUM(NVL(B.PREPAY_AMOUNT_APPLIED, 0)) REMAIN
    10. FROM ap.AP_INVOICES_ALL A, APPS.AP_UNAPPLY_PREPAYS_V B
    11. WHERE A.INVOICE_ID = B.INVOICE_ID(+)
    12. AND A.ORG_ID = 126 /*:B4*/
    13. AND A.SOURCE = 'OSM IMPORTED' /*:B3*/
    14. AND A.INVOICE_NUM BETWEEN NVL( /*:B2*/ null, A.INVOICE_NUM) AND
    15. NVL( /*:B1*/ null, A.INVOICE_NUM)
    16. GROUP BY A.INVOICE_ID,
    17. A.INVOICE_NUM,
    18. A.INVOICE_AMOUNT,
    19. A.VENDOR_ID,
    20. A.GL_DATE,
    21. A.INVOICE_CURRENCY_CODE)
    22. WHERE REMAIN > 0 ;

    添加 HINT 后的执行计划如下。

    1. -------------------------------------------------------------------------------------
    2. | Id |Operation | Name |Rows|Bytes|Cost|
    3. -------------------------------------------------------------------------------------
    4. | 0 |SELECT STATEMENT | | 1| 69| 723|
    5. |* 1 | FILTER | | | | |
    6. | 2 | SORT GROUP BY | | 1| 69| 723|
    7. |* 3 | HASH JOIN OUTER | | 3| 207| 698|
    8. |* 4 | TABLE ACCESS FULL |AP_INVOICES_ALL | 3| 153| 694|
    9. | 5 | VIEW |AP_UNAPPLY_PREPAYS_V | 1| 18| 3|
    10. | 6 | NESTED LOOPS | | 1| 372| 3|
    11. | 7 | NESTED LOOPS | | 1| 368| 3|
    12. | 8 | NESTED LOOPS | | 1| 361| 2|
    13. | 9 | NESTED LOOPS | | 1| 347| 1|
    14. | 10 | NESTED LOOPS OUTER | | 1| 334| 1|
    15. | 11 | NESTED LOOPS OUTER | | 1| 321| 1|
    16. | 12 | NESTED LOOPS OUTER | | 1| 295| 1|
    17. | 13 | NESTED LOOPS OUTER | | 1| 269| 1|
    18. | 14 | NESTED LOOPS OUTER | | 1| 243| 1|
    19. | 15 | NESTED LOOPS OUTER | | 1| 197| 1|
    20. | 16 | NESTED LOOPS OUTER | | 1| 157| 1|
    21. | 17 | NESTED LOOPS OUTER | | 1| 98| 1|
    22. |*18 | TABLE ACCESS BY INDEX ROWID|AP_INVOICE_DISTRIBUTIONS_ALL| 1| 72| 1|
    23. |*19 | INDEX FULL SCAN |AP_INVOICE_DISTRIBUTIONS_N20| 1| | |
    24. |*20 | TABLE ACCESS BY INDEX ROWID|AP_TAX_CODES_ALL | 1| 26| |
    25. |*21 | INDEX UNIQUE SCAN |AP_TAX_CODES_U1 | 1| | |
    26. |*22 | TABLE ACCESS BY INDEX ROWID|PO_DISTRIBUTIONS_ALL | 1| 59| |
    27. |*23 | INDEX UNIQUE SCAN |PO_DISTRIBUTIONS_U1 | 1| | |
    28. |*24 | TABLE ACCESS BY INDEX ROWID |PO_HEADERS_ALL | 1| 40| |
    29. |*25 | INDEX UNIQUE SCAN |PO_HEADERS_U1 | 1| | |
    30. |*26 | TABLE ACCESS BY INDEX ROWID |PO_LINE_LOCATIONS_ALL | 1| 46| |
    31. |*27 | INDEX UNIQUE SCAN |PO_LINE_LOCATIONS_U1 | 1| | |
    32. |*28 | TABLE ACCESS BY INDEX ROWID |PO_LINES_ALL | 1| 26| |
    33. |*29 | INDEX UNIQUE SCAN |PO_LINES_U1 | 1| | |
    34. | 30 | TABLE ACCESS BY INDEX ROWID |RCV_TRANSACTIONS | 1| 26| |
    35. |*31 | INDEX UNIQUE SCAN |RCV_TRANSACTIONS_U1 | 1| | |
    36. | 32 | TABLE ACCESS BY INDEX ROWID |RCV_SHIPMENT_LINES | 1| 26| |
    37. |*33 | INDEX UNIQUE SCAN |RCV_SHIPMENT_LINES_U1 | 1| | |
    38. |*34 | INDEX UNIQUE SCAN |RCV_SHIPMENT_HEADERS_U1 | 1| 13| |
    39. |*35 | TABLE ACCESS BY INDEX ROWID |AP_INVOICE_DISTRIBUTIONS_ALL| 1| 13| |
    40. |*36 | INDEX UNIQUE SCAN |AP_INVOICE_DISTRIBUTIONS_U2 | 1| | |
    41. |*37 | TABLE ACCESS BY INDEX ROWID |AP_INVOICES_ALL | 1| 14| 1|
    42. |*38 | INDEX UNIQUE SCAN |AP_INVOICES_U1 | 1| | |
    43. |*39 | TABLE ACCESS BY INDEX ROWID |PO_VENDOR_SITES_ALL | 1| 7| 1|
    44. |*40 | INDEX UNIQUE SCAN |PO_VENDOR_SITES_U1 | 1| | |
    45. |*41 | INDEX UNIQUE SCAN |PO_VENDORS_U1 | 1| 4| |
    46. -------------------------------------------------------------------------------------
    47. Predicate Information (identified by operation id):
    48. ---------------------------------------------------
    49. 1 - filter("A"."INVOICE_AMOUNT"-SUM(NVL("B"."PREPAY_AMOUNT_APPLIED",0))>0)
    50. 3 - access("A"."INVOICE_ID"="B"."INVOICE_ID"(+))
    51. 4 - filter("A"."ORG_ID"=126 AND "A"."SOURCE"='OSM IMPORTED' AND
    52. "A"."INVOICE_NUM">=NVL(NULL,"A"."INVOICE_NUM") AND "A"."INVOICE_NUM"<=NVL(NULL,"A"."INVOICE_NUM"))
    53. 18 - filter("AP_INVOICE_DISTRIBUTIONS_ALL"."AMOUNT"<0 AND
    54. NVL("AP_INVOICE_DISTRIBUTIONS_ALL"."REVERSAL_FLAG",'N')<>'Y' AND
    55. "AP_INVOICE_DISTRIBUTIONS_ALL"."LINE_TYPE_LOOKUP_CODE"='PREPAY' AND
    56. NVL("AP_INVOICE_DISTRIBUTIONS_ALL"."ORG_ID",NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
    57. ',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
    58. 19 - filter("AP_INVOICE_DISTRIBUTIONS_ALL"."PREPAY_DISTRIBUTION_ID" IS NOT NULL)
    59. 20 - filter(NVL("AP_TAX_CODES_ALL"."ORG_ID"(+),NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
    60. ',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
    61. 21 - access("AP_INVOICE_DISTRIBUTIONS_ALL"."TAX_CODE_ID"="AP_TAX_CODES_ALL"."TAX_ID"(+))
    62. 22 - filter(NVL("PO_DISTRIBUTIONS_ALL"."ORG_ID"(+),NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
    63. ',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
    64. 23 - access("AP_INVOICE_DISTRIBUTIONS_ALL"."PO_DISTRIBUTION_ID"="PO_DISTRIBUTIONS_ALL"."PO_DISTRIBUTION_ID"
    65. (+))
    66. 24 - filter(NVL("PO_HEADERS_ALL"."ORG_ID"(+),NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
    67. ',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
    68. 25 - access("PO_DISTRIBUTIONS_ALL"."PO_HEADER_ID"="PO_HEADERS_ALL"."PO_HEADER_ID"(+))
    69. 26 - filter(NVL("PO_LINE_LOCATIONS_ALL"."ORG_ID"(+),NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
    70. ',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
    71. 27 - access("PO_DISTRIBUTIONS_ALL"."LINE_LOCATION_ID"="PO_LINE_LOCATIONS_ALL"."LINE_LOCATION_ID"(+))
    72. 28 - filter(NVL("PO_LINES_ALL"."ORG_ID"(+),NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
    73. ',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
    74. 29 - access("PO_LINE_LOCATIONS_ALL"."PO_LINE_ID"="PO_LINES_ALL"."PO_LINE_ID"(+))
    75. 31 - access("AP_INVOICE_DISTRIBUTIONS_ALL"."RCV_TRANSACTION_ID"="RTXNS"."TRANSACTION_ID"(+))
    76. 33 - access("RTXNS"."SHIPMENT_LINE_ID"="RSL"."SHIPMENT_LINE_ID"(+))
    77. 34 - access("RSL"."SHIPMENT_HEADER_ID"="RSH"."SHIPMENT_HEADER_ID"(+))
    78. 35 - filter(NVL("AP_INVOICE_DISTRIBUTIONS_ALL"."ORG_ID",NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
    79. ',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
    80. 36 - access("AP_INVOICE_DISTRIBUTIONS_ALL"."PREPAY_DISTRIBUTION_ID"="AP_INVOICE_DISTRIBUTIONS_ALL"."INVOICE
    81. _DISTRIBUTION_ID")
    82. 37 - filter(NVL("AP_INVOICES_ALL"."ORG_ID",NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
    83. ',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
    84. 38 - access("AP_INVOICES_ALL"."INVOICE_ID"="AP_INVOICE_DISTRIBUTIONS_ALL"."INVOICE_ID")
    85. 39 - filter(NVL("PO_VENDOR_SITES_ALL"."ORG_ID",NVL(TO_NUMBER(DECODE(SUBSTRB(:B1,1,1),'
    86. ',NULL,SUBSTRB(:B2,1,10))),(-99)))=NVL(TO_NUMBER(DECODE(SUBSTRB(:B3,1,1),' ',NULL,SUBSTRB(:B4,1,10))),(-99)))
    87. 40 - access("AP_INVOICES_ALL"."VENDOR_SITE_ID"="PO_VENDOR_SITES_ALL"."VENDOR_SITE_ID")
    88. 41 - access("AP_INVOICES_ALL"."VENDOR_ID"="PV"."VENDOR_ID")

    水平高的读者或许有疑问,执行计划 Id=19 是 INDEX FULL SCAN,然后再回表过滤,这里也有性能问题,全表扫描效率应该也比 INDEX FULL SCAN 再回表效率高!是的,我们也发现了这个地方有性能问题,但是既然 SQL 都执行到 1 秒了,也就没继续优化了,千万别得了优化强迫症。

    最后,我们再次强调,如果发生了连接列谓词推入,一定要检查执行计划中是否走了谓词被推入的表的连接列索引。