有人可以解释这个案子吗? 例如,我有一个转储表,其数据如下:
TGL
19810909
19761026
19832529
当我执行此查询时:
SELECT to_date(tgl,'YYYYMMDD') tgl
FROM
(
SELECT tgl
FROM tmpx
WHERE
SUBSTR(tgl,5,2) BETWEEN '01' AND '12'
AND length(tgl) = 8
)
WHERE to_date(tgl,'YYYYMMDD') < to_date('19811231','YYYYMMDD')
结果:没有错误
TGL
09/09/1981
26/10/1976
但是,当我执行此查询时:
SELECT to_date(tgl,'YYYYMMDD') tgl
FROM
(
SELECT tgl
FROM tmpx
WHERE
SUBSTR(tgl,5,2) IN ('01','02','03','04','05','06','07','08','09','10','01','12')
AND length(tgl) = 8
)
WHERE to_date(tgl,'YYYYMMDD') < to_date('19811231','YYYYMMDD')
结果:错误
ORA-01843: not a valid month
为什么第三行(19832529)包含在导致错误的选择中? 而如果我执行以下查询:
SELECT tgl
FROM tmpx
WHERE
SUBSTR(tgl,5,2) IN ('01','02','03','04','05','06','07','08','09','10','11','12')
AND length(tgl) = 8
结果是这样的(没有第3行)
TGL
19810909
19761026
谢谢。
答案 0 :(得分:1)
如果查看两个查询的执行计划,您可以看到优化程序如何处理它们。对于第一个:
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 6 | 3 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| TMPX | 1 | 6 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(LENGTH("TGL")=8 AND SUBSTR("TGL",5,2)>='01' AND
SUBSTR("TGL",5,2)<='12' AND TO_DATE("TGL",'YYYYMMDD')<TO_DATE('
1981-12-31 00:00:00', 'syyyy-mm-dd hh24:mi:ss'))
第二个:
--------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 6 | 3 (0)| 00:00:01 |
|* 1 | TABLE ACCESS FULL| TMPX | 1 | 6 | 3 (0)| 00:00:01 |
--------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(LENGTH("TGL")=8 AND TO_DATE("TGL",'YYYYMMDD')<TO_DATE('
1981-12-31 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND
(SUBSTR("TGL",5,2)='01' OR SUBSTR("TGL",5,2)='02' OR
SUBSTR("TGL",5,2)='03' OR SUBSTR("TGL",5,2)='04' OR
SUBSTR("TGL",5,2)='05' OR SUBSTR("TGL",5,2)='06' OR
SUBSTR("TGL",5,2)='07' OR SUBSTR("TGL",5,2)='08' OR
SUBSTR("TGL",5,2)='09' OR SUBSTR("TGL",5,2)='10' OR
SUBSTR("TGL",5,2)='12'))
请注意应用过滤器的顺序。在第一个中,它首先查看子字符串,然后只有通过该过滤器的值才会转换为1998年比较的日期。
在第二个中,首先进行日期检查,因此它会在过滤掉之前尝试转换无效值。
这里真正的问题是将日期存储为字符串,这允许输入无效数据。如果您坚持使用那么另一种方法是使用函数来尝试将字符串转换为日期并忽略抛出的错误,这仍然不理想,但会忽略您已经存在的相同值。有很多例子,including this one of mine。你可以这样做:
SELECT safe_to_date(tgl) tgl
FROM tmpx
WHERE safe_to_date(tgl) < date '1981-12-31';
或者如果您愿意:
SELECT tgl
FROM (
SELECT safe_to_date(tgl) tgl
FROM tmpx
)
WHERE tgl < date '1981-12-31';
您的功能只能查找YYYYMMDD格式字符串,或者您可以传入要检查的格式,如果您不希望它灵活。