Oracle SQL中多集映射的意外结果

时间:2013-06-14 16:36:09

标签: sql oracle collections oracle11g multiset

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

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

问题

为什么在最后一个示例(示例5)中collection (select count(1) ...个实例中的第一行中的collection子查询映射到表的所有行,而预期的结果是将每个collections实例映射到它自己的行?
同时正确选择cardinality(...)表达式from。{ 如果以这种方式构造查询的wherecreate 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 / 部分中使用的集合,则存在相同的情况(示例中未涵盖)。

测试架构设置

SQLFiddle

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

表数据:

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
/

实施例

示例1 SQLFiddle) - 使用嵌套表格字段 - 确定

| 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) |

结果:

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
/

示例2 SQLFiddle) - 仅使用构建的源数据 - 确定

| 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) |

结果:

WITH

示例3 SQLFiddle) - 在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) |

结果:

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
)
/

示例4 SQLFiddle) - 使用WITH +子查询构建的多重集合连接表 - 确定

| 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) |

结果:

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 
)  
/

示例5 SQLFiddle) - 即时构建多联网的联接表 - FAILED

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) | 列中的所有值都相同 - 错误! ):

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

Oracle版本信息

实例1

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 

实例2

{{1}}

SQLFiddle将所有查询放在一起。

1 个答案:

答案 0 :(得分:2)

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

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