小编典典

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

sql

请帮助我确认以下说明的行为是一个错误,或者明确说明为什么是正确的。我很可能误解了一些概念,但是现在对我而言,这似乎是一个错误。

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

问题

为什么在子查询的最后一个示例(示例5)collection中,(select count(1) ...从第一行的实例
映射到表的所有行,而预期结果是将每个collection实例映射到它自己的行?
同时collectionscardinality(...)表达式中使用正确选择。
如果以此方式构造查询中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 |

例子

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

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

示例2SQLFiddle)-单独处理构造的源数据-
确定

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

示例4SQLFiddle)-具有在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) |

实施例5SQLFiddle) -连接表与在飞行构造多重集-
FAILED

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与所有查询一起。


阅读 195

收藏
2021-04-22

共1个答案

小编典典

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

Oracle正在重新编写查询以对其进行优化,但是这样做有误。通过跟踪查询,您可能会获得更多信息,但是我怀疑您是否能够真正解决该问题。现在就解决它,并向Oracle提交服务请求,这样他们就可以创建错误并最终对其进行修复。

2021-04-22