如何在两个表之间的组中实现递归连接?
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_NAME
和TEMPLATE_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
【讨论】:
以上是关于如何在两个表之间的组中实现递归连接?的主要内容,如果未能解决你的问题,请参考以下文章
如何在elasticsearch中实现两种类型之间的交叉连接?