2012-06-07 99 views
3

我在我的數據庫中有3個表。JOIN Breaking WHERE子句子查詢

  • PARENT_A有一個「ID」主鍵列。
  • PARENT_B具有 「ID」 主鍵列。
  • CHILD有 「PARENT_A_ID」 和 「PARENT_B_ID」 外鍵列。它還有一個「START_DATE」列,這是一個VARCHAR(遺憾的是,我不能改變這個)。

現在,我有以下查詢。

更新 - 我更新了子查詢有點像我的實際代碼。對c2有一個額外的限制,保證START_DATE是有效的日期。

SELECT * 
FROM PARENT_B pb 
LEFT OUTER JOIN CHILD c1 ON c1.PARENT_B_ID = pb.ID 
WHERE pb.ID IN 
(
    SELECT c2.PARENT_B_ID 
    FROM PARENT_A pa 
    LEFT OUTER JOIN CHILD c2 ON c2.PARENT_A_ID = pa.ID 
    WHERE TO_DATE(c2.START_DATE, 'mm/dd/yyyy') BETWEEN 
      ADD_MONTHS(TRUNC(SYSDATE, 'MONTH'), -12) AND 
      (TRUNC(SYSDATE, 'MONTH') - 1) 
    AND c2.HAS_VALID_DATE = 1 
); 

該查詢失敗。我得到一個ORA-01843: not a valid month異常。但是,如果我刪除第一個連接(查詢的第3行),查詢運行良好。

我不知道發生了什麼事。子查詢本身運行良好,所有值都是正確的日期格式。

沒有人有任何想法是怎麼回事?

+0

您可以加入一些示例CHILD.START_DATE值? – Andomar

+0

所有的日期都像'10/07/2011','03/07/2012'等,但請參閱上述更新。我想我已經發現了這個問題。 –

回答

5

問題幾乎可以肯定的是,CHILD中至少有一些行,start_date字符串無法使用mm/dd/yyyy格式轉換爲日期。由於Oracle可以按任何順序評估謂詞,我希望你碰巧這個計劃沒有參加執行to_date和之前的計劃是消除那些壞行你碰巧與加盟消除行之前被評估to_date越來越越來越由於其他原因。當然,明天,優化器可以選擇不同的計劃,並且查詢可能與加入一起工作,如果沒有它,則會失敗。或者您可能會發現查詢成功返回第一行,然後在嘗試讀取行時引發ORA-01843錯誤。

除非您可以修復數據,否則最好的選擇通常是編寫自己的字符串日期轉換例程,如果該字符串不能轉換爲日期,則返回NULL。像

CREATE OR REPLACE FUNCTION my_to_date(p_dt_str IN VARCHAR2, p_mask IN VARCHAR2) 
    RETURN DATE 
    DETERMINISTIC 
IS 
    l_dt DATE; 
BEGIN 
    l_dt := to_date(p_dt_str, p_mask); 
    RETURN l_dt; 
EXCEPTION 
    WHEN others THEN 
    RETURN NULL; 
END; 
東西

您的查詢就會然後說

WHERE my_to_date(c2.start_date, 'mm/dd/yyyy') between ... 

您也可以使用這個功能來查找行,其中的start_date無效

SELECT * 
    FROM child 
WHERE start_date is not null 
    AND my_to_date(start_date, 'mm/dd/yyyy') is null 

喬納森Gennick有一個偉大的文章Subquery Madness這更詳細地解決了這個問題。這也是一個有趣的閱讀。

+0

我不知道Oracle可以按不同的順序做事。我的子查詢實際上有一個額外的限制,確保日期有效(請參閱問題更新)。所以我猜測它正在執行日期限制,然後再削減無效的日期限制。關於嘗試你的建議。 –

+0

+1非常感謝您的幫助。我創建了該功能,並且完美運行。 –