如何在两个表之间的组中实现递归连接?

Posted

技术标签:

【中文标题】如何在两个表之间的组中实现递归连接?【英文标题】:How can I implement this a recursive join in a group between two tables? 【发布时间】:2013-02-18 14:52:27 【问题描述】:

我在一个名为AVAILABLE_TEMPLATES 的表中有一些杂乱的数据。这是一个简单的例子:

TEMPLATE_GROUP TEMPLATE_NAME LOCALE
-------------- ------------- ------
RO              LTRU          fi_FI  
RO              LTRU          se_SE  
RO              LTRU          en_US  
BL              V1PRO         se_SE  
BL              V1PRO         en_US  

我有另一个表,其中包含名为 SYSTEM_LOCALES 的语言环境。

SYS_LOCALE
------
lv_LV  
fi_FI  
sv_SE  
en_US

我希望通过加入这两个表获得的数据应该具有SYS_LOCALE 表中的行和AVAILABLE_TEMPLATES 表中不同的TEMPLATE_GROUP/TEMPLATE_NAME 行的笛卡尔积。

我们的默认语言环境是fi_FI。对于TEMPLATE_GROUP 中的每个TEMPLATE_NAME,我想检查是否有匹配的语言环境可用,如果是,则应将其返回为USE_LOCALE。如果找不到匹配的语言环境,我想返回系统的默认语言环境,即fi_FI,如果TEMPLATE_NAMETEMPLATE_GROUP 存在USE_LOCALE

以下是连接两个表应返回的内容:

TEMPLATE_GROUP TEMPLATE_NAME SYS_LOCALE  USE_LOCALE
-----------    ------------- ------      ----------
RO             LTRU          lv_LV       fi_FI      --There's a fi_FI locale but no lv_LV 
RO             LTRU          fi_FI       fi_FI    
RO             LTRU          se_SE       se_SE    
RO             LTRU          en_US       en_US    
BL             V1PRO         lv_LV       NULL       --There's no lv_LV or a fi_FI locale 
BL             V1PRO         fi_FI       NULL       --There's no fi_FI locale
BL             V1PRO         se_SE       se_SE    
BL             V1PRO         en_US       en_US    

我一直无法弄清楚这一点,并且对此一无所知。是否必须通过递归来完成?谢谢

【问题讨论】:

对于给定的TEMPLATE_GROUP,您可以有多个TEMPLATE_NAME 值(反之亦然),或者这两个值之间是否存在一一对应关系?另外,您是否有单独的模板/模板组值表(除了AVAILABLE_TEMPLATES)? 【参考方案1】:

唯一允许您在不多次读取表的情况下执行此操作的构造是partitioned outer join。

以下是您的数据集的示例:

SQL> create table available_templates (template_group,template_name,locale)
  2  as
  3  select 'RO', 'LTRU', 'fi_FI' from dual union all
  4  select 'RO', 'LTRU', 'se_SE' from dual union all
  5  select 'RO', 'LTRU', 'en_US' from dual union all
  6  select 'BL', 'V1PRO', 'se_SE' from dual union all
  7  select 'BL', 'V1PRO', 'en_US' from dual
  8  /

Table created.

SQL> create table system_locales (sys_locale)
  2  as
  3  select 'lv_LV' from dual union all
  4  select 'fi_FI' from dual union all
  5  select 'se_SE' from dual union all
  6  select 'en_US' from dual
  7  /

Table created.

以及分区外连接:

SQL> select at.template_group
  2       , at.template_name
  3       , sl.sys_locale
  4       , nvl
  5         ( at.locale
  6         , max(decode(at.locale,'fi_FI',at.locale)) over (partition by at.template_group, at.template_name)
  7         ) use_locale
  8    from system_locales sl
  9         left outer join available_templates at
 10           partition by (at.template_group,at.template_name)
 11           on (at.locale = sl.sys_locale)
 12  /

TEMPLATE_GROUP TEMPLATE_NAME SYS_LOCALE USE_LOCALE
-------------- ------------- ---------- ----------
BL             V1PRO         en_US      en_US
BL             V1PRO         fi_FI
BL             V1PRO         lv_LV
BL             V1PRO         se_SE      se_SE
RO             LTRU          en_US      en_US
RO             LTRU          fi_FI      fi_FI
RO             LTRU          lv_LV      fi_FI
RO             LTRU          se_SE      se_SE

8 rows selected.

这是表只被扫描一次的证据:

    SQL> select * from table(dbms_xplan.display_cursor(null,null,'allstats last'))
      2  /

    PLAN_TABLE_OUTPUT
    ---------------------------------------------------------------------------------------------------------------------------------------
    SQL_ID  57br33gc6n1sc, child number 0
    -------------------------------------
    select at.template_group      , at.template_name      , sl.sys_locale
       , nvl        ( at.locale        ,
    max(decode(at.locale,'fi_FI',at.locale)) over (partition by
    at.template_group, at.template_name)        ) use_locale   from
    system_locales sl        left outer join available_templates at
     partition by (at.template_group,at.template_name)          on
    (at.locale = sl.sys_locale)

    Plan hash value: 921719364

-----------------------------------------------------------------------------------------------------------------------------------------
| Id  | Operation                    | Name                | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
-----------------------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |                     |      1 |        |      8 |00:00:00.01 |       6 |       |       |          |
|   1 |  WINDOW BUFFER               |                     |      1 |      1 |      8 |00:00:00.01 |       6 |  2048 |  2048 | 2048  (0)|
|   2 |   VIEW                       |                     |      1 |      1 |      8 |00:00:00.01 |       6 |       |       |          |
|   3 |    MERGE JOIN PARTITION OUTER|                     |      1 |      1 |      8 |00:00:00.01 |       6 |       |       |          |
|   4 |     SORT JOIN                |                     |      3 |      4 |      9 |00:00:00.01 |       3 |  2048 |  2048 | 2048  (0)|
|   5 |      TABLE ACCESS FULL       | SYSTEM_LOCALES      |      1 |      4 |      4 |00:00:00.01 |       3 |       |       |          |
|*  6 |     SORT PARTITION JOIN      |                     |      9 |      5 |      5 |00:00:00.01 |       3 |  2048 |  2048 | 2048  (0)|
|   7 |      TABLE ACCESS FULL       | AVAILABLE_TEMPLATES |      1 |      5 |      5 |00:00:00.01 |       3 |       |       |          |
-----------------------------------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   6 - access("AT"."LOCALE"="SL"."SYS_LOCALE")
       filter("AT"."LOCALE"="SL"."SYS_LOCALE")

Note
-----
   - dynamic sampling used for this statement (level=2)


35 rows selected.

问候, 抢。

【讨论】:

慢拍。我什至从未听说过partioned-outer-joins。那很有帮助!我会牢记这一点,以备将来之用。谢谢罗伯。【参考方案2】:

你可以这样做:

select t_base.template_group, t_base.template_name, l.sys_locale, 
      nvl( t.locale, default_locale)  use_locale
  from system_locales l
       cross join (select t.template_group, t.template_name, 
                          max(case t.locale when 'fi_FI' then  t.locale end) default_locale
                     from available_templates t
                    group by t.template_group, t.template_name) t_base
       left outer join available_templates t
               on t.template_group = t_base.template_group
              and t.template_name = t_base.template_name
              and t.locale = l.sys_locale
 order by 1, 2

【讨论】:

【参考方案3】:
select
   dat.template_group,
   dat.template_name,
   sl.sys_locale,
   nvl(at.locale, dat.fi_fi) as use_locale
from
   (
      select
         template_group,
         template_name,
         max(decode(locale, 'fi_FI', 'fi_FI')) as fi_fi
      from
         AVAILABLE_TEMPLATES
      group by
         template_group,
         template_name
   ) dat
   cross join SYS_LOCALE sl
   left join AVAILABLE_TEMPLATES at
      on at.template_group = dat.template_group
      and at.template_name = dat.template_name
      and at.locale = sl.sys_locale

【讨论】:

【参考方案4】:

试试:

with templates as 
(select template_group, template_name, max(case locale when 'fi_FI' then locale end) available_default
 from available_templates
 group by template_group, template_name),
template_combinations as
(select t.*, s.sys_locale
 from templates t
 cross join sys_locale s)
select c.template_group, c.template_name, c.sys_locale, coalesce(a.locale, c.available_default) use_locale
from template_combinations c
left join available_templates a 
on c.template_group = a.template_group and c.template_name = a.template_name and c.sys_locale = a.locale

从所提供的信息来看,您的数据库似乎没有正确规范化 - 至少应该有一个额外的表用于 template_group 和 template_name 的组合。

【讨论】:

【参考方案5】:

这样的?

它生成区域设置和所有组/名称星座的笛卡尔积。然后它为每个组/名称/语言环境返回一行。

CASE 语句返回:

区域设置,当它存在于该组时 fi_FI 存在时 如果没有则为空
select
  sl.SYS_LOCALE,
  at.TEMPLATE_GROUP,
  at.TEMPLATE_NAME,
  CASE 
    WHEN EXISTS (SELECT 1 FROM AVAILABLE_TEMPLATES at_loc 
      WHERE 
        at_loc.TEMPLATE_GROUP = at.TEMPLATE_GROUP, 
        at_loc.TEMPLATE_NAME = at_fi.TEMPLATE_NAME
        at_loc.LOCALE = sl.SYS_LOCALE) 
      THEN sl.SYS_LOCALE

    WHEN EXISTS (SELECT 1 FROM AVAILABLE_TEMPLATES at_fi 
      WHERE 
        at_fi.TEMPLATE_GROUP = at.TEMPLATE_GROUP, 
        at_fi.TEMPLATE_NAME = at_fi.TEMPLATE_NAME
        at_fi.LOCALE = 'fi_FI') 
      THEN 'fi_FI'
    ELSE NULL
  END as USE_LOCALE
from SYSTEM_LOCALES sl,
    (select 
      TEMPLATE_GROUP, 
      TEMPLATE_NAME
    from AVAILABLE_TEMPLATES
    GROUP BY TEMPLATE_GROUP, TEMPLATE_NAME)
    allgroups

【讨论】:

以上是关于如何在两个表之间的组中实现递归连接?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 QML 中实现对象之间的单一连接?

如何在elasticsearch中实现两种类型之间的交叉连接?

如何在微信小游戏制作工具中实现递归函数

如何在leanback BrowseFragment中实现两个PageRow之间的转换?

如何在hibernate中实现对两个表的查询数据?

如何在Java中实现多个线程来下载单个表数据?