Oracle where條件中substr(字段,1,?)='XXX...'建議改寫為like


 

Oracle where條件中substr(字段,1,?)='XXX...'建議改寫為like

前言

類似where中substr(name,1,2)='AB'會導致name上的普通索引一定無法使用。

 

案例

原SQL語句如下(經過處理):

INSERT INTO XXXX_XXXXXXXXX_TMP
  SELECT XXXXXXX_ID1,
         XXXX_ID2,
         XXX_ID3,
         XXXX_XXXXXXXXX_SEQ_NO.NEXTVAL,
         XXXX_TYPE1,
         XXXX_TYPE2,
         XXXX_NAME,
         FILE_SIZE,
         FILE_FLAG,
         SENDER,
         SUBSTR(RECEIVE, 1, 7) || '28' RECEIVE,
         XX_TIME,
         IN_POSITION,
         NULL,
         NULL,
         0,
         'Failed_HZ',
         SAVE_POSITION,
         KEYWORDS,
         FUNCTION_CODE
    FROM XXXX_XXXXXXXXX X
   WHERE X.SEQ_NO IN
         (SELECT MAX(T.SEQ_NO) SEQ_NO
            FROM XXXX_XXXXXXXXX T
           WHERE T.XX_TIME >= SYSDATE - 5
             AND T.XXXX_TYPE1 = '處理'
             AND T.XXXX_TYPE2 = '處理處理'
             AND SUBSTR(T.XXXX_NAME, 1, 1) = 'F'
           GROUP BY UPPER(SUBSTR(T.XXXX_NAME,
                                 INSTR(T.XXXX_NAME, 'CN'),
                                 INSTR(T.XXXX_NAME, '_', -1) - 8) || '.xml')
          HAVING COUNT(*) = '2');

 該語句跑完會導致XXXX_XXXXXXXXX 至少全表掃描1次以上。

下邊是select部分執行完的執行計划信息:

Plan hash value: 733887443

------------------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                            | Name                  | Starts | E-Rows | A-Rows |   A-Time   | Buffers | Reads  |  OMem |  1Mem | Used-Mem |
------------------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                     |                       |      1 |        |      0 |00:00:56.11 |    1260K|   1260K|       |       |          |
|   1 |  SEQUENCE                            | XXXX_XXXXXXXXX_SEQ_NO |      1 |        |      0 |00:00:56.11 |    1260K|   1260K|       |       |          |
|   2 |   NESTED LOOPS                       |                       |      1 |      3 |      0 |00:00:56.11 |    1260K|   1260K|       |       |          |
|   3 |    NESTED LOOPS                      |                       |      1 |      3 |      0 |00:00:56.11 |    1260K|   1260K|       |       |          |
|   4 |     VIEW                             | VW_NSO_1              |      1 |      3 |      0 |00:00:56.11 |    1260K|   1260K|       |       |          |
|*  5 |      FILTER                          |                       |      1 |        |      0 |00:00:56.11 |    1260K|   1260K|       |       |          |
|   6 |       HASH GROUP BY                  |                       |      1 |      3 |      4 |00:00:56.11 |    1260K|   1260K|   846K|   846K|  727K (0)|
|   7 |        PARTITION RANGE ITERATOR      |                       |      1 |    227 |      4 |00:00:56.11 |    1260K|   1260K|       |       |          |
|*  8 |         TABLE ACCESS FULL            | XXXX_XXXXXXXXX        |      1 |    227 |      4 |00:00:56.11 |    1260K|   1260K|       |       |          |
|*  9 |     INDEX UNIQUE SCAN                | PK_XXXX_XXXXXXXXX     |      0 |      1 |      0 |00:00:00.01 |       0 |      0 |       |       |          |
|  10 |    TABLE ACCESS BY GLOBAL INDEX ROWID| XXXX_XXXXXXXXX        |      0 |      1 |      0 |00:00:00.01 |       0 |      0 |       |       |          |
------------------------------------------------------------------------------------------------------------------------------------------------------------

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

   5 - filter(COUNT(*)=2)
   8 - filter(("T"."XXXX_TYPE2"='處理處理' AND "T"."XXXX_TYPE1"='處理' AND SUBSTR("T"."XXXX_NAME",1,1)='F' AND "T"."XX_TIME">=SYSDATE@!-5))
   9 - access("X"."SEQ_NO"="SEQ_NO")

 語句執行56s左右,根據歷史執行情況,執行時間在1min~2min不等。

其中,表高達62G,每次執行全表都是物理讀(直接路徑讀)。

由於分區字段問題,除了每個月前5天,均只會訪問1個分區,產生的物理讀為1260000*8/1024/1024=9.7G。

而每個月前5天,由於條件為T.XX_TIME >= SYSDATE - 5,因此會導致訪問2個分區,那么物理讀只會更大。

另外,T.XXXX_NAME是有索引的,且第一個字符是'F'的可選擇率非常小。

原語句寫法為SUBSTR(T.XXXX_NAME, 1, 1) = 'F'會導致該字段上的索引無法使用。

因此建議該條件改為:AND T.FILE_NAME LIKE 'F%':

INSERT INTO FILE_TRANSFERS_TMP
  SELECT CUSTOMS_ID,
         PORT_ID,
         DOCK_ID,
         FILE_TRANSFERS_SEQ_NO.NEXTVAL,
         BUSI_TYPE,
         FILE_TYPE,
         FILE_NAME,
         FILE_SIZE,
         FILE_FLAG,
         SENDER,
         SUBSTR(RECEIVE, 1, 7) || '28' RECEIVE,
         IN_TIME,
         IN_POSITION,
         NULL,
         NULL,
         0,
         'Failed_HZ',
         SAVE_POSITION,
         KEYWORDS,
         FUNCTION_CODE
    FROM FILE_TRANSFERS X
   WHERE X.SEQ_NO IN
         (SELECT MAX(T.SEQ_NO) SEQ_NO
            FROM FILE_TRANSFERS T
           WHERE T.IN_TIME >= SYSDATE - 5
             AND T.BUSI_TYPE = '平文'
             AND T.FILE_TYPE = '回執處理'
             AND T.FILE_NAME LIKE 'F%'
           GROUP BY UPPER(SUBSTR(T.FILE_NAME,
                                 INSTR(T.FILE_NAME, 'CN'),
                                 INSTR(T.FILE_NAME, '_', -1) - 8) || '.xml')
          HAVING COUNT(*) = '2');

 

新的語句select部分秒出,執行計划消耗信息如下:

Plan hash value: 4213418537

-------------------------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                                | Name                  | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
-------------------------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                         |                       |      1 |        |      0 |00:00:00.02 |    1048 |       |       |          |
|   1 |  SEQUENCE                                | XXXX_XXXXXXXXX_SEQ_NO |      1 |        |      0 |00:00:00.02 |    1048 |       |       |          |
|   2 |   NESTED LOOPS                           |                       |      1 |      1 |      0 |00:00:00.02 |    1048 |       |       |          |
|   3 |    NESTED LOOPS                          |                       |      1 |      1 |      0 |00:00:00.02 |    1048 |       |       |          |
|   4 |     VIEW                                 | VW_NSO_1              |      1 |      1 |      0 |00:00:00.02 |    1048 |       |       |          |
|*  5 |      FILTER                              |                       |      1 |        |      0 |00:00:00.02 |    1048 |       |       |          |
|   6 |       HASH GROUP BY                      |                       |      1 |      1 |      4 |00:00:00.02 |    1048 |   846K|   846K|  743K (0)|
|*  7 |        TABLE ACCESS BY GLOBAL INDEX ROWID| XXXX_XXXXXXXXX        |      1 |     18 |      4 |00:00:00.02 |    1048 |       |       |          |
|*  8 |         INDEX RANGE SCAN                 | IDX_XXXX_NAME         |      1 |   2807 |   7781 |00:00:00.01 |     112 |       |       |          |
|*  9 |     INDEX UNIQUE SCAN                    | PK_XXXX_XXXXXXXXX     |      0 |      1 |      0 |00:00:00.01 |       0 |       |       |          |
|  10 |    TABLE ACCESS BY GLOBAL INDEX ROWID    | XXXX_XXXXXXXXX        |      0 |      1 |      0 |00:00:00.01 |       0 |       |       |          |
-------------------------------------------------------------------------------------------------------------------------------------------------------

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

   5 - filter(COUNT(*)=2)
   7 - filter(("T"."XXXX_TYPE"='處理處理' AND "T"."XXXX_TYPE"='處理' AND "T"."XX_TIME">=SYSDATE@!-5))
   8 - access("T"."XXXX_NAME" LIKE 'F%')
       filter("T"."XXXX_NAME" LIKE 'F%')
   9 - access("X"."SEQ_NO"="SEQ_NO")

沒有了物理讀,邏輯讀也僅為8.2M。

 

建議

一般情況下,substr(字段,1,?)='XXX...'均寫為“字段 like 'XXX...%'”,為可能使用索引創造條件。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM