为什么这个 to_date 不起作用,当结果已被过滤以匹配我的日期格式时 (Oracle SQL)
Why doesn't this to_date work, when the results have been filtered to match my date format (Oracle SQL)
我有一个 table 'A' 一列 (VARCHAR2)。 table 包含包含文本“01/01/2021”的行和包含文本 'A'.
的另一行
当我尝试过滤掉 'A' 然后 to_date 剩余值时,我得到 'ORA-01858: a non-numeric character was found where a numeric was expected'。我试过两种方法。
select *
from tbl
where col <> 'A'
and to_Date(col,'DD/MM/YYYY') = to_date('01/01/2020','DD/MM/YYYY');
select *
from ( select *
from tbl
where col <> 'A')
where to_Date(col,'DD/MM/YYYY') = to_date('01/01/2020','DD/MM/YYYY');
我能理解为什么第一个可能不起作用,但在第二个示例中,to_date 应该只能看到过滤后的数据(即“01/01/2020”)。
当我删除 'A' 的值时,语句运行并且我得到了我的结果,所以它不是 运行 的原因似乎是确定的,因为它试图 to_date 'A' 的值,尽管到那时应该已经过滤掉了。
我已经能够使用实际的 Oracle tables 复制它,但不幸的是,当我尝试使用 WITH AS 重现 tables 时,查询有效并且没有遇到错误 - 另一个谜!
为什么这个查询不起作用?操作顺序似乎令人满意(如果我使用 WITH AS 就可以)。
Oracle(和其他数据库)没有义务在评估外部谓词之前评估应用于内联视图的谓词。实际上,从性能优化的角度来看,您经常希望优化器将选择性谓词从外部查询推送到视图、内联视图或子查询中。在这种情况下,查询是否抛出错误将取决于优化器选择的查询计划以及它实际首先评估的谓词。
作为快速技巧,您可以更改内联视图以防止推送谓词。在这种情况下,rownum
的存在会阻止优化器推送谓词。您还可以使用 no_push_pred
之类的提示来尝试强制优化器使用您想要的计划
select *
from ( select t.*, rownum rn
from tbl t
where col <> 'A')
where to_Date(col,'DD/MM/YYYY') = to_date('01/01/2020','DD/MM/YYYY');
不过,这些快速技巧中的任何一个都存在问题,即某些未来版本的优化器可能会提供比您现在所知更多的选项,因此您将来可能会遇到问题。
更好的选择是重写查询,这样您就不必关心谓词的计算顺序。在这种情况下(取决于 Oracle 版本),这很容易,因为 to_date
允许您在出现转换错误时指定一个值
select *
from tbl
where col <> 'A'
and to_Date(col default null on conversion error,'DD/MM/YYYY') =
to_date('01/01/2020','DD/MM/YYYY');
如果您使用的是早期版本的 Oracle,或者 to_date
只是实际问题的一个示例,您可以创建一个执行相同操作的自定义函数。
create function safe_to_date( p_str in varchar2, p_fmt in varchar2 )
return date
is
begin
return to_date( p_str, p_fmt );
exception
when value_error
then
return null;
end safe_to_date;
select *
from tbl
where col <> 'A'
and safe_to_date(col,'DD/MM/YYYY') = to_date('01/01/2020','DD/MM/YYYY');
我有一个 table 'A' 一列 (VARCHAR2)。 table 包含包含文本“01/01/2021”的行和包含文本 'A'.
的另一行当我尝试过滤掉 'A' 然后 to_date 剩余值时,我得到 'ORA-01858: a non-numeric character was found where a numeric was expected'。我试过两种方法。
select *
from tbl
where col <> 'A'
and to_Date(col,'DD/MM/YYYY') = to_date('01/01/2020','DD/MM/YYYY');
select *
from ( select *
from tbl
where col <> 'A')
where to_Date(col,'DD/MM/YYYY') = to_date('01/01/2020','DD/MM/YYYY');
我能理解为什么第一个可能不起作用,但在第二个示例中,to_date 应该只能看到过滤后的数据(即“01/01/2020”)。
当我删除 'A' 的值时,语句运行并且我得到了我的结果,所以它不是 运行 的原因似乎是确定的,因为它试图 to_date 'A' 的值,尽管到那时应该已经过滤掉了。
我已经能够使用实际的 Oracle tables 复制它,但不幸的是,当我尝试使用 WITH AS 重现 tables 时,查询有效并且没有遇到错误 - 另一个谜!
为什么这个查询不起作用?操作顺序似乎令人满意(如果我使用 WITH AS 就可以)。
Oracle(和其他数据库)没有义务在评估外部谓词之前评估应用于内联视图的谓词。实际上,从性能优化的角度来看,您经常希望优化器将选择性谓词从外部查询推送到视图、内联视图或子查询中。在这种情况下,查询是否抛出错误将取决于优化器选择的查询计划以及它实际首先评估的谓词。
作为快速技巧,您可以更改内联视图以防止推送谓词。在这种情况下,rownum
的存在会阻止优化器推送谓词。您还可以使用 no_push_pred
之类的提示来尝试强制优化器使用您想要的计划
select *
from ( select t.*, rownum rn
from tbl t
where col <> 'A')
where to_Date(col,'DD/MM/YYYY') = to_date('01/01/2020','DD/MM/YYYY');
不过,这些快速技巧中的任何一个都存在问题,即某些未来版本的优化器可能会提供比您现在所知更多的选项,因此您将来可能会遇到问题。
更好的选择是重写查询,这样您就不必关心谓词的计算顺序。在这种情况下(取决于 Oracle 版本),这很容易,因为 to_date
允许您在出现转换错误时指定一个值
select *
from tbl
where col <> 'A'
and to_Date(col default null on conversion error,'DD/MM/YYYY') =
to_date('01/01/2020','DD/MM/YYYY');
如果您使用的是早期版本的 Oracle,或者 to_date
只是实际问题的一个示例,您可以创建一个执行相同操作的自定义函数。
create function safe_to_date( p_str in varchar2, p_fmt in varchar2 )
return date
is
begin
return to_date( p_str, p_fmt );
exception
when value_error
then
return null;
end safe_to_date;
select *
from tbl
where col <> 'A'
and safe_to_date(col,'DD/MM/YYYY') = to_date('01/01/2020','DD/MM/YYYY');