oracle查询中'to_date'和'long to date'之间的结果令人困惑

时间:2014-01-13 13:50:30

标签: sql oracle date

我有一个名为“订阅”的表格,如下所示。

desc subscription;

Name                  Null     Type       
--------------------- -------- ----------
SUBSCRIPTION_ID       NOT NULL NUMBER(38)
EXPIRATIONDATE                 DATE`

输出查询如下。

SELECT
  subscription_id,
  expirationdate
FROM subscription
WHERE subscription_id = 41919;

SUBSCRIPTION_ID        EXPIRATIONDATE          
---------------------- -------------------------
41919                  18-JAN-14 13:45:56 

我正试图以不同的方式执行以下查询。

第一个查询返回一行:

SELECT s.subscription_id
  FROM subscription$active s
 WHERE s.expirationdate - (116 / 24)
       BETWEEN TO_DATE('13-JAN-14 11:38:22', 'dd/mm/yyyy hh24:mi:ss')
           AND TO_DATE('13-JAN-14 18:30:00', 'dd/mm/yyyy hh24:mi:ss')
   AND s.subscription_id = 41919;

SUBSCRIPTION_ID        
----------------------
41919

第二个查询不返回任何行:

SELECT s.subscription_id
  FROM subscription$active s
 WHERE s.expirationdate - (116 / 24)
       BETWEEN (trunc(1389613102220 / (1000), 0) / (24 * 60 * 60))
                + to_date('01/01/1970', 'mm/dd/yyyy')
           AND (trunc(1389637800000 / (1000), 0) / (24 * 60 * 60))
                + to_date('01/01/1970', 'mm/dd/yyyy')
   AND s.subscription_id = 41919;

SUBSCRIPTION_ID
----------------

这里两个上面的where子句是相同的。第一个尝试使用“to_date”,第二个转换“long to date”。但是当我看到输出时,第一个返回一行,第二个不返回任何结果。 我无法找到“长期迄今为止”转换在这里产生的差异。

长期与日期之间的转换也是正确的。
select (trunc(1389613102220 / (1000), 0) / (24 * 60 * 60)) + to_date('01/01/1970','mm/dd/yyyy') from dual

输出:
13-JAN-14 11:38:22

select (trunc(1389637800000 / (1000), 0) / (24 * 60 * 60)) + to_date('01/01/1970','mm/dd/yyyy') from dual

输出:
13-JAN-14 18:30:00

有人可以帮助我理解第一个和第二个查询之间的区别吗?

2 个答案:

答案 0 :(得分:3)

问题不在于您的基于纪元的查询,它是您表中的数据(在某种程度上,您构建非纪元版本的日期的方式)。您使用的是2位数年份和4位数字格式的掩码。当您在过滤器中使用to_date时,您实际上使用的是0014年,而不是2014;你可以看到只是通过转换字符串值,但显示完整的四位数年份的结果:

select to_char(to_date('13-JAN-14 11:38:22', 'dd/mm/yyyy hh24:mi:ss'),
  'YYYY-MM-DD HH24:MI:SS') as test_date
from dual;

TEST_DATE         
-------------------
0014-01-13 11:38:22 

关键部分是您使用格式模型14转换YYYY。正如the documentation mentions

  

数字元素用前导零填充到宽度   元素允许的最大值。例如,YYYY元素   被填充到四位数(长度'9999')

虽然这部分主要讨论的是to_char,但这同样适用于to_date。执行to_date('14', 'YYYY')时,它被解释为to_date('0014', 'YYYY')。您可以改为使用RRRRRR,因为您只提供了当年的两位数字,其中任何一个都会给您2014;但最好是明确的。

看起来您也是在插入过程中执行此操作,因为如果到期日期也在41919,您的第一个查询只会找到记录0014

当您使用纪元时间戳转换时,您实际上获得了2014,因此您的记录实际上不在该范围内。

要确认这一点,请在初始查询中指定包含全年的格式模型:

select subscription_id,
  to_char(expirationdate, 'YYYY-MM-DD HH24:MI:SS') as expirationdate
from subscription
where subscription_id = 41919; 

SUBSCRIPTION_ID EXPIRATIONDATE    
--------------- -------------------
          41919 0014-01-18 13:45:56 

如果您在第一个查询中更改日期字符串或格式模型(现在只能使用,因为Oracle有时太有助于解析这些),您也会看到没有数据:

select s.subscription_id from subscription s
where s.expirationdate - (116/24) between TO_DATE('13-JAN-14 11:38:22',
    'dd-mon-rr hh24:mi:ss')
  and TO_DATE('13-JAN-14 18:30:00', 'dd-mon-rr hh24:mi:ss')
and s.subscription_id=41919;

...如果表格日期为0014,则不会返回任何行。

答案 1 :(得分:0)

检查通过查询DBMS_Xplan()生成的解释计划的完整输出,它将显示内部使用的值作为评估这些常量的结果。这可能表明存在意外的数据类型转换。