【问题标题】:JOIN Breaking WHERE Clause SubqueryJOIN 打破 WHERE 子句子查询
【发布时间】:2012-06-11 20:02:44
【问题描述】:

我的数据库中有 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
);

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

我不知道发生了什么。子查询本身运行良好,所有值都采用正确的日期格式。

有人知道发生了什么吗?

【问题讨论】:

  • 您能否添加一些示例 CHILD.START_DATE 值?
  • 所有日期都类似于“10/07/2011”、“03/07/2012”等。但请参阅上面的更新。我想我已经发现了这个问题。

标签: sql oracle date join subquery


【解决方案1】:

问题几乎可以肯定是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

Jonathan Gennick 有一篇很棒的文章 Subquery Madness 更详细地介绍了这个问题。读起来也很有趣。

【讨论】:

  • 我不知道 Oracle 可以按不同的顺序做事。我的子查询实际上有一个额外的限制,以确保日期有效(请参阅问题更新)。所以我猜它在削减无效日期之前执行日期限制。即将尝试您的建议。
  • +1 非常感谢您的帮助。我创建了这个函数,它运行良好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-10-14
  • 1970-01-01
  • 2020-05-05
  • 2013-07-06
  • 1970-01-01
  • 2017-05-26
  • 1970-01-01
相关资源
最近更新 更多