2017-02-09 32 views
1

我們有一個表格,其日期以YYYYMMDD格式存儲爲VARCHAR2(它是我們無法控制的遺留數據表格,格式爲:)。我們在表格上只有SELECT特權(不可能寫程序/功能)。Oracle - 嵌入式視圖 - 外部WHERE條件在線應用

需要從表中選擇日期> sysdate的所有行。

我們對有效格式和附加檢查應用正則表達式檢查,以確保該日在給定的月份內有效。所有這些都很棒!我們的內聯視圖選擇確保只選擇具有有效日期字符串的記錄。

但是,當我們申請條件檢查> SYSDATE作爲外部條款 - 我們得到即使內嵌視圖選擇確保沒有這樣的記錄挑選特定的月份錯誤無效的日期。

看起來好像查詢執行是在應用內聯視圖條件之前從外部子句應用條件。欣賞對此行爲的任何評論;而且,我們如何確保只有在符合條件的情況下才能應用來自外部的條件?

數據,並用查詢:

CREATE TABLE TEST_DATA_TABLE 
(
    DATESTRING VARCHAR2(20 BYTE) 
); 

插入3行與值:

19960322 --Valid Date in past 
19831131 --Invalid Date 11/31 
20180224 --Valid Date > SYSDATE 

有效數據的選擇: (where子句1保證格式和第2條保證了一個月有效日期):

SELECT datestring AS i_dob 
    FROM test_data_table 
WHERE  REGEXP_LIKE (TRIM (datestring), 
         '(19|20)\d\d(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])') 
     AND TRIM (datestring) <= 
       TO_CHAR (
       LAST_DAY (
        TO_DATE (SUBSTR (TRIM (datestring), 1, 6) || '01', 
          'YYYYMMDD')), 
       'YYYYMMDD') 

上面的查詢工作正常,返回有效的行與VAL ID日期字符串,

要選擇具有日期字符串> SYSDATE記錄,上述數據被用於內聯和我們如下應用條件> SYSDATE。

SELECT i_dob 
    FROM (
    SELECT datestring AS i_dob 
     FROM test_data_table 
    WHERE  REGEXP_LIKE (TRIM (datestring), 
          '(19|20)\d\d(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])') 
      AND TRIM (datestring) <= 
        TO_CHAR (
        LAST_DAY (
         TO_DATE (SUBSTR (TRIM (datestring), 1, 6) || '01', 
           'YYYYMMDD')), 
        'YYYYMMDD') 
) X 
WHERE TO_DATE (X.I_DOB, 'YYYYMMDD') > SYSDATE 

它開始拋出錯誤:ORA-01839: date not valid for month specified

看起來像之前應用條件的所有內嵌視圖條件進行檢查。

+0

好的,這是一個有趣的問題 - 感謝發佈它!目前爲止的兩個觀察結果(我會繼續尋找......)** FIRST **:您可以檢查'X.I_DOB> TO_CHAR(SYSDATE,'YYYYMMDD')',而不是'WHERE'子句。當然,這不會回答你的問題(也許你需要其他計算的日期,你真的需要將它轉換爲日期);只是指出這個特定的查詢有一個解決方法。 – mathguy

+0

** SECOND **展望提示;我認爲'/ * + ORDERED_PREDICATES * /'應該可以工作,事實上,如果將兩個WHERE子句合併爲一個子查詢(無子查詢),但使用子查詢 - 外部查詢結構則不行。如果我能夠找出正確的提示(我認爲'NO_QUERY_TRANSFORMATION'應該可以工作,但它沒有或者我沒有正確使用它),我會回寫。 – mathguy

回答

0

您可以通過在外部查詢之前通過向其添加額外的rownum列來操作內聯視圖來解決此問題(rownum列的存在意味着Oracle需要計算該列適用於之前應用於的子查詢它可以在其上進一步篩選

所以,您的查詢就會變成:

SELECT i_dob 
    FROM (
    SELECT datestring AS i_dob, 
      rownum rn 
     FROM test_data_table 
    WHERE  REGEXP_LIKE (TRIM (datestring), 
          '(19|20)\d\d(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])') 
      AND TRIM (datestring) <= 
        TO_CHAR (
        LAST_DAY (
         TO_DATE (SUBSTR (TRIM (datestring), 1, 6) || '01', 
           'YYYYMMDD')), 
        'YYYYMMDD') 
) X 
WHERE TO_DATE (X.I_DOB, 'YYYYMMDD') > SYSDATE; 

我會添加評論到你的查詢說明此列的存在,否則在未來的某個開發者可能會認爲「誒?這什麼都不做;我會將它刪除!「。

(同樣值得注意的是在12.2中,他們增加了/修改了功能以允許easier data validation,這會在你達到12.2時簡化你的查詢!)

+0

這很有效,感謝您的解決方法。 – Soorjith

+0

這對錶演有什麼影響?儘管解決方法在功能上起作用,但它在黑客攻擊之上... :( – BobC

+0

同意性能命中,但看起來像沒有其他選項..查詢優化器可能會根據性能進行排序,但我們強制排序 – Soorjith