ORACLE - 选择子查询上的Count

时间:2013-12-04 22:10:34

标签: sql oracle count subquery

我有一个包含一组范围(RangeA和RangeB)的Oracle表。这些列是varchar,因为它们可以包含数字和字母数字值,如下例所示:

ID|RangeA|RangeB
1 |   10 |   20
2 |   21 |   30
3 | AB50 | AB70
4 | AB80 | AB90

我需要执行一个只返回具有数值的记录的查询,并对该查询执行Count。到目前为止,我已经尝试过两个不同的查询,但没有任何运气:

查询1:

SELECT COUNT(*) FROM (
SELECT RangeA, RangeB FROM table R
WHERE upper(R.RangeA) = lower(R.RangeA)
) A
WHERE TO_NUMBER(A.RangeA) <= 10

查询2:

WITH A(RangeA,RangeB) AS(
SELECT RangeA, RangeB FROM table 
WHERE upper(RangeA) = lower(RangeA)
)
SELECT COUNT(*) FROM A WHERE TO_NUMBER(A.RangeA) <= 10

子查询工作正常,因为我得到的两个记录只有数字值,但查询的COUNT部分失败了。我应该只计算1,但我得到以下错误:

ORA-01722: invalid number
01722. 00000 -  "invalid number"

我做错了什么?非常感谢任何帮助。

3 个答案:

答案 0 :(得分:3)

尝试此查询:

  SELECT COUNT(*)
  FROM table R
  WHERE translate(R.RangeA, 'x0123456789', 'x') = 'x' and
        translate(R.RangeB, 'x0123456789', 'x') = 'x'

首先,您不需要子查询来实现此目的。其次,使用to_number()upper() / lower()会遇到其他问题。函数translate()用第三个参数中的值替换第二个参数中的每个字符。在这种情况下,它会删除数字。如果没有剩余,则原始值为整数。

您可以对负值和浮点数进行更复杂的检查,但问题中的示例似乎是关于正整数值。

答案 1 :(得分:3)

您可以使用正则表达式测试每个列,以确定它是否为有效数字:

SELECT COUNT(1)
FROM   table_of_ranges
WHERE  CASE WHEN REGEXP_LIKE( RangeA, '^-?\d+(\.\d*)?$' )
            THEN TO_NUMBER( RangeA )
            ELSE NULL END
          < 10
AND    REGEXP_LIKE( RangeB, '^-?\d+(\.\d*)?$' );

另一种选择是使用用户定义的函数:

CREATE OR REPLACE FUNCTION test_Number (
  str VARCHAR2
) RETURN NUMBER DETERMINISTIC
AS
  invalid_number EXCEPTION;
  PRAGMA EXCEPTION_INIT(invalid_number, -6502);
BEGIN
  RETURN TO_NUMBER( str );
EXCEPTION
  WHEN invalid_number THEN
    RETURN NULL;
END test_Number;
/

然后你可以这样做:

SELECT COUNT(*)
FROM   table_of_ranges
WHERE  test_number( RangeA ) <= 10
AND    test_number( RangeB ) IS NOT NULL;

答案 2 :(得分:0)

差不多四年后来到这个问题(显然,这是从一个更新的主题中指出)。其他答案显示了如何实现所需的输出,但没有回答OP的问题,即“我做错了什么?

你没有做错任何事。甲骨文做错了什么。它将谓词(WHERE条件)从外部查询“推送”到内部查询中。推送谓词是优化程序使查询更有效的最基本方法之一,但在某些情况下(并且您提出的问题是完美的插图),实际上,结果在逻辑上并不等同于原始查询。

有一些方法可以阻止优化器推送谓词;或者你可以用更好的方式编写查询(如其他答案所示)。但如果你想知道为什么你看到你所看到的,这就是原因。