【问题标题】:How to reuse dynamic columns in an Oracle SQL statement?如何在 Oracle SQL 语句中重用动态列?
【发布时间】:2009-04-13 12:35:33
【问题描述】:

我尝试重用一些我在 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

顺便说一句,如果有人感兴趣,SB 是星系的“表面亮度”,B 和 D 是修正项。

【问题讨论】:

    标签: sql oracle ora-00904


    【解决方案1】:

    我们在 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
    

    这是最易读、最易维护、最易遵循的版本。

    对于更新,有一个使用 column_name = 表示法的已弃用 SQL Server 解决方法,您可以在其中引用列表中先前已更新的列。但这不能在 SELECT 中使用。

    我希望将来某个时候将一些堆叠表达式的功能(不使用标量 UDF)添加到 ANSI SQL。

    【讨论】:

    • 哈哈,不过确实有副作用!因为我第一次使用“T”和“S”作为我的新列,它们确实存在!所以没有错误,只有完全不正确的值。 (200 多列,全部 2-5 个字符...)
    • 结果在列顺序变化下是不变的。希望 ANSI 能够以某种内联堆栈指令的形式提供一些缓解。
    【解决方案2】:

    我不确定您是否可以做到这一点(我从未见过这样做过),但您可以通过以下方式解决它:

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

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

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

    SELECT P, Q FROM viewintotablename
    

    【讨论】:

    • 是的,我把示例中的查询做得很简单,实际上我多次使用值 P 来计算其他列,而 P 稍微复杂一些。我会更新问题。
    • 我不完全确定您为什么如此关注查询。它们往往只写一次然后就不管了,所以它们有多“丑陋”并不重要(假设真正丑陋的有正确的记录:-)。
    • 我知道 DBA 所关心的唯一 是原始速度,我认为如果不进行非规范化,您将无法轻松修复(即,在 DB2 中引入生成的列)。
    • 我关心的是可读性和正确性,而不是速度。但我的主要目标是学习,我想看看我是否可以将一些简单的计算卸载到数据库中。
    • 视图会这样做,存储过程也会这样做。对于简单的情况,您会发现视图可能更容易。如果您按 A 排序,则可以优化存储过程以使用某些列中的前行数据(假设 A 相同)。我在 DB2 中做过这个,Oracle 我不知道。
    【解决方案3】:

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

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

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

    对于 P 和 Q,这并不(很多)比原来的好,但如果函数很复杂,并且不需要太多参数,它可能会使您的语句更具可读性。

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

    【讨论】:

    • 我以前从未在 Oracle 中创建过函数,显然我没有足够的权限这样做。 (我不会经常需要这些查询来保证 DB 范围的功能。)
    【解决方案4】:

    你可能会比你给出的内联视图示例更喜欢这个:

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

    我认为它相当于同一件事,但阅读起来更清晰一些。

    如果计算列是您将在多个列中使用的东西,那么创建一个永久视图可能是有意义的。

    Oracle 11(我还没用过)有一个虚拟列功能,可能对你有用。

    【讨论】:

    • 确实更具可读性。我相信它是一个 11g 的数据库,但我不是 DBA,我似乎需要为虚拟列。
    【解决方案5】:

    你不能。

    如果您不希望重新评估子查询,请为子查询添加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 成本高)并且更容易调试。

    【讨论】:

    • 我不确定这如何适用,但我会记住这一点。 (是否重新评估只是速度问题。)
    【解决方案6】:

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

    【讨论】:

      猜你喜欢
      • 2016-09-10
      • 1970-01-01
      • 1970-01-01
      • 2016-09-14
      • 2017-03-22
      • 1970-01-01
      • 2021-01-28
      • 2017-09-26
      • 1970-01-01
      相关资源
      最近更新 更多