【问题标题】:Converting Oracle Dates转换 Oracle 日期
【发布时间】:2016-05-30 21:05:20
【问题描述】:

我在 Oracle 中处理日期最困难。由于日期问题,我发现自己花费数小时试图编译和运行查询。我不明白这有什么用。

我有一个文本字段 2015 年 4 月 23 日作为日期。我需要将此日期转换为两种形式; 2015 年 4 月和 2015 年 4 月 23 日。我已经想出了如何通过使用 TO_CHAR 和 TO_DATE 转换为这两种类型,但我的结果似乎不一致。

当我尝试转换为 MM/YYYY 格式时,出现此错误:ORA-01841: (full) year must be between -4713 and +9999, and not be 0

TO_DATE(hist.DATESTAMP, 'MM/YYYY')

如果我将其更改为TO_CHAR(hist.DATESTAMP, 'MM/YYYY'),它会按预期工作,但现在在给定日期之后显示项目的查询不再有效,因为它现在是字符串而不是日期。 WHERE DATESTAMP > '04/01/2015'

我有一个大的 csv 文件,我将数据导入到 Oracle 表中,需要提供关于它的报告。

我需要在日期上可以搜索结果,并且报告只显示月份和年份。通过 group by 组合了总计,以显示当月的总计。

在 SQL Server 中,我可以轻松完成这项任务。但是,Oracle 在处理日期时非常痛苦,我不知道如何使其正常工作。

我一直在阅读我能找到的所有日期来源,尤其是这里。在通过双重选择的真空中,我可以将字符串转换为日期,然后将该日期转换为我的月/年字符串。但是我的活动导入表在日期前后出现错误后继续抛出错误。

我可以使用一些真正的指导。谢谢

为好奇者准备的表格和脚本

CREATE TABLE MONTHLY_DATA
(
  ID                     NUMBER(10)             NOT NULL,
  DATESTAMP              DATE                   NOT NULL,
  PARTNER                VARCHAR2(256 BYTE)     NOT NULL,
  APPNAME                VARCHAR2(256 BYTE)     NOT NULL,
  TYPE_NAME              VARCHAR2(256 BYTE)     NOT NULL,
  PLATFORM               VARCHAR2(10 BYTE)      NOT NULL, -- RED or BLUE
  VOLUME                 NUMBER(10)             NOT NULL,
  LBSSUMMARY             VARCHAR2(5 BYTE)       NOT NULL, -- TRUE or FALSE
  CREATE_DTS             DATE,
  MODIFIED_DTS           DATE,
  LAST_MODIFIED_USER     VARCHAR2(40 BYTE)
)


CREATE TABLE MONTHLY_SUMMARY
(
  DATESTAMP           DATE                      NOT NULL,
  RED_VOLUME          NUMBER(10)                NOT NULL,
  BLUE_VOLUME         NUMBER(10)                NOT NULL,
  LBS_VOLUME          NUMBER(10)                NOT NULL,
  CREATE_DTS          DATE,
  MODIFIED_DTS        DATE,
  LAST_MODIFIED_USER  VARCHAR2(40 BYTE)
)

MONTHLY_DATA 中的数据每月大约有 50k 行。 目标是总结这些数据,并提供一个整体。

此外,NLS 设置为: select * from nls_session_parameters where parameter = 'NLS_DATE_FORMAT'; NLS_DATE_FORMAT: DD-MON-RR

按月分组的 MONTHLY_SUMMARY 的值。每个月应该只有 1 行;

03/2015 52 77 19

04/2015 34 76 21

这两个表都用于使用日期构建报告。使 where 子句变得简单;如上所述是什么 我在追WHERE DATESTAMP > '04/01/2015',但似乎我需要使用 TO_DATE 进行转换。

这是加载数据的脚本。

   INSERT INTO MONTHLY_SUMMARY (DATESTAMP, RED_VOLUME, BLUE_VOLUME, LBS_VOLUME)
   SELECT DISTINCT
          MONTHLY_DATA.DATESTAMP,
          CASE WHEN red.VOLUME IS NULL THEN 0 ELSE red.VOLUME END AS RED_VOLUME,
          CASE WHEN blue.VOLUME IS NULL THEN 0 ELSE blue.VOLUME END AS BLUE_VOLUME,
          CASE WHEN lbs.VOLUME IS NULL THEN 0 ELSE lbs.VOLUME END AS LBS_VOLUME
   FROM MONTHLY_DATA hist
   LEFT JOIN (SELECT DATESTAMP, SUM(hist.VOLUME) AS VOLUME
              FROM MONTHLY_DATA md
              WHERE PLATFORM = 'RED'
              GROUP BY DATESTAMP) red
          ON md.DATESTAMP = red.DATESTAMP
   LEFT JOIN (SELECT DATESTAMP, SUM(hist.VOLUME) AS VOLUME
              FROM MONTHLY_DATA md
              WHERE PLATFORM = 'BLUE'
              GROUP BY DATESTAMP) blue
          ON md.DATESTAMP = blue.DATESTAMP
   LEFT JOIN (SELECT DATESTAMP, SUM(MONTHLY_DATA.VOLUME) AS VOLUME
              FROM MONTHLY_DATA
              WHERE LBSSUMMARY = 'TRUE' 
              GROUP BY DATESTAMP) lbs
          ON MONTHLY_DATA.DATESTAMP = lbs.DATESTAMP
   WHERE MONTHLY_DATA.DATESTAMP 
     NOT IN (SELECT DISTINCT DATESTAMP FROM MONTHLY_SUMMARY)
   GROUP BY 
         MONTHLY_DATA.DATESTAMP,
         red.VOLUME,
         blue.VOLUME,
         lbs.VOLUME
   ORDER BY 1;

【问题讨论】:

  • 您是否从所有日期都有效的干净表格开始?您希望避免将导入的日期作为字符串存储到数据库表中的情况。如果其中一些字符串无效,则在您尝试在查询期间动态转换它们时它将失败。在进一步之前,最好完成将所有日期存储在表中 DATE 类型列中的数据清理练习。如果您已经做到了这一步,请告诉我们?
  • 是的,该列已经过验证并且包含格式正确的日期。
  • 我希望将日期保存为 DATE 数据类型。但是当我把它变成一个字符串时,转换似乎只部分起作用。
  • 问题是您将日期存储为文本。你在这里讨论的每一个问题都源于此。此外,您的数据顺序也不好。 YYYY-MM-DD 或 YYYYMMDD 会更好,但仍不如日期。
  • 更新为包含表格和脚本。

标签: oracle date


【解决方案1】:

将字符串/varchar 值转换为日期

现在,您需要正确转换日期。

TO_DATE(hist.DATESTAMP, 'MM/YYYY')

上面是错误的,因为您的日期存储为'dd/mm/yyy',因此使用格式掩码mm/yyyy4/23/2015 之类的varchar 值转换为正确的日期将不起作用。您需要提供与您的输入相匹配的格式掩码。你必须使用TO_DATE(hist.DATESTAMP, 'dd/MM/YYYY')

如果我将其更改为 TO_CHAR(hist.DATESTAMP, 'MM/YYYY') 它会按预期工作

这不会“按预期工作”,因为to_char()DATE 值转换为字符串/varchar 值。但是您将 varchar 值传递给函数,该函数首先将 隐式 转换为日期,以使函数调用正常工作。此转换取决于运行该语句的客户端 的区域设置 (NLS) 设置。在我的计算机上,您的查询将失败,因为我的默认日期格式不同。

如果您想以不同的格式显示您的字符串,您首先必须将其正确转换为日期,然后将该日期转换回字符串:

to_char(TO_DATE(hist.DATESTAMP, 'dd/MM/YYYY'), 'mm/yyyy')

如果您将日期正确存储为 DATE to_char((hist.DATESTAMP, 'mm/yyyy')` 就足够了。

如果您想对字符串值使用“日期”条件,您必须首先将字符串值转换为正确的日期:

WHERE to_date(DATESTAMP, 'mm/dd/yyyy') > to_date('04/01/2015', 'dd/mm/yyyy')

现在这是比较日期和日期。 WHERE DATESTAMP > '04/01/2015' 正在将字符串与字符串进行比较。由于mm/dd/yyyy 的格式,这将无法正常工作。如果你真的想存储和比较字符串,你至少应该以可以正确比较的格式存储它们,例如yyyy-mm-dd

对于日期常量,我更喜欢使用简短且明确的 ANSI 日期文字:

WHERE to_date(DATESTAMP, 'mm/dd/yyyy') > DATE '2015-04-01'

比较 Oracle 中的日期值

如果该列确实是date(正如您的 cmets 现在似乎指出的那样),那么您需要做的就是:

WHERE DATESTAMP > DATE '2015-04-01'

或者如果您只想检查特定的月份和年份组合:

where extract(year from datestamp) = 2015
  and extract(month from datestamp) = 4

where (extract(year from datestamp), extract(month from datestamp)) = ((2015,4))

另外,这确实可以使用字符串比较来完成:

where to_char(datestamp, 'yyyy-mm') = '2015-04'

或者,如果您更喜欢可以利用 datestamp 列上的索引的查询

where datestamp >= date '2015-04-01'  
  and datestamp < date '2015-05-01'  

关于 Oracle 中日期的唯一真正令人讨厌的是它们总是包含时间部分。在我看来,甲骨文对修复这个问题没有兴趣,例如通过引入新的DATE2 类型或类似的东西。

【讨论】:

  • 为什么有时间是问题?
  • @igr:因为如果你不需要它,你仍然需要处理它。例如current_date 还返回日期时间。像where some_date_column = current_date 这样的东西通常不会返回任何东西,因为列中的时间部分和 current_date
  • 对于最后提到的问题,使用 TRUNC(datecolumn) 将时间设置为午夜。您也可以将该功能用于日历单位,例如月。
  • @a_horse_with_no_name 如果您不使用时间,那应该不是问题(在所有尺寸上,始终如一。因此,如果您仅存储日期,仅读取日期,仅比较日期应该没问题. 唯一要记住的是为显示和隐式转换使用适当的正确默认格式,并使用显式格式作为例外。我发现时间完全独立于日期的有问题的方法...
【解决方案2】:

经过无数次的轮转和尝试,我相信我找到了我误解的症结所在。 TO_DATE 和 TO_CHAR 方法需要 NLS 格式的日期。如果您尝试使用不同的格式,您可能会面临出错的风险。

一旦我将初始格式转换为 DD-MON-RR,所有部分都开始正常工作。我还能够使用 where 子句执行日期过滤器,这在以前会导致问题。

感谢您的所有帮助和支持。我想我终于走对了。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-01-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多