如何在Oracle SQL语句中重用动态列?

时间:2009-04-13 12:35:33

标签: sql oracle ora-00904

我尝试重用一些我在Oracle SQL中动态计算的列,比如

SELECT
    A*2 AS P,
    P+5 AS Q
FROM tablename

其中'tablename'有一个名为'A'的列,但没有其他列。这给了我一个

ORA-00904: "P": invalid identifier

我知道如何通过使用像

这样的子查询来解决这个问题
SELECT P, P+5 AS Q
FROM ( SELECT A*2 AS P FROM tablename )

但我认为这有点难看。此外,我想使查询更复杂,例如重用'Q',我不想创建另一个子查询。

更新:我想存储'P'计算的原因是我想让它更复杂,并多次重复使用'P'。所以我不想明确地说'A * 2 + 5 AS Q',因为随着'P'越来越复杂,这很快会变得很麻烦。

必须有一个很好的方法来做到这一点,任何想法?

更新:我应该注意到我不是数据库管理员:(。

<小时/> 更新:一个真实的例子,具有更具体的查询。我想做的是:

SELECT 
    SL/SQRT(AB) AS ALPHA,
    5*LOG(10,ALPHA) AS B,
    2.5*LOG(10,1-EXP(-5/ALPHA)*(5/ALPHA+1)) AS D
    BS -2.74 + B + D AS BSA
FROM tablename

现在,我已经把它写出来了,但是很难看:

SELECT
    SL/SQRT(AB) AS ALPHA,
    5*LOG(10,SL/SQRT(AB)) AS B,
    2.5*LOG(10,1-EXP(-5/(SL/SQRT(AB)))*(5/(SL/SQRT(AB))+1)) AS D
    BS -2.74 + 5*LOG(10,SL/SQRT(AB)) + 2.5*LOG(10,1-EXP(-5/(SL/SQRT(AB)))*((5/(SL/SQRT(AB)))+1)) AS BSA
FROM tablename

我可以在收到数据后完成所有这些操作,但我想,让我们看看我能让数据库做多少。另外,我也想选择'BSA'(我现在可以用这个查询作为子查询/ with-clause)。


更新:好的,我想现在我已经完成了Cade Roux和Dave Costa的解决方案。虽然Pax'和Jens Schauder的解决方案看起来会更好,但我不能使用它们,因为我不是DBA。现在我不知道将谁标记为最佳答案:)。

WITH 
  A1 AS ( 
    SELECT A0.*, 
    SL/SQRT(AB) AS ALPHA
    FROM tablename A0
  ),
  A2 AS (
    SELECT A1.*, 
    5*LOG(10,ALPHA) AS B,
    2.5*LOG(10,1-EXP(-5/ALPHA)*((5/ALPHA)+1)) AS D
    FROM A1
  )
SELECT
  ALPHA, B, D, BS,
  BS -2.74 + B + D AS BSA
FROM A2

BTW,如果有人感兴趣,SB是星系的“表面亮度”,其中B和D是校正项。

6 个答案:

答案 0 :(得分:2)

我们在SQL Server中遇到同样的问题(这是一个ANSI问题)。我相信它旨在避免混淆别名效果:

SELECT A * 2 AS A
    ,A * 3 AS B -- This is the original A, not the new A
FROM whatever

我们通过堆叠公用表表达式来解决它:

WITH A1 AS (
    SELECT A * 2 AS A
    FROM whatever
)
,A2 AS (
    SELECT A1.*
        ,A * 3 AS B
    FROM A1
)
,A3 AS (
    SELECT A2.*
        ,A + B AS X
    FROM A2
)
SELECT *
FROM A3

这是最具可读性和维护性且可以跟随的版本。

对于UPDATE,使用column_name = notation有一个不推荐使用的SQL Server变通方法,您可以在其中引用先前已在列表中更新的列。但这不能在SELECT中使用。

我希望将来某些时候可以在ANSI SQL中添加一些堆栈表达式(不使用标量UDF)的能力。

答案 1 :(得分:0)

我不确定你能做到这一点(我从未见过它)但你可以解决这个问题:

SELECT
    A*2   AS P,
    A*2+5 AS Q
FROM tablename

这肯定比引入子查询更好。

我建议的另一种方法是创建一个视图,为您提供P / Q类型列(使用上面的公式),这至少可以简化查询的文本。然后你可以:

SELECT P, Q FROM viewintotablename

答案 2 :(得分:0)

你不能。

如果您不希望重新评估子查询,请为子查询添加NO_MERGE提示:

此子查询将在嵌套循环中重新评估(使用MERGE提示):

SELECT  /*+ LEADING(g) USE_NL(g, r) MERGE(g) */
        *
FROM    (
        SELECT  1
        FROM    dual
        UNION ALL
        SELECT  2
        FROM    dual
        ) r, 
        (
        SELECT  SYS_GUID() AS guid
        FROM    dual d
        ) g

---
33CA48C1AB6B4403808FB0219302CE43
711BB04F9AFC406ABAEF8A8F4CFA1266

此子查询不会在嵌套循环中重新评估(使用NO_MERGE提示):

SELECT  /*+ LEADING(g) USE_NL(g, r) NO_MERGE(g) */
        *
FROM    (
        SELECT  1
        FROM    dual
        UNION ALL
        SELECT  2
        FROM    dual
        ) r, 
        (
        SELECT  SYS_GUID() AS guid
        FROM    dual d
        ) g

------
7715C69698A243C0B379E68ABB55C088
7715C69698A243C0B379E68ABB55C088

在你的情况下,只需写下:

SELECT  BS - 2.74 + d
FROM    (
        SELECT  t2.*, 2.5 * LOG(10, 1 - EXP(-5 / b)) * ((5 / A) + 1) AS d
        FROM    (
                SELECT  t1.*, 5 * LOG(10, alpha) AS b
                FROM    (
                        SELECT  /*+ NO_MERGE */ t.*,
                                SL/SQRT(AB) AS alpha
                        FROM    tablename t
                        ) t1
                ) t2
        ) t3

,效率更高(EXPLOG代价高昂)并且更容易调试。

答案 3 :(得分:0)

在sql中没有直接的方法可以做到这一点。

但您可以使用PL / SQL定义一个函数。所以你的选择看起来像这样

select 
    P(A), 
    Q(P(A)) 
from tablename

对于P和Q,这并不比原版更好,但是如果函数很复杂,并且没有很多参数,那么它可能会使你的语句更具可读性。

它还允许您独立于sql语句和任何数据测试函数。

答案 4 :(得分:0)

您可能比您提供的内联视图示例更好一点:

WITH inner_view AS ( SELECT A*2 AS P FROM tablename )
SELECT P, P+5 AS Q
FROM inner_view

这相当于同样的事情,但我认为它更清晰一点。

如果计算列是您将在多列中使用的内容,则创建永久视图可能是有意义的。

Oracle 11(我尚未使用)具有可能对您有用的虚拟列功能。

答案 5 :(得分:0)

别名重用在Teradata中很常见,但是有时可能会造成混淆,主要是当您使用表/子查询中存在的名称为列名加上别名并尝试重用时,数据库将使用原始列而不是您使用的列别名。