【问题标题】:Unbelievable, Oracle return different result for a query time to time难以置信,Oracle 不时返回不同的查询结果
【发布时间】:2018-10-23 00:03:52
【问题描述】:

我有一个Oracle查询,奇怪的是查询结果有时会有所不同。

select 
cast(pmm_datetime - 1 / 24 * utc_offset as timestamp) as pmm_datetime_utc,
count(distinct(NODEB_NAME)) as num_of_recs
from pmmcounter_db.WC_B_NODEBFUNCTION_QH t1 
where pmm_datetime >= to_timestamp('201810230525','YYYYMMDDHH24MI')
and cast(pmm_datetime - 1 / 24 * utc_offset as timestamp) = '2018-10-22 19:15:00.000'
group by cast(pmm_datetime - 1 / 24 * utc_offset as timestamp) 

大多数时候,查询返回的预期结果如下:

PMM_DATETIME_UTC        NUM_OF_RECS
2018-10-22 19:15:00.000 6930

但是,它随机返回如下结果。如您所见,相同的日期时间和状态被分成了几行。

PMM_DATETIME_UTC        NUM_OF_RECS
2018-10-22 19:15:00.000 785
2018-10-22 19:15:00.000 1990
2018-10-22 19:15:00.000 162
2018-10-22 19:15:00.000 3993

更奇怪的是,我曾尝试用 sum for QH_NUM_OF_RECS 来放另一个组,结果是一样的。 Oracle 好像无法按 PMM_DATETIME_UTC 分组?

这里发生了什么?为什么查询结果会不一样??????

【问题讨论】:

  • pmm_datetime 是日期、时间戳还是带时区的时间戳?
  • @tbone pmm_datetime 是 TIMESTAMP(6)。示例 '2018-10-23 05:00:00.000'
  • 请注意timestamp(6) 也可以存储2018-10-22 18:15:00.0001112018-10-22 18:15:00.000999
  • @a_horse_with_no_name,理论上是的。我们所有的时间戳都四舍五入到 15 分钟。

标签: sql oracle


【解决方案1】:

不要从TIMESTAMP 数据类型中减去数字;这是 Oracle 应用于 DATE 数据类型的操作,会将您的 TIMESTAMP 隐式转换为 DATE 并丢失精度(然后您必须应用显式转换才能将其转换回来)。

改为减去INTERVAL 的倍数(或使用NUMTODSINTERVAL( utc_offset, 'HOUR' ) 生成区间):

SELECT pmm_datetime - utc_offset * INTERVAL '1' HOUR as pmm_datetime_utc,
       COUNT( DISTINCT NODEB_NAME ) as num_of_recs
FROM   pmmcounter_db.WC_B_NODEBFUNCTION_QH t1 
WHERE  pmm_datetime >= TIMESTAMP '2018-10-23 05:25:00'
AND    pmm_datetime - utc_offset * INTERVAL '1' HOUR = TIMESTAMP '2018-10-22 19:15:00.000'
GROUP BY pmm_datetime - utc_offset * INTERVAL '1' HOUR

您还可以使用时间戳文字 TIMESTAMP '2018-10-22 19:15:00.000',而不是依赖从字符串到 TIMESTAMP 数据类型的隐式转换。

您还可以使用TIMESTAMP WITH TIME ZONE 数据类型来管理使用FROM_TZ 函数和AT TIME ZONE 'UTC' 转换为UTC。

为什么[可能]查询结果不同?

TIMESTAMP(6) 数据类型可以包含微秒值。如果您只对微秒值感兴趣,则将该值转换为 TIMESTAMP(3) 以降低精度。

你可以使用:

SELECT TO_CHAR(
         pmm_datetime - utc_offset * INTERVAL '1' HOUR,
         'YYYY-MM-DD HH24:MI:SS.FF6'
       ) as pmm_datetime_utc,
       COUNT( DISTINCT NODEB_NAME ) as num_of_recs
...

看看是不是这样。

【讨论】:

  • 太好了,它解决了问题。在这种情况下,中间的转换是不必要且有问题的。您认为这是 Oracle 方面的问题吗?
【解决方案2】:

尝试使用以下代码。它可以解决问题。您在 selectwheregroup by 中重复了 cast(pmm_datetime - 1 / 24 * utc_offset as timestamp) 表达式,这可能会返回不同的值。代码中重复行的原因是group by

with
    cte
as
(
select 
    cast(pmm_datetime - 1 / 24 * utc_offset as timestamp)   as pmm_datetime_utc,
    NODEB_NAME                                              as num_of_recs
from
    pmmcounter_db.WC_B_NODEBFUNCTION_QH t1 
where
    pmm_datetime >= to_timestamp('201810230525','YYYYMMDDHH24MI')
)
select
    pmm_datetime_utc,
    count(distinct(NODEB_NAME)) as num_of_recs
from
    cte
where
    pmm_datetime_utc = '2018-10-22 19:15:00.000'
group by
    pmm_datetime_utc;

【讨论】:

  • 为什么会有任何不同? utc_offset 似乎是一个常数(关于查询的单次执行),pmm_datetime 是存储在列中的 TIMESTAMP(6) 值,在查询的不同部分执行期间两者都不会发生变化。跨度>
  • 感谢 Mohammad,您建议的查询因 ORA-00979 而失败。不是按表达式分组。:)
  • @user3893836 你是对的。很抱歉,查询中有一些错误。我更新了答案。
  • @MT0 group by 是唯一在此查询结果集中进行更改的子句。我的意思是 pmm_datetime_utc 列有一些不同的值进行了更改。
猜你喜欢
  • 1970-01-01
  • 2011-11-08
  • 1970-01-01
  • 2011-10-25
  • 1970-01-01
  • 2021-11-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多