在 Oracle 中使用类似于数值的字符串值
use string value like numeric value in Oracle
我的 oracle 查询中有一个条件:
AND a.ACCARDACNT > '0880080200000006' and a.ACCARDACNT < '0880080200001000'
table 中 ACCARDACNT 列的类型是 varchar
并已编入索引,但在那种情况下我想将其用作数字。当我执行此查询时,执行计划显示 CBO 可以使用索引并按索引扫描 table。
是真的吗?
我想将它们作为数字来使用和比较,还想使用索引。有什么解决办法吗?
您当前的查询应该可以使用索引。但问题是您正在比较文本,但期望它按数字排序。通常,它可能不会,因为文本按 SQL 中的字典顺序排序(即按字典顺序)。因此,要获得正确的排序行为,您必须将 ACCARDACNT
转换为数字:
AND CAST(LTRIM(a.ACCARDACNT, '0') AS FLOAT) BETWEEN 880080200000007 AND 880080200000999
认为不能同时使用数值比较和索引。
the execution plan shows that CBO can use index
此处可能使用了全索引扫描,因此只是table扫描但列数较少。
可能的方法是将数字转换为带前导零的固定长度字符串,然后使用 1 进行比较。在这种情况下,将使用索引。
如果保证ACCARDACNT
都是数字,那就用
and to_number(a.accardacnt) > 880080200000006 and a.accardacnt < 880080200001000;
这确保数字不会作为 '2' > '10'
处的字符串进行比较,因为查看第一个字符“2”大于“1”。
(如果是小数,请确保存储在字符串中的小数分隔符与当前会话设置匹配。)
如果要为此提供索引,请使用此函数索引:
create index idx_accardacnt on mytable( to_number(accardacnt) );
或包含 to_number(accardacnt)
的复合索引。由于字符串查询的执行计划显示要使用的索引,因此数字比较和函数索引也应如此。 (请记住,DBMS 可以自由使用或不使用提供的索引。我们只是提供它们,但 DBMS 最清楚在查询中使用它们是否有意义。)
另一个选项仍然是计算列:
alter table mytable add accardacnt_num as (to_number(accardacnt));
并提供一个或多个包含此列的索引:
create index idx_accardacnt_num on mytable(accardacnt_num);
然后现有代码继续工作,但新查询可以从数字列中受益:
and a.accardacnt_num > 880080200000006 and a.accardacnt_num < 880080200001000;
我认为以下逻辑可以满足您的需求:
a.ACCARDACNT > '0880080200000006' and
a.ACCARDACNT < '0880080200001000' and
length(ACCARDACNT) = 16
此外,如果有合适的索引可用,这可以在列上使用索引。
如果您希望 15 个字符的帐号 '880080200000060'
符合您的条件,这将是不正确的。我猜你不想要这个。
您必须在列上创建函数索引:accardacnt
和
create index idx_fn_accardant on table_name( to_number(accardacnt) );
在query查询的where子句中将其转化为数字:
where to_number(ACCARDACNT) > 0880080200000006 and to_number(ACCARDACNT) < 0880080200001000
我的 oracle 查询中有一个条件:
AND a.ACCARDACNT > '0880080200000006' and a.ACCARDACNT < '0880080200001000'
table 中 ACCARDACNT 列的类型是 varchar
并已编入索引,但在那种情况下我想将其用作数字。当我执行此查询时,执行计划显示 CBO 可以使用索引并按索引扫描 table。
是真的吗?
我想将它们作为数字来使用和比较,还想使用索引。有什么解决办法吗?
您当前的查询应该可以使用索引。但问题是您正在比较文本,但期望它按数字排序。通常,它可能不会,因为文本按 SQL 中的字典顺序排序(即按字典顺序)。因此,要获得正确的排序行为,您必须将 ACCARDACNT
转换为数字:
AND CAST(LTRIM(a.ACCARDACNT, '0') AS FLOAT) BETWEEN 880080200000007 AND 880080200000999
认为不能同时使用数值比较和索引。
the execution plan shows that CBO can use index
此处可能使用了全索引扫描,因此只是table扫描但列数较少。
可能的方法是将数字转换为带前导零的固定长度字符串,然后使用 1 进行比较。在这种情况下,将使用索引。
如果保证ACCARDACNT
都是数字,那就用
and to_number(a.accardacnt) > 880080200000006 and a.accardacnt < 880080200001000;
这确保数字不会作为 '2' > '10'
处的字符串进行比较,因为查看第一个字符“2”大于“1”。
(如果是小数,请确保存储在字符串中的小数分隔符与当前会话设置匹配。)
如果要为此提供索引,请使用此函数索引:
create index idx_accardacnt on mytable( to_number(accardacnt) );
或包含 to_number(accardacnt)
的复合索引。由于字符串查询的执行计划显示要使用的索引,因此数字比较和函数索引也应如此。 (请记住,DBMS 可以自由使用或不使用提供的索引。我们只是提供它们,但 DBMS 最清楚在查询中使用它们是否有意义。)
另一个选项仍然是计算列:
alter table mytable add accardacnt_num as (to_number(accardacnt));
并提供一个或多个包含此列的索引:
create index idx_accardacnt_num on mytable(accardacnt_num);
然后现有代码继续工作,但新查询可以从数字列中受益:
and a.accardacnt_num > 880080200000006 and a.accardacnt_num < 880080200001000;
我认为以下逻辑可以满足您的需求:
a.ACCARDACNT > '0880080200000006' and
a.ACCARDACNT < '0880080200001000' and
length(ACCARDACNT) = 16
此外,如果有合适的索引可用,这可以在列上使用索引。
如果您希望 15 个字符的帐号 '880080200000060'
符合您的条件,这将是不正确的。我猜你不想要这个。
您必须在列上创建函数索引:accardacnt
和
create index idx_fn_accardant on table_name( to_number(accardacnt) );
在query查询的where子句中将其转化为数字:
where to_number(ACCARDACNT) > 0880080200000006 and to_number(ACCARDACNT) < 0880080200001000