简体   繁体   English

Oracle使用混合varchar列但使用数字where子句对结果进行排序

[英]Oracle ordering of results using a mixed varchar column but numeric where clause

I have a table with a VARCHAR2 column which contains values that are a mixture of pure-numbers and alpha-numerics. 我有一个带有VARCHAR2列的表,该列包含纯数字和字母数字的混合值。 I have a CODE column that contains: 我有一个CODE列,其中包含:

200
215
220
A553
D545
etc.

The following query works: 以下查询有效:

select *
from   TABLE
where  CLASS = 3
AND    (CODE >= 210 and CODE < 220) or CODE = 291)

Values that are CLASS 3 are always numeric. CLASS 3的值始终是数字。

But when I add a ORDER BY , it doesn't work: 但是当我添加ORDER BY ,它不起作用:

select *
from   TABLE
where  CLASS = 3
and    (CODE >= 210 and CODE < 220) or CODE = 291)
ORDER BY CODE

instead I get ORA-01722: invalid number . 相反,我得到ORA-01722: invalid number This seems to be because the Oracle Optimiser is assessing the "order by" before the where clause, and thus non-numeric values get assessed. 似乎是因为Oracle Optimiser正在评估where子句之前的“ order by”,因此会评估非数字值。

I have tried changing it to ORDER BY TO_CHAR(CODE) but to no affect. 我尝试将其更改为ORDER BY TO_CHAR(CODE)但没有任何影响。 Similar negative result with trying to place it all into a sub-query. 尝试将所有内容都放入子查询中的结果类似。

So, how do I order the results of this query by CODE ( ASC ending)? 那么,如何按CODEASC结尾)排序此查询的结果? I guess I can specify all possible CODE values manually in the where clause as strings (ie code = '210' or code = '211' or... ), but is there a more elegant way? 我想我可以在where子句中手动将所有可能的CODE值指定为字符串(即code = '210' or code = '211' or... ),但是还有一种更优雅的方法吗?

The ORDER BY has nothing to do with the problem -- at least not directly. ORDER BY与问题无关-至少不是直接相关。

SQL in general, and Oracle in particular, make no promises about the order of evaluation of conditions in the WHERE clause. 通常,SQL(尤其是Oracle)对WHERE子句中条件的评估顺序不做任何保证。 Hence, the WHERE clause is not (necessarily) evaluated in the order written. 因此, WHERE子句不会(必须)按照编写的顺序求值。 The presence of the ORDER BY might affect the order of evaluation of the conditions in this particular case. 在这种特殊情况下, ORDER BY的存在可能会影响条件评估的顺序。

In general, it is really bad practice to mix data types, the way that you are doing it. 通常,将数据类型混合在一起是一种非常不好的做法。 But, you can guarantee the order of evaluation by using case : 但是,您可以使用case来保证评估的顺序:

select *
from   TABLE
where  CLASS = 3 
       'true' = (case when class <> 3 then 'false'
                      when (CODE >= 210 and CODE < 220) or CODE = 291) then 'true'
                 end);

I do not recommend doing this. 我不建议这样做。 I only want to point out that case does force the order of evaluation of the conditions. 我只想指出,这种case确实会强制评估条件。

The correct solution is to use string comparisons. 正确的解决方案是使用字符串比较。 In this case, I would go with: 在这种情况下,我可以选择:

select *
from   TABLE
where  CLASS = 3 AND
       CODE in ('210', '211', '212', '213', '214', '215', '216', '217', '218', '219', '291')

Alternatively, you could do: 或者,您可以执行以下操作:

where  CLASS = 3 and length(CODE) = 3 and
       ((CODE >= '210' and CODE < '220') or CODE = '291')

Note that for accuracy you do need to take the length into account. 请注意,为确保准确性,您确实需要考虑长度。

The problem can be in your WHERE condition, given that it forces Oracle to cast your code to number; 问题可能出在您的WHERE状态,因为它迫使Oracle将代码强制转换为数字。

Try keeping the WHERE condition in varchar2 format: 尝试将WHERE条件保持为varchar2格式:

with TABLE_(code, class_) as
(
select '200',3 from dual union all
select '215',3 from dual union all
select '220',3 from dual union all
select 'A553',3 from dual union all
select 'D545',3 from dual
)
select *
from   TABLE_
where  CLASS_ = 3
and   ( (CODE >= '210' and CODE < '220') or CODE = '291')
ORDER BY CODE

Since the data type of code column is VARCHAR2 , you must compare it as a string in the where clause not as a number. 由于代码列的数据类型VARCHAR2 ,因此必须在where子句中将它作为字符串进行比较,而不是数字。

Simple error reproduction : 简单的错误再现

SQL> SELECT * FROM DUAL WHERE DUMMY = 10;
SELECT * FROM DUAL WHERE DUMMY = 10
                         *
ERROR at line 1:
ORA-01722: invalid number


SQL>

You need to use single-quotation marks to make it a string and pass the values as IN-list rather than a range . 您需要使用单引号将其设置为字符串,并将值作为IN-list而不是range传递。 Numeric operations on characters will use ASCII value for arithmetic. 对字符的数字运算将使用ASCII值进行算术运算。

For example, 例如,

SQL> WITH sample_data AS(
  2  SELECT '210' code FROM dual UNION ALL
  3  SELECT '2101' code FROM dual UNION ALL
  4  SELECT '220' code FROM dual UNION ALL
  5  SELECT 'A123' code FROM dual
  6  )
  7  --end of sample_data mimicking real table
  8  SELECT code
  9  FROM sample_data
 10  WHERE code IN ('210', '220')
 11  ORDER BY code;

CODE
----
210
220

SQL>

Now coming to the ORDERING of rows as number and not string , you must use TO_NUMBER in the SELECT and use the same alias in the ORDER BY clause. 现在来行作为而不是顺序 ,你必须在使用TO_NUMBER SELECT ,并使用相同的别名在ORDER BY子句。

For example, 例如,

SQL> WITH sample_data AS(
  2  SELECT '210' code FROM dual UNION ALL
  3  SELECT '2101' code FROM dual UNION ALL
  4  SELECT '220' code FROM dual UNION ALL
  5  SELECT 'A123' code FROM dual
  6  )
  7  --end of sample_data mimicking real table
  8  SELECT to_number(code) code_num
  9  FROM sample_data
 10  WHERE code IN ('210', '220')
 11  ORDER BY code_num;

  CODE_NUM
----------
       210
       220
SELECT *
FROM   table_name
WHERE  class = 3
AND    (  ( code >= '210' AND code < '220' AND LENGTH( code ) = 3 )
       OR code = '291' )
ORDER BY CODE; 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM