【问题标题】:Why is Oracle Pivot producing non-existent results?为什么 Oracle Pivot 会产生不存在的结果?
【发布时间】:2020-03-24 08:55:46
【问题描述】:

我管理一个数据库,其中包含从各个站点收集的大量气候数据。这是一个 Oracle 12.2 DB,下面是相关表的概要:

FACT = 在特定时间的单独测量

  • UTC_START = 开始测量的 UTC 时间
  • LST_START = 开始测量的本地标准时间(到特定站点)的时间
  • SERIES_ID = 测量所属系列的 ID(FK 到 SERIES)
  • STATION_ID = 发生测量的台站 ID(FK 到 STATION)
  • VALUE = 测量值

请注意,每个站的 UTC_START 和 LST_START 始终具有恒定的差异(LST 与 UTC 的偏移)。我已经确认没有任何情况下 UTC_START 和 LST_START 之间的差异超出预期。

SERIES = 数据系列的描述性数据

  • SERIES_ID = 系列 ID (PK)
  • NAME = 系列的文本名称(例如温度)

STATION = 台站的描述性数据

  • STATION_ID = 电台 ID (PK)
  • SITE_ID = 站点所在站点的 ID(大多数站点有一个站点,但少数站点有 2 个)
  • SITE_RANK = 站点内站点的排名(如果站点超过 1 个)。
  • EXT_ID = 网站的外部 ID(提供给我们)

站点的 EXT_ID 适用于该站点的所有站点(但可能不会填充,除非 SITE_RANK == 1,不理想,我知道,但不是这里的问题),并且首选来自排名较低的站点的数据。为了将这些数据组织成可使用的格式,我们使用 PIVOT 将在同一地点/时间发生的测量数据收集到行中。

这是查询:

WITH
    primaries AS (
        SELECT site_id, ext_id
        FROM station
        WHERE site_rank = 1
    ),

    data as (
        SELECT d.site_id, d.utc_start, d.lst_start, s.name, d.value FROM (
            SELECT s.site_id, f.utc_start, f.lst_start, f.series_id, f.value,
                 ROW_NUMBER() over (PARTITION BY s.site_id, f.utc_start, f.series_id ORDER BY s.site_rank) as ORDINAL
                 FROM fact f
                      JOIN station s on f.station_id = s.station_id
        ) d
            JOIN series s ON d.series_id = s.series_id
            WHERE d.ordinal = 1
                AND d.site_id = ?
                AND d.utc_start >= ?
                AND d.utc_start < ?
    )

    records as (

        SELECT * FROM data
        PIVOT (
               MAX(VALUE) AS VALUE
               FOR NAME IN (
                   -- these are a few series that we would want to collect by UTC_START
                   't5' as t5,
                   'p5' as p5,
                   'solrad' as solrad,
                   'str' as str,
                   'stc_05' as stc_05,
                   'rh' as rh,
                   'smv005_05' as smv005_05,
                   'st005_05' as st005_05,
                   'wind' as wind,
                   'wet1' as wet1
                   )
                )
    )

SELECT r.*, p.ext_id
FROM records r JOIN primaries p on r.site_id = p.site_id

这就是事情变得奇怪的地方。此查询在 SQLAlchemy、IntelliJ(使用 OJDBC Thin)和 Orcale SQL Developer 中完美运行。但是,当它在我们的 Java 程序中运行时(相同的 JDBC url 和凭据,使用普通的旧 JDBC 语句和结果集),它会给出没有意义的结果。特别是对于同一个站,它将返回 2 行具有相同的 UTC_START,但不同的 LST_START(回想一下,我已经验证了这个 100% 不会出现在 FACT 表的任何地方)。为了确保不会发生奇怪的参数处理,我们测试了占位符的硬编码值,并在各个客户端之间复制并粘贴了完全相同的查询,唯一返回这些奇怪结果的是 Java 程序(它使用与 IntelliJ 完全相同的 OJDBC jar)。

如果有人有任何见解或可能的原因,将不胜感激。我们现在有点不知所措。

【问题讨论】:

  • UTC_STARTLST_START的数据类型是什么?由于时区不同,这听起来像是一些转换..
  • 它们都是 DATE,但 LST_START 只是 UTC_START 加上 UTC 偏移量(即它是从 UTC_START 计算的,而不是直接观察到的)。而且我已经验证了 UTC_START 和 LST_START 对于 FACT 中的每一行都有适当的差异。实际上没有明确的时区转换本身,它只是一个简单的加法操作
  • 尝试在 CTE 查询中使用 /*+ MATERIALIZE */ 或 /*+ INLINE */ 提示。如果相同的 SQL 返回不同的结果,则取决于 Exec。计划的话,肯定是Oracle BUG。在引入 12c 版本时,以一种新的方式对 GROUP BY 评估存在一些令人讨厌的错误。
  • @ibre5041 任何一个提示都会发生同样的事情。在执行计划的注释中,通过 IntelliJ 或 Java 运行时的步骤是相同的​​,但由于某种原因,Java 版本的估计成本要小得多
  • 您确定这不是 Java 程序显示或输出结果数据的方式问题,而不是 Oracle 返回的数据不正确吗?

标签: java sql oracle jdbc oracle12c


【解决方案1】:

事实证明Nathan's comment 是正确的。尽管这似乎违反直觉(至少对我而言),但似乎在 DATE 列上调用 ResultSet.getString 实际上会首先转换为 Timestamp。除非您明确指定,否则时间戳具有使用系统默认时区的不幸默认行为。

这种默认行为意味着我们不打算考虑夏令时,从而导致所描述的奇怪行为。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-11-10
    • 2018-02-05
    • 1970-01-01
    • 2017-02-21
    • 1970-01-01
    相关资源
    最近更新 更多