【问题标题】:Oracle TO_DATE: Works in SELECT; Not a Valid Month in WHERE ClauseOracle TO_DATE:在 SELECT 中工作; WHERE 子句中的月份无效
【发布时间】:2019-07-16 19:26:36
【问题描述】:

我编写了一个包含 2 个子查询的查询。子查询格式化生成的字段名称和数据类型,因此它们是相同的。主要查询是直接的 UNION。

两个子查询都从托管的 Oracle 服务器中检索数据;我们无法控制数据库结构。两个子查询之一使用字符串数据类型来包含 DateTime。我在 SELECT 中使用以下代码将值转换为 DateTime 并添加 2 小时以调整时区:

TO_DATE(TCF.TRANS_DATE, 'MM-DD-YYYY HH24:MI:SS') + 2/24 AS TRANS_DT,

这会产生正确的值。

问题在于:WHERE 子句中的相同 TO_DATE 会产生错误 ORA-01843: not a valid month。这是完整的 WHERE 子句:

WHERE TO_DATE(TCF.TRANS_DATE, 'MM-DD-YYYY HH24:MI:SS') + 2/24 >= {?START_DT}

显而易见的问题是,为什么上面的代码在 SELECT 中有效,但在 WHERE 子句中抛出了这个错误?

字段中的值按照 TO_DATE 语句中指定的格式设置。如果不是我希望 SELECT 语句产生错误,但是当 WHERE 子句被删除时没有错误。

我尝试将 TRUNC() 添加到 WHERE 子句,因为参数 {?START_DT} 是 Date 数据类型。我还硬编码了一个有效日期来代替使用该参数。这些都对错误没有任何影响。

完成第二个子查询的 WHERE 子句是我完成这个项目并继续前进的全部内容。我正在使用具有成熟可靠连接的 Crystal Reports 来访问数据库。任何想法或建议表示赞赏。

为了您的信息,这里是整个查询:

SELECT DISTINCT TCF.TRIPCARD_UNIT_NO AS UNIT_NO,
TO_DATE(TCF.TRANS_DATE, 'MM-DD-YYYY HH24:MI:SS') AS TRANS_DT2,
TCF.ODOMETER AS METER
FROM MFIVE.VIEW_TRIPCARD_FAILED_TRANS TCF
WHERE TO_DATE(TCF.TRANS_DATE, 'MM-DD-YYYY HH24:MI:SS') >= TO_DATE('02-01-2019 00:00:00', 'MM-DD-YYYY HH24:MI:SS')

出于沮丧,我从 TRANS_DT 字符串中解析了月、日和年的值。没有超出适当范围的值;具体来说,所有月份的值都在 1 到 12 之间,正如预期的那样。

我在 WHERE 子句中针对上述连接部分创建了一个新的 TO_DATE 语句。检查并再次检查格式...我得到了同样的错误。

在我看来,WHERE 子句中不允许使用 TO_DATE。有没有其他人听说过这个限制?有什么方法可以在 SQL 中创建 DateTime 变量并将其传递给 WHERE 子句?

更新 #3:

省略了 WHERE 子句,我将查询的其余部分用作子查询,并将 EXTRACT MONTH FROM TRANS_DT 用于 SELECT 语句。这个子查询运行良好,生成了从 1 到 12 的整数。这再次证实不涉及格式错误的日期。

扩展子查询概念 我尝试使用以下语句应用 WHERE 子句:WHERE TRUNC(TCF2.TRANS_DT) >= TO_DATE('02/01/2019', 'MM/DD/YYYY') 这产生了同样的错误!我闻到了虫子的味道,但如果我知道它是什么,那该死的。

【问题讨论】:

  • TCF.TRANS_DATE 列的数据类型是什么?
  • @Littlefoot:TCF.TRANS_DATE 是我提到的字符串数据类型。
  • 你从SELECT MAX(TO_DATE(TCF.TRANS_DATE, 'MM-DD-YYYY HH24:MI:SS')) FROM <whatever the table name is> TCF得到什么?如果出现错误(我很确定它会这样),那么您的数据格式不正确,并且没有where,它的工作原理是 Gordon 给出的原因之一。 (这是将日期存储为字符串不好的主要原因之一,但是从您的“无法控制”评论中我猜您已经知道了!)
  • @Alex:抱歉,您的查询没有返回错误。它返回今天的日期,这是查询的正确结果。不过,就您而言,如果我的数据格式不正确……为什么 SELECT 子句在没有 WHERE 子句的情况下不抛出错误? (是的,这就是弗兰肯斯坦的桌子。设计极差。)
  • @spacetanker - 如果它格式错误,那么 Gordon 说了什么;但似乎不是。您确定{?START_DT} 并且您尝试的固定值实际上是日期,而不是依赖于隐式转换的字符串吗?其他地方还有其他类似的“日期”比较吗?将其添加为过滤器可能会更改执行计划,并且现在正在以不同的顺序对其进行评估。检查两个执行计划,看看有什么变化。

标签: sql oracle oracle11g crystal-reports


【解决方案1】:

我能想到两个可能的原因:

首先,您正在运行查询,但只查看几行结果,而不是整个结果集。您看到的行的日期没有问题。如果你滚动到最后,那么你会看到问题。

第二个原因是select 表达式在all 过滤之后运行(至少在某些情况下)。不良值可能会被其他过滤条件过滤掉。当表达式移动到where 子句时,所有数据都可能通过此条件。

【讨论】:

  • 当我在没有WHERE子句的情况下运行查询时,基本上没有约束,所有数据都被SELECT子句中的TO_DATE语句成功处理。我看了你的评论后做了更彻底的检查,没有看到任何明显的问题。当然,不用说没有错误。关于操作顺序的好点。我没有考虑过这一点,但唯一的过滤是前面提到的由单个过滤条件组成的 WHERE 子句。没有它,它就会运行。有了它,它就会出错。
【解决方案2】:

等号或匹配时请检查数据类型:

WITH S_DT AS 
(
SELECT TO_DATE(TO_CHAR(SYSDATE,'MM-DD-YYYY HH24:MI:SS'), 'MM-DD-YYYY HH24:MI:SS') START_DT FROM DUAL
),
TAB AS
(
SELECT TO_DATE(TO_CHAR(SYSDATE,'MM-DD-YYYY HH24:MI:SS'), 'MM-DD-YYYY HH24:MI:SS') + 2/24 AS TRANS_DT FROM DUAL
UNION ALL
SELECT TO_DATE(TO_CHAR(SYSDATE,'MM-DD-YYYY HH24:MI:SS'), 'MM-DD-YYYY HH24:MI:SS') - 2/24 FROM DUAL
UNION ALL
SELECT TO_DATE(TO_CHAR(SYSDATE,'MM-DD-YYYY'), 'MM-DD-YYYY') + 2/24 FROM DUAL
)
SELECT * FROM TAB,S_DT WHERE TAB.TRANS_DT >= S_DT.START_DT;

输出:

TRANS_DT  START_DT 
--------- ---------
2/22/2019 2:09:39 PM    2/22/2019 12:09:39 PM

或者,您也可以试试这个NEW_TIME 函数来更改time zone

SELECT SYSDATE, 
       NEW_TIME(SYSDATE, 'GMT', 'EST') 
FROM dual;

输出:

SYSDATE   NEW_TIME(SYSDATE,'GMT','EST')
--------- -----------------------------
2/22/2019 12:10:10 PM   2/22/2019 7:10:10 AM

【讨论】:

  • 我检查了数据类型。我知道 TRANS_DT 是一个字符串,格式为“MM-DD-YYYY HH24:MI:SS”。 SELECT 子句中的 TO_DATE 没有例外。除非我遗漏了您试图在示例中说明的内容。也就是说,我从我的代码中删除了时区更正,它仍然在没有 WHERE 子句和包含错误时运行。时区功能的积分;我以前没见过。
猜你喜欢
  • 1970-01-01
  • 2013-06-19
  • 2011-07-07
  • 2019-02-25
  • 2013-06-07
  • 1970-01-01
  • 2016-07-07
  • 2017-11-09
  • 1970-01-01
相关资源
最近更新 更多