JOIN打破WHERE子句子查询

Sno*_*irl 3 sql oracle join date subquery

我的数据库中有3个表.

  • 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
);
Run Code Online (Sandbox Code Playgroud)

此查询失败.我得到一个ORA-01843: not a valid month例外.但是,如果我删除第一个连接(查询的第3行),查询运行正常.

我不知道是怎么回事.子查询本身运行正常,所有值都采用正确的日期格式.

有谁知道发生了什么事?

Jus*_*ave 5

几乎可以肯定的是,至少有一些行不能使用格式CHILDstart_date字符串转换为日期mm/dd/yyyy.由于Oracle可以按任何顺序评估谓词,我希望您在没有连接的情况下获得的计划是在执行之前消除那些坏行,to_date并且您正在通过连接获得的计划正在评估to_date之前为其他行消除行原因.当然,明天,优化器可以选择不同的计划,查询可能与加入一起工作,如果没有它,则失败.或者您可能会发现查询成功返回第一行,然后在尝试获取行时抛出ORA-01843错误.

除非您可以修复数据,否则最好的选择通常是编写自己的字符串到日期转换例程,NULL如果字符串无法转换为日期,则返回a .就像是

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;
Run Code Online (Sandbox Code Playgroud)

然后你的查询会说

WHERE my_to_date( c2.start_date, 'mm/dd/yyyy' ) between ...
Run Code Online (Sandbox Code Playgroud)

您还可以使用此函数查找start_date无效的行

SELECT *
  FROM child
 WHERE start_date is not null
   AND my_to_date( start_date, 'mm/dd/yyyy' ) is null
Run Code Online (Sandbox Code Playgroud)

Jonathan Gennick有一篇很棒的文章Subquery Madness,详细介绍了这个问题.这也是一个有趣的阅读.