【问题标题】:Why do I get an incompatible value type for my column?为什么我的列的值类型不兼容?
【发布时间】:2020-04-04 02:48:19
【问题描述】:

我正在尝试使用 JDBC 连接计算 Oracle 数据库中两个日期之间的差异。我遵循了this question 的建议,使用如下查询:

SELECT CREATE_DATE - CLOSED
    FROM TRANSACTIONS;

我收到以下错误:

Incompatible value type specified for 
column:CREATE_DATE-CLOSED. Column Type = 11 and Value Type = 
8.[10176] Error Code: 10176

我应该改变什么才能成功计算日期之间的差异?

注意:CREATE_DATECLOSED 都有 TIMESTAMP 类型

【问题讨论】:

  • 我猜 JDBC 不支持开箱即用的 INTERVAL 数据类型(它不是标准数据类型)
  • create_dateclosed的数据类型是什么?
  • @GMB 他们都是 TIMESTAMP
  • @LukaszSzozda:Oracle JDBC 驱动程序确实支持通过ResultSet.getObject() 的间隔,它将返回oracle.sql.INTERVALDS docs.oracle.com/en/database/oracle/oracle-database/12.2/jjdbc/… 的实例

标签: sql oracle date date-arithmetic


【解决方案1】:

当我们从另一个 Oracle 中减去一个日期时,我们会得到一个数字的差异:这是简单的算术。但是,当我们从另一个中减去一个 时间戳 - 这就是你正在做的 - 结果是一个 INTERVAL。 Older versions of JDBC don't like the INTERVAL datatype (docs)

这里有几个解决方法,具体取决于您要对结果执行的操作。首先是根据间隔结果计算秒数。 extract second from ... 只给了我们间隔中的秒数。如果您的时间间隔不超过五十九秒,这将很好。更长的时间间隔需要我们提取分钟、小时甚至几天。所以这个解决方案是:

select t.*
       ,   extract (day    from (t.closed - t.create_date)) * 84600 
         + extract (hour   from (t.closed - t.create_date)) * 3600
         + extract (minute from (t.closed - t.create_date)) * 60 
         + extract (second from (t.closed - t.create_date)) as no_of_secs
from transactions t 

第二种解决方案是遵循 JDBC 映射指南中的建议,将区间转换为字符串:

select t.*
       , cast ((t.closed - t.create_date) as varchar2(128 char)) as intrvl_str
from transactions t  

字符串区间的格式为verbose:INTERVAL'+000000001 04:40:59.710000'DAY(9)TO SECOND。这在应用程序的 Java 端可能没有用。但是使用正则表达式,我们可以将其转换为可以转换为a Java 8 Duration object (docs) 的字符串:PnDTnHnMn.nS

select t.id
       , regexp_replace(cast ((t.closed - t.create_date) as varchar2(128 char))
                        , 'INTERVAL''\+([0-9]+) ([0-9]{2}):([0-9]{2}):([0-9]{2})\.([0-9]+)''DAY\(9\)TO SECOND'
                        , 'P\1DT\2H\3M\4.\5S') 
        as duration
from transactions t

a demo on db<>fiddle

【讨论】:

  • Oracle JDBC 驱动程序确实支持通过ResultSet.getObject() 的间隔,它将返回oracle.sql.INTERVALDSoracle.sql.INTERVALYM 的实例
  • @a_horse_with_no_name - 适合我查看旧版本的文档
【解决方案2】:

如前所述,JDBC 似乎无法使用 INTERVAL 数据类型。使用 EXTRACT 函数将其作为数字转换为预期输出怎么样?如果您想要这两个时间戳之间的秒数,它将是:

SELECT EXTRACT(SECOND FROM (CREATE_DATE - CLOSED)) FROM TRANSACTIONS;

以下是可以用来代替SECOND 的选项列表: https://docs.oracle.com/database/121/SQLRF/functions067.htm#SQLRF00639

【讨论】:

    【解决方案3】:

    您找到的答案与date 数据类型有关,但您正在处理timestamps。减去两个 Oracle dates 会返回一个数字,而减去 timestamps 会产生一个 interval 数据类型。这可能不是您想要的,而且很明显,您的驱动程序没有正确处理此数据类型。

    对于这个用例,一种解决方案是在减去 casttimestamps 到 dates 之前:

    select cast(create_date as date) - cast(closed as date) from transactions;
    

    【讨论】:

    • 这是有道理的,但结果几乎相同。它给了我这个错误:Incompatible value type specified for column:CAST(create_date)-CAST(closed). Column Type = 9 and Value Type = 8.[10176] Error Code: 10176
    • CAST(create_date as date)
    • 对,该错误来自尝试使用您推荐的查询(大写转换没有改变结果)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-24
    • 2020-06-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多