【问题标题】:TABLE/CAST/MULTISET vs subquery in FROM clauseFROM 子句中的 TABLE/CAST/MULTISET 与子查询
【发布时间】:2015-06-02 14:19:00
【问题描述】:

以下查询不起作用。预计会失败,因为 temp.col 引用了在该上下文中不可用的内容。

with temp as  (
       select 'A' col from dual
       union all
       select 'B' col from dual
     )
select *
from temp,
     (select level || temp.col from dual connect by level < 3);

来自 Oracle 的错误消息是:ORA-00904: "TEMP"."COL": invalid identifier

但为什么下一个查询有效?我认为 CAST/MULTISET 是一种从 SQL 表到集合类型和 TABLE 回到 SQL 表的方法。为什么我们使用这样的往返?我想使查询工作,但如何?

with temp as  (
       select 'A' col from dual
       union all
       select 'B' col from dual
     )
select *
from temp,
     table(
       cast(
         multiset(
           select level || temp.col from dual connect by level < 3
         ) as sys.odcivarchar2list
       )
     ) t;

结果是:

COL COLUMN_VALUE
--- ------------
A   1A          
A   2A          
B   1B          
B   2B          

看看第二列是如何命名的COLUMN_VALUE。看起来像是由构造 CAST/MULTISET 或 TABLE 之一生成的名称。

编辑

有了下面接受的答案,我查了文档,发现TABLE机制是一个表集合表达式。圆括号之间的表达式是集合表达式。文档定义了一种称为左相关的机制:

collection_expression 可以引用定义为的表的列 它留在 FROM 子句中。这称为左相关。剩下 关联只能发生在 table_collection_expression 中。其他 子查询不能包含对在 子查询。

所以这就像 12c 中的 LATERAL。

【问题讨论】:

    标签: sql oracle oracle11g


    【解决方案1】:

    Oracle 允许lateral 内联视图引用内联视图内的其他表。

    在旧版本中,此功能主要用于优化,如 Oracle 优化器博客 here 中所述。在 12c 中添加了显式横向连接。您的第一个查询只需稍作改动即可在 12c 中运行:

    with temp as  (
           select 'A' col from dual
           union all
           select 'B' col from dual
         )
    select *
    from temp,
         lateral(select level || temp.col from dual connect by level < 3);
    

    显然,Oracle 也默默地使用横向连接来解除集合的嵌套。在少数情况下,SQL 使用了逻辑交叉连接,但表显然是密切相关的;例如 XMLTable、JSON_table 和第二个示例中的查询。在这些情况下,将两个表一起执行是有意义的。我假设那里使用了横向机制,尽管执行计划和 10053 优化器跟踪都没有使用“横向”一词。该文档甚至在Collection Unnesting: Examples 中有一个与您的非常相似的示例。然而,这个“特性”仍然没有得到很好的记录。


    附带说明,通常您应该避免使用会增加上下文的 SQL 功能。横向连接、公用表表达式和相关子查询等功能可能很有用,但它们也会使 SQL 语句更难理解。常规的内联视图可以自行运行和理解,并且具有非常简单的界面 - 它的投影列。这种简单性使得将小组件组装成大语句变得更加容易。

    我建议您重新编写查询,如下所示。像对待函数或过程一样对待每个内联视图 - 给它们起好名字和 cmets。当您将它们组合成大型、现实的陈述时,它将对您有所帮助。

    select col, the_level||col
    from
    (
        --Good comment 1.
        select 'A' col from dual union all
        select 'B' col from dual
    ) good_name_1
    cross join
    (
        --Good comment 2.
        select level the_level
        from dual
        connect by level < 3
    ) good_name_2
    

    【讨论】:

    • 我猜如果我们需要在连接方式中使用第一个表的列,那么 table/cast/multiset 是必要的。这将使我们避免得到一个完整的交叉产品。当然,我的示例是在选择列表中使用它,我们没有过滤叉积。
    猜你喜欢
    • 1970-01-01
    • 2021-08-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多