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)
如果你查看两个查询的执行计划,特别是谓词信息,你会看到第一个执行计划:
---------------------------------------------------------------------------
| 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)