如果条件延长时间,则Oracle查询不返回结果

Bra*_*ing 3 sql database oracle

如果我做:

SELECT count(*) FROM XX where "date" >= '8-APR-2015' and "date" <= '8-APR-2016'
Run Code Online (Sandbox Code Playgroud)

它将返回许多行,但如果我这样做:

SELECT count(*) FROM XX where "date" >= '8-APR-2010' and "date" <= '8-APR-2016'
Run Code Online (Sandbox Code Playgroud)

它返回0.这怎么可能?如果有什么我会得到更多的行,因为我正在增加有效的检索范围.有任何想法吗?

编辑:

NLS_TIMESTAMP_FORMAT 'DD-MON-RR HH.MI.SSXFF 
NLS_DATE_FORMAT DD-MON-RR
Run Code Online (Sandbox Code Playgroud)

Ale*_*ole 5

如果你查看两个查询的执行计划,特别是谓词信息,你会看到第一个执行计划:

---------------------------------------------------------------------------     
| Id  | Operation          | Name | Rows  | Bytes | Cost (%CPU)| Time     |     
---------------------------------------------------------------------------     
|   0 | SELECT STATEMENT   |      |     1 |    13 |     3   (0)| 00:00:01 |     
|   1 |  SORT AGGREGATE    |      |     1 |    13 |            |          |     
|*  2 |   TABLE ACCESS FULL| XX   |     1 |    13 |     3   (0)| 00:00:01 |     
---------------------------------------------------------------------------     

Predicate Information (identified by operation id):                             

   2 - filter("date">=TO_TIMESTAMP('8-APR-2015') AND                            
              "date"<=TO_TIMESTAMP('8-APR-2016'))                               
Run Code Online (Sandbox Code Playgroud)

而第二个做:

----------------------------------------------------------------------------    
| Id  | Operation           | Name | Rows  | Bytes | Cost (%CPU)| Time     |    
----------------------------------------------------------------------------    
|   0 | SELECT STATEMENT    |      |     1 |    13 |     0   (0)|          |    
|   1 |  SORT AGGREGATE     |      |     1 |    13 |            |          |    
|*  2 |   FILTER            |      |       |       |            |          |    
|*  3 |    TABLE ACCESS FULL| XX   |     1 |    13 |     3   (0)| 00:00:01 |    
----------------------------------------------------------------------------    

Predicate Information (identified by operation id):                             
---------------------------------------------------                             

   2 - filter(NULL IS NOT NULL)                                                 
   3 - filter("date">=TO_TIMESTAMP('8-APR-2010') AND                            
              "date"<=TO_TIMESTAMP('8-APR-2016'))                               
Run Code Online (Sandbox Code Playgroud)

而且NULL IS NOT NULL从来都不是真的,那就是零行.但这取决于您的NLS设置.使用其他格式掩码时,它没有该过滤步骤.

如果您查看to_timestamp()使用格式NLS设置评估这些调用的方式,您可以了解正在发生的事情:

alter session set nls_timestamp_format = 'DD-MON-RR HH.MI.SSXFF';

select to_char(to_timestamp('8-APR-2015'), 'YYYY-MM-DD') as from_1,
  to_char(to_timestamp('8-APR-2016'), 'YYYY-MM-DD') as to_1,
  to_char(to_timestamp('8-APR-2010'), 'YYYY-MM-DD') as from_2,
  to_char(to_timestamp('8-APR-2016'), 'YYYY-MM-DD') as to_2
from dual;

FROM_1     TO_1       FROM_2     TO_2     
---------- ---------- ---------- ----------
2015-04-08 2016-04-08 2020-04-08 2016-04-08
Run Code Online (Sandbox Code Playgroud)

第一对日期看起来还行 - 2015年是2016年之前.但第二对日期已经到了2020年,而不是2010年; 由于Oracle足够聪明地意识到2020年晚于2016年,它知道没有可匹配的数据,并且增加了短路的不可能条件并避免了冗余数据访问.

将其与正确处理四位数年份的面具进行比较:

alter session set nls_timestamp_format = 'DD-MON-RRRR HH.MI.SSXFF';

select to_char(to_timestamp('8-APR-2015'), 'YYYY-MM-DD') as from_1,
  to_char(to_timestamp('8-APR-2016'), 'YYYY-MM-DD') as to_1,
  to_char(to_timestamp('8-APR-2010'), 'YYYY-MM-DD') as from_2,
  to_char(to_timestamp('8-APR-2016'), 'YYYY-MM-DD') as to_2
from dual;

FROM_1     TO_1       FROM_2     TO_2     
---------- ---------- ---------- ----------
2015-04-08 2016-04-08 2010-04-08 2016-04-08
Run Code Online (Sandbox Code Playgroud)

现在,'date'的第二个是正确的.

所不同的是下降到了如何RR格式掩码的行为,尽管这种特定的行为是不是真的记录.

实际发生的事情取决于Oracle在尝试灵活解释格式掩码方面的帮助.正如它在文档中所说的那样,在日期时间格式元素表下,"Oracle数据库将字符串转换为具有一定灵活性的日期" - 但其效果有时会有点意外.

它实际上就是把它抛出 的一点点RR.你可以通过这个小小的演示看到:

with t as (
  select 1998 + level as year from dual connect by level < 16
)
select year, to_char(to_timestamp(to_char(year), 'RR HH'), 'YYYY-MM-DD HH24:MI:SS')
from t;

      YEAR TO_CHAR(TO_TIMESTAM
---------- -------------------
      1999 1999-04-01 00:00:00
      2000 2000-04-01 00:00:00
      2001 2020-04-01 01:00:00
      2002 2020-04-01 02:00:00
      2003 2020-04-01 03:00:00
      2004 2020-04-01 04:00:00
      2005 2020-04-01 05:00:00
      2006 2020-04-01 06:00:00
      2007 2020-04-01 07:00:00
      2008 2020-04-01 08:00:00
      2009 2020-04-01 09:00:00
      2010 2020-04-01 10:00:00
      2011 2020-04-01 11:00:00
      2012 2020-04-01 12:00:00
      2013 2013-04-01 00:00:00
Run Code Online (Sandbox Code Playgroud)

RR模型似乎只看到了今年的前两位数字,但是当它有用时它也会尝试为你处理四位数的年份,这对于2015年和2016年都有效.如果面具有其他年份它可以工作没有时间成分.但它确实如此,并且更倾向于使用HH蒙版的一部分来解释四位数年份的第三和第四个字符.

因此,对于2010年,它看到'10',决定它可以将其解释为一个HH值,那样做,然后只使用RR面具转换剩余的两个数字'20' - 它被视为2020.所以你最终在上午10点2020年4月8日.同样的事情发生在2000年(虽然你无法区分)到2012年.当你到了2013年,'13'不再对HH面具有效,所以它又回到了治疗所有四个数字作为年份.如果NLS格式桅杆HH24那么它也将在2013-2023"打破".


道德是永远不要依赖NLS设置.(绝不使用2位数年份或2位数年份面具).将字符串显式转换为日期/时间戳:

where "date" >= to_timestamp('8-APR-2015', 'DD-MON-YYYY')
and "date" <= to_timestamp('8-APR-2016', 'DD-MON-YYYY');
Run Code Online (Sandbox Code Playgroud)

...虽然最好不要使用月份名称,因为它们也依赖于NLS,但您可以指定您想要英语翻译:

where "date" >= to_timestamp('8-APR-2015', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE=ENGLISH')
and "date" <= to_timestamp('8-APR-2016', 'DD-MON-YYYY', 'NLS_DATE_LANGUAGE=ENGLISH');
Run Code Online (Sandbox Code Playgroud)

或者甚至更好的固定值,使用ANSI日期/时间戳文字:

where "date" >= timestamp '2010-04-08 00:00:00'
and "date" <= timestamp '2016-04-08 00:00:00';
Run Code Online (Sandbox Code Playgroud)