如何在 Oracle SQL 语句中重用动态列?
Posted
技术标签:
【中文标题】如何在 Oracle SQL 语句中重用动态列?【英文标题】:How to reuse dynamic columns in an Oracle SQL statement? 【发布时间】: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 是修正项。
【问题讨论】:
【参考方案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
,效率更高(EXP
和 LOG
成本高)并且更容易调试。
【讨论】:
我不确定这如何适用,但我会记住这一点。 (是否重新评估只是速度问题。)【参考方案6】:别名重用在 Teradata 中很常见,但有时可能会造成混淆,主要是当您使用表/子查询中存在的名称为列名取别名并尝试重用它时,数据库将使用原始列而不是您的列别名。
【讨论】:
以上是关于如何在 Oracle SQL 语句中重用动态列?的主要内容,如果未能解决你的问题,请参考以下文章