4

我尝试重用我在 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”(我现在可以将此查询作为子查询/带有子句)。


更新:好的,我想现在我完成了 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 是修正项。

4

6 回答 6

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 中使用。

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

于 2009-04-13T13:46:19.887 回答
0

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

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

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

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

SELECT P, Q FROM viewintotablename
于 2009-04-13T12:40:12.417 回答
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

,这更有效(EXP并且LOG成本更高)并且更容易调试。

于 2009-04-13T12:46:35.833 回答
0

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

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

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

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

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

于 2009-04-13T12:57:47.180 回答
0

您可能会比您提供的内联视图示例更喜欢这个:

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

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

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

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

于 2009-04-13T13:03:01.520 回答
0

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

于 2019-04-21T17:28:25.793 回答