2013-03-04 55 views
3

我在一張名爲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_GROUPUSE_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  

我一直沒能弄清楚這一個,已經完全迷失了機智。它是否必須用遞歸來完成?由於

+0

對於給定的「TEMPLATE_GROUP」(或反之亦然),您是否可以有多個「TEMPLATE_NAME」值?或者這兩個值之間是否存在一對一的對應關係?另外,您是否有模板/模板組值的單獨表格(除AVAILABLE_TEMPLATES以外)? – 2013-03-04 13:46:07

回答

5

唯一允許您在不多次讀取表格的情況下執行此操作的構造是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. 

Regards,
Rob。

+0

慢拍。我從來沒有聽說過分配外連接。這很有幫助!我將銘記未來。感謝Rob。 – 2013-03-04 14:59:53

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

嘗試:

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的組合額外的表。

1

是這樣的?

它使區域設置和所有組/名稱星座的笛卡爾乘積。然後它爲每個組/名稱/區域設置返回一行。

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