2

在尝试使用 Oracle 数据库执行一些检查时,XQuery我需要比较两个日期是否相同,但不能以明显的方式做到这一点,因为强制转换xs:date并不会真正从xs:dateTime.

查询本身似乎在另一个环境中运行良好(例如http://www.xpathtester.com/xquery)。

我是否错过了重要的事情,或者这种情况只是一个错误并且需要特殊的解决方法(转换为字符串值以进行比较,分别比较两个日期的年份、月份和日期等等)?


一个小例子...

假设我们有一个简单的 XML:

<root>
  <date_value>2015-09-11T15:25:55</date_value>
</root> 

并希望与忽略时间部分date_value的固定值进行比较。xs:date('2015-09-11')

首先,将节点的内容转换为所需的类型,并通过将其转换为来删除时间部分xs:date

xs:date(xs:dateTime($doc/root/date_value))

XMLQuery()如果我们在传递上面的文档时选择这个值as $doc,我们会得到预期的输出:

2015-09-11+00:00 

好的。似乎时间部分已删除,但比较失败:

xs:date(xs:dateTime($doc/root/date_value)) eq xs:date('2015-09-11') 

返回false,如果我们尝试查看表达式中的值之间的差异而不是比较它们:

xs:date(xs:dateTime($doc/root/date_value)) - xs:date('2015-09-11') 

我们看到'PT15H25M55S',它们是完全匹配时间的一部分date_value

使用上述所有表达式进行查询以进行测试:

select 
  XMLCast(
    XMLQuery( column_value
      passing 
        xmltype(q'[
          <root>
            <date_value>2015-09-11T15:25:55</date_value>
          </root> 
        ]') as "doc"
      returning content
    )
    as varchar2(4000)
  ) result_value,
  column_value  expression
from 
  table(sys.odcivarchar2list(
    q'[ xs:date(xs:dateTime($doc/root/date_value)) ]',
    q'[ xs:date('2015-09-11') ]',
    q'[ xs:date(xs:dateTime($doc/root/date_value)) eq xs:date('2015-09-11') ]',
    q'[ xs:date(xs:dateTime($doc/root/date_value)) - xs:date('2015-09-11') ]'
  ))

在此 Oracle 版本上重现的行为:

Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit Production
PL/SQL Release 12.1.0.2.0 - Production
CORE    12.1.0.2.0  Production
TNS for IBM/AIX RISC System/6000: Version 12.1.0.2.0 - Production
NLSRTL Version 12.1.0.2.0 - Production

Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
PL/SQL Release 11.2.0.3.0 - Production
CORE    11.2.0.3.0  Production
TNS for IBM/AIX RISC System/6000: Version 11.2.0.3.0 - Production
NLSRTL Version 11.2.0.3.0 - Production

更新

感谢collapsarAlex Poole的回答,它们给了我关于正确解决方法的主要想法。但是为了解释问题的核心,我过度简化了我们的用例,其中包括一些日期算术和现实世界的解决方法,看起来像下面的查询。

select 
  XMLCast(
    XMLQuery( 
      q'[
        let 
          $date1 := fn:dateTime( 
                      adjust-date-to-timezone(
                        xs:date(xs:dateTime($doc/root/date_value)),
                        ()
                      ),
                      adjust-time-to-timezone( xs:time('00:00'), ())
                    ),
          $date2 := fn:dateTime( 
                      adjust-date-to-timezone(
                        xs:date(xs:dateTime($doc/root/date_value2)),
                        ()
                      ),
                      adjust-time-to-timezone( xs:time('00:00'), ())
                    )
        return
          $date1 + xs:yearMonthDuration('P1Y') - xs:dayTimeDuration('P1D')
          eq
          $date2
      ]'
      passing 
        xmltype(q'[
          <root>
            <date_value>2015-09-11T01:02:03-11:00</date_value>
            <date_value2>2016-09-10T10:20:30+13:00</date_value2>
          </root> 
        ]') as "doc"
      returning content
    )
    as varchar2(4000)
  ) result_value
from 
  dual
4

2 回答 2

2

将文字日期值提升到 dateTime 值就可以了(从提供的 dateTime 值中提取正确的时间偏移量):

 xs:dateTime($doc/root/date_value) eq fn:dateTime(xs:date('2015-09-11'), xs:time(xs:dateTime($doc/root/date_value)))

此解决方案也适用于仅词汇化为日期的输入。

于 2015-10-26T17:30:50.637 回答
0

您可以从转换回来看到dateTime时间已被保留;这没有帮助,但我不确定这是一个错误还是预期的行为 - 我想象后者在 Oracle 的世界中,并且在 MOS 中看不到任何对这种行为的引用......

您可以改为与dateTime范围进行比较:

select 
  XMLCast(
    XMLQuery( column_value
      passing 
        xmltype(q'[
          <root>
            <date_value>2015-09-11T15:25:55</date_value>
          </root> 
        ]') as "doc"
      returning content
    )
    as varchar2(4000)
  ) result_value,
  column_value  expression
from 
  table(sys.odcivarchar2list(
    q'[ xs:date(xs:dateTime($doc/root/date_value)) ]',
    q'[ xs:date('2015-09-11') ]',
    q'[ xs:date(xs:dateTime($doc/root/date_value)) eq xs:date('2015-09-11') ]',
    q'[ xs:date(xs:dateTime($doc/root/date_value)) - xs:date('2015-09-11') ]',
    q'[ xs:dateTime($doc/root/date_value) ]',
    q'[ xs:dateTime(xs:date(xs:dateTime($doc/root/date_value))) ]',
    q'[ xs:dateTime($doc/root/date_value) ge xs:dateTime('2015-09-11T00:00:00') ]',
    q'[ xs:dateTime($doc/root/date_value) lt xs:dateTime('2015-09-12T00:00:00') ]',
    q'[ xs:dateTime($doc/root/date_value) ge xs:dateTime('2015-09-11T00:00:00')
          and xs:dateTime($doc/root/date_value) lt xs:dateTime('2015-09-12T00:00:00') ]'
  ))
/

这使:

RESULT_VALUE                             EXPRESSION                                                                          
---------------------------------------- -------------------------------------------------------------------------------------
 2015-09-11+00:00                         xs:date(xs:dateTime($doc/root/date_value))                                          
 2015-09-11+00:00                         xs:date('2015-09-11')                                                               
false                                     xs:date(xs:dateTime($doc/root/date_value)) eq xs:date('2015-09-11')                 
PT15H25M55S                               xs:date(xs:dateTime($doc/root/date_value)) - xs:date('2015-09-11')                  
 2015-09-11T15:25:55.000000+00:00         xs:dateTime($doc/root/date_value)                                                   
 2015-09-11T15:25:55.000000+00:00         xs:dateTime(xs:date(xs:dateTime($doc/root/date_value)))                             
true                                      xs:dateTime($doc/root/date_value) ge xs:dateTime('2015-09-11T00:00:00')             
true                                      xs:dateTime($doc/root/date_value) lt xs:dateTime('2015-09-12T00:00:00')             
true                                      xs:dateTime($doc/root/date_value) ge xs:dateTime('2015-09-11T00:00:00')             
                                                  and xs:dateTime($doc/root/date_value) lt xs:dateTime('2015-09-12T00:00:00')    

您需要生成两个日期来比较而不是一个,这对您来说可能是也可能不是问题,这取决于这些日期来自哪里以及您如何构建真正的查询。可能比@collapsar 的子字符串更复杂,但也可以说更明确一点。

于 2015-10-26T17:45:43.323 回答