2

请帮我确认下面解释的行为是一个错误,或者清楚地解释为什么它是正确的。我很可能误解了一些概念,但现在对我来说它看起来像一个错误。

下面的所有示例都尽可能简化以展示问题的核心。实际情况非常复杂,因此只能接受与查询构造原理相关的一般答案和变通方法。
欢迎您在评论中提出澄清问题,我会尽力回答。
感谢您的关注。:)

问题

为什么在上一个示例(示例 5)collection中的子查询中的实例 (select count(1) ...从第一行映射到表的所有行,而预期结果是将每个collection实例映射到它自己的行?
同时collections用于cardinality(...)表达方式选择得当。如果以这种方式构造在查询中使用或作为查询的一部分的
集合,则存在相同的情况(示例中未涵盖) 。fromwhere

测试架构设置

( SQLFiddle )

create or replace type TabType0 as table of varchar2(100)
/

create table Table0( tab_str_field varchar2(100), tab_field TabType0)
nested table tab_field store as tab_field_table
/

insert into table0 (tab_str_field, tab_field) values (
   'A',
   cast(multiset(
     select 'A' from dual union all
     select 'B' from dual union all
     select 'C' from dual 
   ) as TabType0) 
)
/

insert into table0 (tab_str_field, tab_field) values (
   'B',
   cast(multiset(
     select 'B' from dual union all
     select 'C' from dual 
   ) as TabType0) 
)
/

insert into table0 (tab_str_field, tab_field) values (
   'C',
   cast(multiset(
     select 'A' from dual union all
     select 'B' from dual union all
     select 'C' from dual union all
     select 'D' from dual 
   ) as TabType0) 
)
/

insert into table0 (tab_str_field, tab_field) values (
   'D',
   cast(multiset(
     select 'A' from dual 
   ) as TabType0) 
)
/
select 'Initial table data' caption from dual
/
select * from table0
/

表数据:

| TAB_STR_FIELD | TAB_FIELD |
-----------------------------
|             A |     A,B,C |
|             B |       B,C |
|             C |   A,B,C,D |
|             D |         A |

例子

示例 1 ( SQLFiddle ) - 使用嵌套表字段 -好的

select 'Work with nested table - OK' caption from dual
/
select 
  tab_field                               tab_field,

  -- cardinality
  cardinality(tab_field)                  tab_cardinality,

  -- select from table field of current row
  (select count(1) from table(tab_field)) tab_count,

  -- select from field of current row while joining 
  -- with another field of same row
  ( select column_value from table(tab_field) 
    where column_value = tab_str_field
  )                                       same_value
from table0
/

结果:

| TAB_FIELD | TAB_CARDINALITY | TAB_COUNT | SAME_VALUE |
--------------------------------------------------------
|     A,B,C |               3 |         3 |          A |
|       B,C |               2 |         2 |          B |
|   A,B,C,D |               4 |         4 |          C |
|         A |               1 |         1 |     (null) |

示例 2 ( SQLFiddle ) - 单独使用构造的源数据 -好的

select 'Work with constructed source data alone - OK' caption from dual
/
with table_data_from_set as (
  select
    'A' tab_str_field,
    cast(multiset(
      select 'A' from dual union all
      select 'B' from dual union all
      select 'C' from dual
    ) as TabType0)  tab_field
  from dual union all
  select
    'B' tab_str_field,
    cast(multiset(
      select 'B' from dual union all
      select 'C' from dual
    ) as TabType0)  tab_field
  from dual union all
  select
    'C' tab_str_field,
    cast(multiset(
      select 'A' from dual union all
      select 'B' from dual union all
      select 'C' from dual union all
      select 'D' from dual
    ) as TabType0) tab_field
  from dual union all
  select
    'D' tab_str_field,
    cast(multiset(
      select 'A' from dual
    ) as TabType0) tab_field
  from dual
)
select
  tab_field                                tab_field,

  -- cardinality
  cardinality(tab_field)                   tab_cardinality,

  -- select from table field of current row
  (select count(1) from table(tab_field))  tab_count,

  -- select from field of current row while joining
  -- with another field of same row
  ( select column_value from table(tab_field)
    where column_value = tab_str_field
  )                                        same_value
from table_data_from_set
/

结果:

| TAB_FIELD | TAB_CARDINALITY | TAB_COUNT | SAME_VALUE |
--------------------------------------------------------
|     A,B,C |               3 |         3 |          A |
|       B,C |               2 |         2 |          B |
|   A,B,C,D |               4 |         4 |          C |
|         A |               1 |         1 |     (null) |

示例 3 ( SQLFiddle ) - 使用构造的多集连接表WITH-确定

select 'Join table with multisets constructed in WITH - OK' caption from dual
/
with table_data_from_set as (
  select
    'A' tab_str_field,
    cast(multiset(
      select 'A' from dual union all
      select 'B' from dual union all
      select 'C' from dual
    ) as TabType0)  tab_field
  from dual union all
  select
    'B' tab_str_field,
    cast(multiset(
      select 'B' from dual union all
      select 'C' from dual
    ) as TabType0)  tab_field
  from dual union all
  select
    'C' tab_str_field,
    cast(multiset(
      select 'A' from dual union all
      select 'B' from dual union all
      select 'C' from dual union all
      select 'D' from dual
    ) as TabType0) tab_field
  from dual union all
  select
    'D' tab_str_field,
    cast(multiset(
      select 'A' from dual
    ) as TabType0) tab_field
  from dual
)
select
  table0.tab_field                                            table0_tab_field,
  table_data_from_set.tab_field                               set_tab_field,

  -- cardinality
  cardinality(table0.tab_field) table0_tab_cardinality,
  cardinality(table_data_from_set.tab_field)                  set_tab_cardinality,

  -- select from table field of current row
  (select count(1) from table(table_data_from_set.tab_field)) set_tab_count,

  -- select from field of current row while joining
  -- with another field of same row
  ( select column_value from table(table_data_from_set.tab_field)
    where column_value = table0.tab_str_field
  )                                                           same_value
from 
  table0, 
  table_data_from_set 
where 
  table_data_from_set.tab_str_field = table0.tab_str_field
/

结果:

| TABLE0_TAB_FIELD | SET_TAB_FIELD | TABLE0_TAB_CARDINALITY | SET_TAB_CARDINALITY | SET_TAB_COUNT | SAME_VALUE |
----------------------------------------------------------------------------------------------------------------
|            A,B,C |         A,B,C |                      3 |                   3 |             3 |          A |
|              B,C |           B,C |                      2 |                   2 |             2 |          B |
|          A,B,C,D |       A,B,C,D |                      4 |                   4 |             4 |          C |
|                A |             A |                      1 |                   1 |             1 |     (null) |

示例 4 ( SQLFiddle ) - 使用 WITH + 子查询中构造的多集连接表 -确定

select 'Join table with multisets constructed in WITH and subquery - OK' caption from dual
/
with table_data_from_set as (
  select
    'A' tab_str_field,
    cast(multiset(
      select 'A' from dual union all
      select 'B' from dual union all
      select 'C' from dual
    ) as TabType0)  tab_field
  from dual union all
  select
    'B' tab_str_field,
    cast(multiset(
      select 'B' from dual union all
      select 'C' from dual
    ) as TabType0)  tab_field
  from dual union all
  select
    'C' tab_str_field,
    cast(multiset(
      select 'A' from dual union all
      select 'B' from dual union all
      select 'C' from dual union all
      select 'D' from dual
    ) as TabType0) tab_field
  from dual union all
  select
    'D' tab_str_field,
    cast(multiset(
      select 'A' from dual
    ) as TabType0) tab_field
  from dual
)
select
  table0_tab_field                            table0_tab_field,
  set_tab_field                               set_tab_field,

  -- cardinality
  cardinality(table0_tab_field)               table0_tab_cardinality,
  cardinality(set_tab_field)                  set_tab_cardinality,

  -- select from table field of current row
  (select count(1) from table(set_tab_field)) set_tab_count,

  -- select from field of current row while joining
  -- with another field of same row
  ( select column_value from table(set_tab_field)
    where column_value = table0_tab_str_field
  )                                           same_value
from (
  select 
    table0.tab_str_field              table0_tab_str_field,
    table0.tab_field                  table0_tab_field,
    table_data_from_set.tab_str_field set_tab_str_field,
    table_data_from_set.tab_field     set_tab_field
  from 
    table0, 
    table_data_from_set 
  where 
    table_data_from_set.tab_str_field = table0.tab_str_field
)
/

结果:

| TABLE0_TAB_FIELD | SET_TAB_FIELD | TABLE0_TAB_CARDINALITY | SET_TAB_CARDINALITY | SET_TAB_COUNT | SAME_VALUE |
----------------------------------------------------------------------------------------------------------------
|            A,B,C |         A,B,C |                      3 |                   3 |             3 |          A |
|              B,C |           B,C |                      2 |                   2 |             2 |          B |
|          A,B,C,D |       A,B,C,D |                      4 |                   4 |             4 |          C |
|                A |             A |                      1 |                   1 |             1 |     (null) |

示例 5 ( SQLFiddle ) - 使用动态构建的多集连接表 -失败

select 'Join table with multisets constructed on the fly - FAIL (set_tab_count wrong)' caption from dual
/
with string_set as (
  select 'A' str_field from dual union all
  select 'B' str_field from dual union all
  select 'C' str_field from dual union all
  select 'D' str_field from dual union all
  select 'E' str_field from dual 
)
select
  table0_tab_field                            table0_tab_field,
  set_tab_field                               set_tab_field,

  -- cardinality
  cardinality(table0_tab_field)               table0_tab_cardinality,
  cardinality(set_tab_field)                  set_tab_cardinality,

  -- select from table field of current row
  (select count(1) from table(set_tab_field)) set_tab_count,

  -- select from field of current row while joining
  -- with another field of same row
  ( select column_value from table(set_tab_field)
    where column_value = table0_tab_str_field
  )                                            same_value
from (
  select 
    table0.tab_str_field     table0_tab_str_field,
    table0.tab_field         table0_tab_field,
    ( 
      cast(multiset(
        
        select 
          string_set.str_field 
        from 
          string_set, 
          table(table0.tab_field) tab_table
        where 
          string_set.str_field = tab_table.column_value
        
      ) as TabType0)
    )                        set_tab_field
  from 
    table0 
)  
/

结果(set_tab_count列中的所有值都相同 -错误!):

| TABLE0_TAB_FIELD | SET_TAB_FIELD | TABLE0_TAB_CARDINALITY | SET_TAB_CARDINALITY | SET_TAB_COUNT | SAME_VALUE |
----------------------------------------------------------------------------------------------------------------
|            A,B,C |         A,B,C |                      3 |                   3 |             3 |          A |
|              B,C |           B,C |                      2 |                   2 |             3 |          B |
|          A,B,C,D |       A,B,C,D |                      4 |                   4 |             3 |          C |
|                A |             A |                      1 |                   1 |             3 |     (null) |

Oracle 版本信息

实例 1

BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
PL/SQL Release 11.2.0.3.0 - Production
CORE    11.2.0.3.0  Production
TNS for IBM/AIX RISC System/6000: Version 11.2.0.3.0 - Production
NLSRTL Version 11.2.0.3.0 - Production

实例 2

BANNER 
--------------------------------------------------------------------------------
Oracle Database 11g Express Edition Release 11.2.0.2.0 - Production 
PL/SQL Release 11.2.0.2.0 - Production 
CORE    11.2.0.2.0  Production 
TNS for 32-bit Windows: Version 11.2.0.2.0 - Production 
NLSRTL Version 11.2.0.2.0 - Production 

SQLFiddle将所有查询放在一起。

4

1 回答 1

2

这是一个错误。在最后一个示例中向第二个内联视图添加/*+ NO_MERGE */提示将生成预期结果。有关示例,请参阅此 SQL Fiddle 。无论查询如何,该提示都不应更改结果。您可以进行其他一些看似无关的更改来生成正确的结果,例如删除一些列,或ROWNUM在中间添加未使用的列。

Oracle 正在重写您的查询以优化它,但做错了。您可能可以通过跟踪查询获得更多信息,但我怀疑您是否能够真正解决问题。暂时解决它并向 Oracle 提交服务请求,以便他们可以创建错误并最终修复它。

于 2013-06-14T23:02:58.500 回答