在Oracle中,递归子查询分解在日期方面有些奇怪

yjghlzjz  于 2023-03-29  发布在  Oracle
关注(0)|答案(2)|浏览(118)

我有一个名为trans_info的表。
| 日期_d|重量|
| --------------|--------------|
| 2016年1月1日|三|
| 2016-01-02 2016-01-02|二|
| 2016-01-03 2016-01-03|1|
| 2016-01-05 2016-01-05|四|
当我执行SQL时

with m1 as (
  select 
  ti.date_d,ti.weight,row_number() over(order by ti.date_d) rn
  from trans_info ti 
),m2(date_d,weight,rn,start_date) as (
  select
  date_d,weight,rn,date_d as start_date
  from m1 where rn=1
  union all
  select
  a.date_d,a.weight,a.rn,
  case when b.start_date+4 <= a.date_d then a.date_d else b.start_date end
  from m1 a,m2 b where b.rn+1=a.rn
)

select * from m2

结果是
| 日期_d|重量|开始日期|
| --------------|--------------|--------------|
| 2016年1月1日|三|2016年1月1日|
| 2016-01-02 2016-01-02|二|2016-01-02 2016-01-02|
| 2016-01-03 2016-01-03|1|2016-01-03 2016-01-03|
| 2016-01-05 2016-01-05|四|2016-01-05 2016-01-05|
但是,当我将sql更改为以下内容时

with m1 as (
  select 
  ti.date_d,ti.weight,row_number() over(order by ti.date_d) rn
  from trans_info ti 
),m2(date_d,weight,rn,start_date) as (
  select
  date_d,weight,rn,date_d as start_date
  from m1 where rn=1
  union all
  select
  a.date_d,a.weight,a.rn,
  case when 4 <= a.date_d-b.start_date then a.date_d else b.start_date end
  from m1 a,m2 b where b.rn+1=a.rn
)

select * from m2

结果是
| 日期_d|重量|开始日期|
| --------------|--------------|--------------|
| 2016年1月1日|三|2016年1月1日|
| 2016-01-02 2016-01-02|二|2016年1月1日|
| 2016-01-03 2016-01-03|1|2016年1月1日|
| 2016-01-05 2016-01-05|四|2016-01-05 2016-01-05|
两个sql之间的唯一区别是日期的比较。

b.start_date+4 <= a.date_d

4 <= a.date_d-b.start_date

这真的很奇怪。为什么?。我在Oracle 11 g上执行SQL。

zdwk9cvp

zdwk9cvp1#

在Oracle中,DATE是一种二进制数据类型,由7个字节组成,代表世纪,世纪年,月,日,小时,分钟和秒。它始终具有这7个组件,并且从不以任何特定的人类可读格式存储。
客户端应用程序(例如SQL Developer、SQL*Plus等)通常选择不以原始二进制形式显示DATE,而是以人类可读的字符串显示。这并不意味着这些组件不存在,只是它们没有显示给您。
要显示整个值,可以用途:

SELECT TO_CHAR(date_d, 'YYYY-MM-DD HH24:MI:SS') FROM trans_info;

或者,在SQL Developer或SQL*Plus中,您可以使用以下命令更改日期的默认显示格式:

ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';

至于你的数据,如果你的日期有午夜时间成分,那么两个查询输出完全相同的值。
如果你的日期有非午夜时间组件,那么你会得到不同的行为,正如你所看到的。

CREATE TABLE trans_info (date_d, weight) AS
SELECT DATE '2016-01-01', 3 FROM DUAL UNION ALL
SELECT DATE '2016-01-02', 2 FROM DUAL UNION ALL
SELECT DATE '2016-01-03', 1 FROM DUAL UNION ALL
SELECT DATE '2016-01-05', 4 FROM DUAL;

然后两个查询输出:
| 日期|重量|注册护士|开始日期|
| --------------|--------------|--------------|--------------|
| 2016年01月01日上午00时00分|三|1|2016年01月01日上午00时00分|
| 2016年01月02日上午00时00分|二|二|2016年01月01日上午00时00分|
| 2016-01-03 00:00:00|1|三|2016年01月01日上午00时00分|
| 2016年01月05日上午00时00分|四|四|2016-01-05 00:00:00|
如果您这样做:

UPDATE trans_info
SET date_d = date_d + INTERVAL '23:59:59' HOUR TO SECOND
WHERE weight = 3;

然后输出更改为:
| 日期|重量|注册护士|开始日期|
| --------------|--------------|--------------|--------------|
| 2016-01-01 23:59:59|三|1|2016-01-01 23:59:59|
| 2016年01月02日上午00时00分|二|二|2016-01-01 23:59:59|
| 2016-01-03 00:00:00|1|三|2016-01-01 23:59:59|
| 2016-01-05 00:00:00|四|四|2016-01-01 23:59:59|
如果将查询更改为:

with m1 as (
  select TRUNC(date_d) AS date_d,
         weight,
         row_number() over(order by TRUNC(date_d)) rn
  from   trans_info
),
m2(date_d,weight,rn,start_date) as (
  select date_d,
         weight,
         rn,
         date_d as start_date
  from   m1
  where  rn=1
  union all
  select a.date_d,
         a.weight,a.rn,
         case
         when 4 <= a.date_d-b.start_date
         then a.date_d
         else b.start_date
         end
  from   m1 a,
         m2 b
  where  b.rn+1=a.rn
)
select * from m2

然后恢复原始行为:
| 日期|重量|注册护士|开始日期|
| --------------|--------------|--------------|--------------|
| 2016年01月01日上午00时00分|三|1|2016年01月01日上午00时00分|
| 2016年01月02日上午00时00分|二|二|2016年01月01日上午00时00分|
| 2016-01-03 00:00:00|1|三|2016年01月01日上午00时00分|
| 2016-01-05 00:00:00|四|四|2016-01-05 00:00:00|
fiddle

xkrw2x1b

xkrw2x1b2#

我在Oracle 11 g上执行SQL
递归子查询分解是11 g发行版2中的新功能,有很多bug。它可能是11840579,因为这是专门关于日期的,但也可能是其他东西,或者是一个未发布的错误。
正如@MTO的fiddle所示,在21 c中,查询得到的结果相同,但是in an unpatched 11.2.0.1 fiddle,前两个查询得到的结果与您所做的完全相同。(还要注意,第四个查询得到一个错误,这似乎是基础版本中的另一个错误)。
假设你还在使用基础版本,如果你有支持合同,你可能仍然可以修补到11.2.0.4,或者升级到更新的版本,在那里这个问题得到了解决。
否则,您只需要仔细检查结果并修改查询以解决遇到的任何异常情况。

相关问题