【问题标题】:Oracle: join on integer value of fieldOracle:加入字段的整数值
【发布时间】:2012-12-06 12:00:58
【问题描述】:

我意识到我正在使用的数据库中的数据结构还有很多不足之处,但我无法控制,所以请耐心等待。

我有一个字段ADDRESSES.COMPANY,它可能是也可能不是整数ID。有时它是一个字符串。但是,当它是整数时,我想加入另一个表COMPANIES。原始查询(预加入)如下所示:

SELECT *
  FROM (SELECT ROWNUM AS ROW_NUM, INNERQUERY.*
          FROM (  SELECT *
                    FROM ADDRESSES, COMPANIES
                ) INNERQUERY) OUTERQUERY
 WHERE ROW_NUM <= 50 AND ROW_NUM > 0

如果我尝试正常加入,我会收到错误ORA-01722: invalid number...,原因很明显(尝试加入的数字并不总是一个数字,或者至少我认为这就是它的含义,因为它是真的!) .我尝试加入后的查询:

SELECT *
  FROM (SELECT ROWNUM AS ROW_NUM, INNERQUERY.*
          FROM (  SELECT *
                    FROM ADDRESSES, COMPANIES
                    WHERE ADDRESSES.COMPANY(+) = COMPANIES.ID
                ) INNERQUERY) OUTERQUERY
 WHERE ROW_NUM <= 50 AND ROW_NUM > 0

我能想到的唯一两个潜在解决方案都是使用 TO_NUMBER() 函数,我认为它会将非数字字符串评估为 0。我试过了:

SELECT *
  FROM (SELECT ROWNUM AS ROW_NUM, INNERQUERY.*
          FROM (  SELECT *
                    FROM ADDRESSES, COMPANIES
                    WHERE TO_NUMBER(ADDRESSES.COMPANY)(+) = COMPANIES.ID
                ) INNERQUERY) OUTERQUERY
 WHERE ROW_NUM <= 50 AND ROW_NUM > 0

ORA-00936: missing expression 有哪些错误(第 7 行)。我假设我对使用它的适当位置的猜测不正确,并尝试了以下方法:

SELECT *
  FROM (SELECT ROWNUM AS ROW_NUM, INNERQUERY.*
          FROM (  SELECT *, TO_NUMBER(ADDRESSES.COMPANY) AS COMPANYID
                    FROM ADDRESSES, COMPANIES
                    WHERE COMPANYID(+) = COMPANIES.ID
                ) INNERQUERY) OUTERQUERY
 WHERE ROW_NUM <= 50 AND ROW_NUM > 0

第 3 行的 ORA-00923: FROM keyword not found where expected 有哪些错误。

当我的第一次尝试没有成功时,我确信我的第二次会成功。但我对这个错误感到困惑。我对 Oracle 语法和/或行为完全一无所知,还是别的什么? 有没有办法解决我的问题?

【问题讨论】:

标签: sql oracle join integer


【解决方案1】:

1.

WHERE TO_NUMBER(ADDRESSES.COMPANY)(+) = COMPANIES.ID

你不能这样做。

(+) 运算符只能应用于列,不能应用于任意 表达。但是,任意表达式可以包含一个或多个 用 (+) 运算符标记的列。

JOINS

2.

SELECT *, TO_NUMBER(ADDRESSES.COMPANY) AS COMPANYID
  FROM ADDRESSES, COMPANIES
 WHERE COMPANYID(+) = COMPANIES.ID

您不能在定义此别名的同一范围内按别名引用列(除非它是 order by 子句)。

在这种情况下执行外连接的正确方法是使用 ansi join 语法:

select * 
  from companies
  left join addresses on companies.id = addresses.companyid

在字符串列上使用to_number 没有多大意义,因为它会在连接期间遇到的第一个非数字值上产生相同的无效数字错误。

不过,您可以尝试按照建议将数字转换为字符。

  select * 
      from companies
       left join addresses 
         on to_char(companies.id) = addresses.companyid

但您必须记住,它可能会降低您的性能,因为您将无法在转换列时使用索引。

【讨论】:

    【解决方案2】:

    如果只是将COMPANIES.ID 转换为varchar 并根据字符串而不是整数连接这些表。

    SELECT *
      FROM (SELECT ROWNUM AS ROW_NUM, INNERQUERY.*
              FROM (  SELECT *
                        FROM ADDRESSES, COMPANIES
                        WHERE 
                        ADDRESSES.COMPANY(+) = CAST (COMPANIES.ID AS VARCHAR(100))
                    ) INNERQUERY) OUTERQUERY
     WHERE ROW_NUM <= 50 AND ROW_NUM > 0
    

    【讨论】:

      【解决方案3】:

      我推断 ADDRESSES.COMPANY 是字符串类型(varchar2()、char()),而 COMPANIES.ID 是数字类型(数字、整数、...)。

      您不能总是将字符串转换为数字,因此 to_number() 是不可靠的。 但是您可以将每个数字转换为字符串:

      WHERE ADDRESSES.COMPANY(+) = to_char(COMPANIES.ID)
      

      当然,如果 ADDRESSES.COMPANY 中的数字以特定方式格式化,您必须使用相同的格式来转换 COMPANIES.ID

      WHERE ADDRESSES.COMPANY(+) = to_char(COMPANIES.ID,'fm00009')
      

      fm00009 表示左填充零且没有前导空格(对于正数)。

      如果 COMPANIES.ID 上有索引,优化器将不会使用它来解析查询,因为谓词涉及 COMPANIES.ID 的函数。一种解决方法是在 to_char(COMPANIES.ID,'fm00009') 上创建一个索引(所谓的基于函数的索引)。

      另一种方法是使用 case 开关来决定何时可以将 COMPANY 转换为数字

      SELECT ...
      FROM
        (select ADDRESSES.*,
           case
             when translate(COMPANY,'X0123456789','X') is null -- base 10 non negative integer
             then to_number(COMPANY)
             else null
           end as COMPANY_N
         from ADDRESSES) as a,
        COMPANIES
      where a.COMPANY_N(+) = COMPANIES.ID
      

      不会使用 ADDRESSES.COMPANY 上的索引。

      【讨论】:

        猜你喜欢
        • 2016-01-31
        • 2013-05-26
        • 2015-07-06
        • 1970-01-01
        • 1970-01-01
        • 2011-10-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多