在查询中转换日历上的一系列日期失败

时间:2019-05-15 11:04:08

标签: sql oracle

我运行此查询将日历从GREGORIAN转换为PERSIAN日期模式。 可以,但是在波斯模式下的某一天,该月有31天,因此无法将其转换为波斯模式,原因是“ ORA-01839”。

发生这种情况是因为我知道波斯模型的日期是完全正确的。

在此示例中,波斯日历的第二个月为31天。但是当我在(19,20,21)之间更改日期时,Oracle会显示“ ORA-01839”错误

-成功执行

SELECT TO_DATE(TO_CHAR(CAST(SUBSTR('18-MAY-18', 1, 10) AS DATE),
                       'YYYYMMDD',
                       'NLS_CALENDAR=PERSIAN'),
               'YYYYMMDD') date_inventory
  FROM DUAL W;

-引发错误ORA-01839

SELECT TO_DATE(TO_CHAR(CAST(SUBSTR('19-MAY-18', 1, 10) AS DATE),
                       'YYYYMMDD',
                       'NLS_CALENDAR=PERSIAN'),
               'YYYYMMDD') date_inventory
  FROM DUAL W;

在下个月,我遇到了以上错误。请注意,这个问题仅在2018年和其他年份有所不同。

MAY (19,20,21)
JUL (22)
AUG(21)
SEP(22)

我使用下面的查询来计算库存过程,但无法更改

create table TREND_WAREHOUSE
(
  id           NUMBER(30) not null,
  reg_date     NUMBER(30),
  org_id       NUMBER(30),
  goods_id     NUMBER(30),
  qty          NUMBER(30),
  reg_datetime TIMESTAMP(6)
)
insert into TREND_WAREHOUSE (id, reg_date, org_id, goods_id, qty, reg_datetime)
values (1, 13970631, 10, 1, 100, to_timestamp('22-09-2018 00:00:00.000000', 'dd-mm-yyyy hh24:mi:ss.ff'));
insert into TREND_WAREHOUSE (id, reg_date, org_id, goods_id, qty, reg_datetime)
values (2, 13930609, 10, 1, 20, to_timestamp('31-08-2014 14:03:44.689000', 'dd-mm-yyyy hh24:mi:ss.ff'));
insert into TREND_WAREHOUSE (id, reg_date, org_id, goods_id, qty, reg_datetime)
values (3, 13940902, 10, 1, -20, to_timestamp('23-11-2015 00:00:00.000000', 'dd-mm-yyyy hh24:mi:ss.ff'));
insert into TREND_WAREHOUSE (id, reg_date, org_id, goods_id, qty, reg_datetime)
values (4, 13940902, 10, 1, 100, to_timestamp('23-11-2015 00:00:00.000000', 'dd-mm-yyyy hh24:mi:ss.ff'));
insert into TREND_WAREHOUSE (id, reg_date, org_id, goods_id, qty, reg_datetime)
values (5, 13940902, 10, 1, 300, to_timestamp('23-11-2015 00:00:00.000000', 'dd-mm-yyyy hh24:mi:ss.ff'));
commit;


    WITH date_inventory_range AS
     (SELECT TRUNC(MIN(date_inventory), 'MONTH') AS first_date_inventory,
             ADD_MONTHS(TRUNC(MAX(date_inventory), 'MONTH'), 1) - 1 AS last_date_inventory

        FROM (SELECT W.ORG_ID,
                     W.GOODS_ID,
                     TO_DATE(TO_CHAR(CAST(SUBSTR(W.REG_DATETIME, 1, 10) AS DATE),
                                     'YYYYMMDD',
                                     'NLS_CALENDAR=PERSIAN'),
                             'YYYYMMDD') date_inventory,
                     SUM(W.QTY) amount
                FROM TREND_WAREHOUSE W
               GROUP BY W.ORG_ID,
                        W.GOODS_ID,
                        CAST(SUBSTR(W.REG_DATETIME, 1, 10) AS DATE)
               ORDER BY 3)),
    all_dates AS
     (SELECT first_date_inventory + LEVEL - 1 AS date_inventory
        FROM date_inventory_range
      CONNECT BY LEVEL <= 1 + last_date_inventory - first_date_inventory),
    dense_data AS
     (SELECT s.org_id,
             s.goods_id,
             TRUNC(d.date_inventory, 'MONTH') AS mnth,
             NVL(SUM(s.amount) OVER(PARTITION BY s.org_id,
                      s.goods_id ORDER BY d.date_inventory),
                 0) AS total_amount
        FROM all_dates d
        LEFT OUTER JOIN (SELECT W.ORG_ID,
                               W.GOODS_ID,
                               TO_DATE(TO_CHAR(CAST(SUBSTR(W.REG_DATETIME, 1, 10) AS DATE),
                                     'YYYYMMDD',
                                     'NLS_CALENDAR=PERSIAN'),
                             'YYYYMMDD') date_inventory,
                               SUM(W.QTY) amount
                          FROM TREND_WAREHOUSE W
                         WHERE W.Org_Id = 10
                           and W.Goods_Id = 1
                         GROUP BY W.ORG_ID,
                                  W.GOODS_ID,
                                  CAST(SUBSTR(W.REG_DATETIME, 1, 10) AS DATE)
                         ORDER BY 3) s PARTITION BY(s.org_id, s.goods_id)
          ON s.date_inventory = d.date_inventory)
    SELECT org_id,
           goods_id,
           TO_CHAR(mnth, 'YYYY-MM') AS month,
           AVG(total_amount) AS month_avg
      FROM dense_data
     GROUP BY org_id, goods_id, mnth
     ORDER BY org_id, goods_id, mnth;

感谢您的指导

1 个答案:

答案 0 :(得分:1)

不熟悉:CAST(SUBSTR('19 -MAY-18',1,10)AS DATE),因为我习惯于to_date函数并始终指定格式字符串。

这似乎正常工作:

select to_char(to_date('20190521', 'YYYYMMDD'), 'YYYYMMDD', 'NLS_CALENDAR=PERSIAN')
  from dual;

哪里出错了,就是在不指定NLS_CALENDAR格式的情况下将此字符串转换回日期。所以你应该使用这个:

select to_date(to_char(to_date('20190521', 'YYYYMMDD'), 'YYYYMMDD', 'NLS_CALENDAR=PERSIAN')
              ,'YYYYMMDD'
              ,'NLS_CALENDAR=PERSIAN')
  from dual;

最后的开始日期与您相同。

请注意,Oracle日期没有格式。它代表一个时间点,没有任何特定的符号。只有将日期转换为字符串时,格式才有意义。