Oracle发现了一个非数字字符,其中数字是预期的

时间:2016-06-13 03:54:10

标签: oracle

我有问题,通过提示输入多个日期 我的查询类似

select something
from
something
where
to_date (bus.TDATE) not in (:didb1)

我想输入类似于2016年6月12日',' 2016年6月11日'

我正在做php oracle, 我的代码如下:

select bus.*,con.* from
BusinessPayment bus,
ConsumerPayment con
where bus.TDATE=con.TDATE
and to_date (bus.TDATE) not in (:didbv)
');
$bscode1='11-June-2016','10-June-2016','09-June-2016';
oci_bind_by_name($stid, ':didbv', $bscode1);
oci_execute($stid);

1 个答案:

答案 0 :(得分:0)

您不能使用单个绑定变量来表示in()子句中的值列表。绑定变量不包含多个值;绑定的值被视为单个字符串值。所以你有效地做了:

where
to_date (bus.TDATE) not in (q'['11-June-2016','10-June-2016','09-June-2016']')

如果TDATE已经是一个日期类型 - 希望它是,并且连接建议它可能是 - 那么你不应该通过to_date()传递 - 你正在从日期进行隐式转换使用你的会话的NLS_DATE_FORMAT来字符串,然后半显式转换回日期。这要么是做了不必要的工作,要么是失去了解决方案,这取决于你的格式模型以及日期是否有时间。如果意图忽略值的时间部分,那么有更好的方法来做到这一点。

无论如何,既然这是一个日期,无论它以前是什么类型,那么过滤器的右侧也会被转换为日期,所以你正在做:

where
to_date (bus.TDATE) not in (to_date(q'['11-June-2016','10-June-2016','09-June-2016']'))
无论您的NLS设置是什么,

......以及抛出异常的内容。即使您传递了单个值,由于嵌入的单引号也会出错。你可以用以下内容复制它:

select to_date(q'['11-June-2016','10-June-2016','09-June-2016']') from dual;

也获得ORA-01858:找到了一个非数字字符,其中包含数字。

理想情况下,您可以将各个日期字符串作为数组元素传递,但您也可以使用XML hack解决此问题。您可以将包含逗号分隔值的单个字符串传递给XMLTable(),并将其拆分为行:

select * from xmltable(q'['11-June-2016','10-June-2016','09-June-2016']');

COLUMN_VALUE                                                                   
--------------------------------------------------------------------------------
11-June-2016
10-June-2016
09-June-2016

然后,您可以将每个值转换为日期:

select to_date(column_value, 'DD-Month-YYYY')
from xmltable(q'['11-June-2016','10-June-2016','09-June-2016']')

并在子查询或联接中使用它:

select * from dual
where trunc(sysdate) not in (select to_date(column_value, 'DD-Month-YYYY')
  from xmltable(q'['11-June-2016','10-June-2016','09-June-2016']'));

这适用于字符串绑定变量,因此您的代码将是:

select bus.*,con.*
from BusinessPayment bus
join ConsumerPayment con
on bus.TDATE=con.TDATE
where bus.TDATE not in (select to_date(column_value, 'DD-Month-YYYY')
      from xmltable(:didbv));

我已将其修改为使用ANSI连接语法,这种语法并不相关,但很少有理由使用您所拥有的古老语法。如果TDATE确实包含了您需要稍微修改它的时间 - 截断最简单,但可能会影响性能。

或者你可以加入:

select bus.*,con.*
from xmltable(:didbv) x
join BusinessPayment bus
on bus.TDATE >= to_date(x.column_value, 'DD-Month-YYYY')
and bus.TDATE < to_date(x.column_value, 'DD-Month-YYYY') + 1
join ConsumerPayment con
on bus.TDATE=con.TDATE

在此版本中,TDATE可以有一个时间,并且它会在绑定变量中指定的日期内的任何时间匹配。

最好以不具有月份名称的格式提供日期,因为在不同的会话或区域设置中运行时可能会给您带来问题。如果您知道PHP方将永远是英语,您可以强制识别它:

on bus.TDATE >= to_date(x.column_value, 'DD-Month-YYYY', #NLS_DATE_LANGUAGE=ENGLISH')

但是如果您可以将PHP格式更改为使用月份数,并更改Oracle格式模型以匹配,那将会更简单。