Oracle 使用混合 varchar 列和数字 where 子句对结果进行排序
Oracle ordering of results using a mixed varchar column but numeric where clause
我有一个 table 和一个 VARCHAR2
列,其中包含纯数字和字母数字混合的值。我有一个 CODE
列,其中包含:
200
215
220
A553
D545
etc.
以下查询有效:
select *
from TABLE
where CLASS = 3
AND (CODE >= 210 and CODE < 220) or CODE = 291)
CLASS3 的值始终是数字。
但是当我添加 ORDER BY
时,它不起作用:
select *
from TABLE
where CLASS = 3
and (CODE >= 210 and CODE < 220) or CODE = 291)
ORDER BY CODE
相反,我得到 ORA-01722: invalid number
。这 seems to be 因为 Oracle 优化器正在评估 where 子句之前的 "order by",因此评估了非数字值。
我试过将其更改为 ORDER BY TO_CHAR(CODE)
但没有任何影响。与尝试将其全部放入子查询类似的负面结果。
那么,如何按 CODE
(ASC
结尾)对这个查询的结果进行排序?我想我可以在 where 子句中手动指定所有可能的 CODE
值作为字符串(即 code = '210' or code = '211' or...
),但是有没有更优雅的方法?
问题可能出在您的 WHERE
条件下,因为它强制 Oracle 将您的代码转换为数字;
尝试将 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
由于code列的数据类型为VARCHAR2,所以必须在where
子句中将其作为字符串进行比较不是一个数字。
简单错误复现:
SQL> SELECT * FROM DUAL WHERE DUMMY = 10;
SELECT * FROM DUAL WHERE DUMMY = 10
*
ERROR at line 1:
ORA-01722: invalid number
SQL>
您需要使用 单引号 使其成为字符串并将值作为 IN-list 而不是 范围。字符的数字运算将使用 ASCII 值进行算术运算。
例如,
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>
现在进入 ORDERING 行 number 而不是 string,你必须在 SELECT
中使用 TO_NUMBER 并在 ORDER BY
子句中使用相同的别名。
例如,
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;
ORDER BY
与问题无关 -- 至少不是直接的。
SQL 通常,特别是 Oracle,不对 WHERE
子句中的条件评估顺序做出任何承诺。因此,WHERE
子句不会(必然)按照写入的顺序求值。 ORDER BY
的存在可能会影响此特定情况下条件的评估顺序。
一般来说,混合数据类型确实是一种不好的做法。但是,您可以使用 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);
我不建议这样做。我只想指出 case
确实强制条件的评估顺序。
正确的解决方案是使用字符串比较。在这种情况下,我会选择:
select *
from TABLE
where CLASS = 3 AND
CODE in ('210', '211', '212', '213', '214', '215', '216', '217', '218', '219', '291')
或者,您可以这样做:
where CLASS = 3 and length(CODE) = 3 and
((CODE >= '210' and CODE < '220') or CODE = '291')
请注意,为了确保准确性,您确实需要考虑长度。
我有一个 table 和一个 VARCHAR2
列,其中包含纯数字和字母数字混合的值。我有一个 CODE
列,其中包含:
200
215
220
A553
D545
etc.
以下查询有效:
select *
from TABLE
where CLASS = 3
AND (CODE >= 210 and CODE < 220) or CODE = 291)
CLASS3 的值始终是数字。
但是当我添加 ORDER BY
时,它不起作用:
select *
from TABLE
where CLASS = 3
and (CODE >= 210 and CODE < 220) or CODE = 291)
ORDER BY CODE
相反,我得到 ORA-01722: invalid number
。这 seems to be 因为 Oracle 优化器正在评估 where 子句之前的 "order by",因此评估了非数字值。
我试过将其更改为 ORDER BY TO_CHAR(CODE)
但没有任何影响。与尝试将其全部放入子查询类似的负面结果。
那么,如何按 CODE
(ASC
结尾)对这个查询的结果进行排序?我想我可以在 where 子句中手动指定所有可能的 CODE
值作为字符串(即 code = '210' or code = '211' or...
),但是有没有更优雅的方法?
问题可能出在您的 WHERE
条件下,因为它强制 Oracle 将您的代码转换为数字;
尝试将 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
由于code列的数据类型为VARCHAR2,所以必须在where
子句中将其作为字符串进行比较不是一个数字。
简单错误复现:
SQL> SELECT * FROM DUAL WHERE DUMMY = 10;
SELECT * FROM DUAL WHERE DUMMY = 10
*
ERROR at line 1:
ORA-01722: invalid number
SQL>
您需要使用 单引号 使其成为字符串并将值作为 IN-list 而不是 范围。字符的数字运算将使用 ASCII 值进行算术运算。
例如,
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>
现在进入 ORDERING 行 number 而不是 string,你必须在 SELECT
中使用 TO_NUMBER 并在 ORDER BY
子句中使用相同的别名。
例如,
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;
ORDER BY
与问题无关 -- 至少不是直接的。
SQL 通常,特别是 Oracle,不对 WHERE
子句中的条件评估顺序做出任何承诺。因此,WHERE
子句不会(必然)按照写入的顺序求值。 ORDER BY
的存在可能会影响此特定情况下条件的评估顺序。
一般来说,混合数据类型确实是一种不好的做法。但是,您可以使用 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);
我不建议这样做。我只想指出 case
确实强制条件的评估顺序。
正确的解决方案是使用字符串比较。在这种情况下,我会选择:
select *
from TABLE
where CLASS = 3 AND
CODE in ('210', '211', '212', '213', '214', '215', '216', '217', '218', '219', '291')
或者,您可以这样做:
where CLASS = 3 and length(CODE) = 3 and
((CODE >= '210' and CODE < '220') or CODE = '291')
请注意,为了确保准确性,您确实需要考虑长度。