【问题标题】: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)?

标签: sql oracle join oracle11g


【解决方案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
        

        【讨论】:

          猜你喜欢
          • 2015-12-24
          • 2014-02-15
          • 1970-01-01
          • 2013-08-06
          • 1970-01-01
          • 1970-01-01
          • 2019-11-17
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多