如何将代表数字的NCHAR值与实际NUMBER进行比较?

时间:2015-06-24 15:26:59

标签: sql oracle oracle11g

我有一个表,每次订单更新时都包含条目;它有点像审计日志。我有另一个表存储我查看的最后一批ID。

我希望查询只返回审计表中的值,该值的批次ID大于我存储的批次ID。

我的问题是我的审核表(F4201z1.SYEDBT)中保存批次ID的列是NCHAR(15)数据类型,而另一个表(F0002.NNN005)中保存最后一批ID的列是NUMBER类型。

Select SYDOCO from crpdta.F4201z1 
where SYEDBT >
  (Select NNN005 from crpCTL.F0002 where NNSY = '55')
and SYTYTN = 'JDESOOUT'

以上是我第一次在查询中运行,它从我的测试数据库返回预期的6个结果,但需要30秒。这太长了,因为生产中的表会很快增长。

Select SYDOCO from crpdta.F4201z1
where SYEDBT >
  (Select Cast(NNN005 as Nchar(15)) as NNN005 from crpCTL.F0002 where NNSY = '55')
and SYTYTN = 'JDESOOUT'

这是我的第二个破解,并在0.39秒内返回最初的50个结果,但它返回总计220k的结果,这是不对的。

到目前为止我所知道的是SYEDBT是一个索引列。在上面的第一个代码示例中,它不会使用该索引,并且需要40秒左右进行全表扫描。

在第二个代码示例中,它使用了索引,但是它正在比较NCHAR值,因此它在the Oracle rules之外工作,它一次比较一个字符,并在它指定的字符集中遇到的第一个差异是'更大'。

有没有办法比较这两个值,同时仍然使用F4201z1表上的索引来保持时间,而不改变我正在使用的表的结构?

2 个答案:

答案 0 :(得分:2)

您的第二个查询正在点击现有索引,但正如您所意识到的那样使用字符比较。虽然你可以操纵SYEDBT值 - 如果不是通过转换为数字,那么可能修剪和左边填充空格,这也是丑陋的 - 这将阻止索引被使用,并且还将恢复为全表扫描。

在将转换后的值与NNN005数值进行比较之前,您的第一个查询是隐式将每个SYEDBT值从nchar转换为数字。它正在进行全表扫描。

真正避免这种情况的唯一方法是,如果你不能改变表结构,就是添加一个基于函数的索引:

create index F4201z1_fbi on F4201z1(to_number(SYEDBT));

然后,您可以使用匹配的显式转换进行查询:

Select SYDOCO from F4201z1
where to_number(SYEDBT) > (Select NNN005 from F0002 where NNSY = '55')
and SYTYTN = 'JDESOOUT';

这会打到你的新索引。

如果您在SYEDBT中存储不同类型的数据,那么您不应该您可能具有无法转换为数字的值,这使得索引和查询失败并使用ORA-01722。您可以使索引更具选择性:

create index F4201z1_fbi on F4201z1(
  case when SYTYTN = 'JDESOOUT' then to_number(SYEDBT) end);

其中的值仅针对特定的SYTYTN进行转换(如果您有其他数值,则可能需要查看其中一个);但是,这也必须在查询中完全重复:

Select SYDOCO from F4201z1
where case when SYTYTN = 'JDESOOUT' then to_number(SYEDBT) end >
  (Select NNN005 from F0002 where NNSY = '55')
and SYTYTN = 'JDESOOUT';

如果存在更多“数量”情况,哪个更加混乱,并且更改索引以添加新的情况将意味着还必须修改依赖于索引的所有查询。使用相同的逻辑向表中添加虚拟列,然后索引该虚拟列将更加清晰。但这会改变表结构。

答案 1 :(得分:0)

为了能够使用SYEDBT列上的索引,比较值必须是相同的类型(即NUMBER)。在子查询中使用to_number转换:

(Select  to_number(NNN005) from crpCTL.F0002 ...