2012-06-07 153 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非常感谢您的帮助。我创建了该功能,并且完美运行。 –