【问题标题】:ORACLE - Select Count on a SubqueryORACLE - 选择子查询上的计数
【发布时间】:2013-12-21 15:15:14
【问题描述】:

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

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

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

查询 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"

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

【问题讨论】:

    标签: sql oracle count subquery


    【解决方案1】:

    试试这个查询:

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

    首先,您不需要为此目的使用子查询。其次,使用to_number()upper()/lower()容易​​出现其他问题。函数translate() 将第二个参数中的每个字符替换为第三个参数中的值。在这种情况下,它会删除数字。如果什么都没有,那么原始值是一个整数。

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

    【讨论】:

    • Oracle 将一个空字符串表示为NULL,并将其传递给TRANSLATE(),然后为任何输入字符串返回NULL。你必须这样做:RangeA IS NOT NULL AND REPLACE( TRANSLATE(RangeA, '0123456789', '0000000000'), '0', '' ) IS NULL.
    • @MT0 - 获得正确结果的更好方法是(没有REPLACE):TRANSLATE(rangeA, 'z0123456789', 'z') IS NULL
    【解决方案2】:

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

    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;
    

    【讨论】:

      【解决方案3】:

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

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

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

      【讨论】:

        猜你喜欢
        • 2021-10-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-11-11
        • 1970-01-01
        • 2021-09-27
        • 1970-01-01
        • 2021-03-26
        相关资源
        最近更新 更多