2016-08-12 159 views
0

有一个主表,其中有两个其他表的引用。这两个表可能每个参考有多个条目。左连接与全外连接组合

主营:

| id | mname | sid | lid | 
|----|-------|-----|-----| 
| 1 | a1 | 1 | 2 | 
| 2 | a2 | 2 | 3 | 
| 3 | a3 | 1 | 1 | 

简称:

| id | lang | sdesc | 
|----|------|-------| 
| 1 | de | S1 | 
| 1 | en | S2 | 
| 2 | de | S3 | 
| 3 | en | S4 | 

(id, lang)是独一无二的。

长:

| id | lang | ldesc | 
|----|------|-------| 
| 1 | de | L1 | 
| 1 | en | L2 | 
| 2 | de | L3 | 
| 3 | en | L4 | 

(id, lang)是独一无二的。

我想加入这三个表有以下结果:

| mname | lang | sdesc | ldesc | 
|-------|------|--------|--------| 
| a1 | de | S1  | L3  | 
| a1 | en | S2  | (null) | 
| a2 | de | S3  | (null) | 
| a2 | en | (null) | L4  | 
| a3 | de | S1  | L1  | 
| a3 | en | S2  | L2  | 

我的第一个尝试是(sqlfiddle

select m.mname, s.lang, s.sdesc, l.lang, l.ldesc 
from main m 
left join short_desc s on s.id = m.sid 
left join long_desc l on l.id = m.lid 

赋予太多的条目。

下一页之一是(sqlfiddle

select m.mname, s.lang, s.sdesc, l.lang, l.ldesc 
from main m 
left join short_desc s on s.id = m.sid 
left join long_desc l on l.id = m.lid 
where s.lang = l.lang 

留下了有效的条目。

以上,很多玩耍的许多变化后,我想出了(sqlfiddle

with x1 as (select m.id, m.mname, s.lang, s.sdesc 
      from main m 
      join short_desc s on s.id = m.sid), 
    x2 as (select m.id, m.mname, l.lang, l.ldesc 
      from main m 
      join long_desc l on l.id = m.lid) 
select coalesce(x1.mname, x2.mname) mname, 
     coalesce(x1.lang, x2.lang) lang, 
     x1.sdesc, 
     x2.ldesc 
from x1 
full outer join x2 on x2.id = x1.id and x2.lang = x1.lang 

这给期望的结果,但对我来说,这似乎过度了这样一个简单的(?)需求。

所以这里是我的问题,有没有更简单的方法?

+1

'...从主米交叉联接(SELECT * FROM UNNEST(阵列[ '日', 'EN']))郎(代码)左加入SHORT_DESC小号在s.id = m.sid和lang.code = s.lang ...' – Abelisto

+0

您的数据和愿望ed结果不匹配。你的结果要求一行a2,en,null,L4,但是你的数据有L4对id3。 – RelativePHPNewbie

+0

@RelativePHPNewbie是,'m.lid = l.id',即'm =(2,a2,2,* 3 *)'和'l =(* 3 *,en,L4)' –

回答

1

IMO你正在错过了你的数据模式是可用的语言的字典。如果引入它,所有其他变得更简单。

期望词典冷为:

  • array['en','de','fr']
  • 真实表中的简单恒定
  • 从表中的视图集的语言​​代码等short_desclong_desc

它是由你决定哪种情况更适合你的目的。

在下面的例子中,我们考虑的语言字典作为CTE:

with lang(code) as (values('en'),('de'),('fr')) 
select m.*, lang.code 
from main m cross join lang; 

╔════╤═══════╤═════╤═════╤══════╗ 
║ id │ mname │ sid │ lid │ code ║ 
╠════╪═══════╪═════╪═════╪══════╣ 
║ 1 │ a1 │ 1 │ 2 │ en ║ 
║ 2 │ a2 │ 2 │ 3 │ en ║ 
║ 3 │ a3 │ 1 │ 1 │ en ║ 
║ 1 │ a1 │ 1 │ 2 │ de ║ 
║ 2 │ a2 │ 2 │ 3 │ de ║ 
║ 3 │ a3 │ 1 │ 1 │ de ║ 
║ 1 │ a1 │ 1 │ 2 │ fr ║ 
║ 2 │ a2 │ 2 │ 3 │ fr ║ 
║ 3 │ a3 │ 1 │ 1 │ fr ║ 
╚════╧═══════╧═════╧═════╧══════╝ 
(9 rows) 

正如你可以看到,现在我们已经为每种语言和各行的下一个和最后一个步骤是加入其他两个表如上所述使用idlang字段查询:

with lang(code) as (values('en'),('de'),('fr')) 
select m.*, lang.code, s.sdesc, l.ldesc 
from main m cross join lang 
    left join short_desc s on s.id = m.sid and s.lang = lang.code 
    left join long_desc l on l.id = m.lid and l.lang = lang.code; 

╔════╤═══════╤═════╤═════╤══════╤═══════╤═══════╗ 
║ id │ mname │ sid │ lid │ code │ sdesc │ ldesc ║ 
╠════╪═══════╪═════╪═════╪══════╪═══════╪═══════╣ 
║ 3 │ a3 │ 1 │ 1 │ de │ S1 │ L1 ║ 
║ 3 │ a3 │ 1 │ 1 │ en │ S2 │ L2 ║ 
║ 3 │ a3 │ 1 │ 1 │ fr │ ░░░░ │ ░░░░ ║ 
║ 1 │ a1 │ 1 │ 2 │ de │ S1 │ L3 ║ 
║ 1 │ a1 │ 1 │ 2 │ en │ S2 │ ░░░░ ║ 
║ 1 │ a1 │ 1 │ 2 │ fr │ ░░░░ │ ░░░░ ║ 
║ 2 │ a2 │ 2 │ 3 │ de │ S3 │ ░░░░ ║ 
║ 2 │ a2 │ 2 │ 3 │ en │ ░░░░ │ L4 ║ 
║ 2 │ a2 │ 2 │ 3 │ fr │ ░░░░ │ ░░░░ ║ 
╚════╧═══════╧═════╧═════╧══════╧═══════╧═══════╝ 
0

您在想left joinlang的条件on条款:

select m.mname, s.lang, s.sdesc, l.lang, l.ldesc 
from main m left join 
    short_desc s 
    on s.id = m.sid left join 
    long_desc l 
    on l.id = m.lid and s.lang = l.lang; 

这应该是Goldilock的解决方案,只有行权数量。

甚至:

select m.mname, s.lang, s.sdesc, l.lang, l.ldesc 
from main m left join 
    short_desc s 
    on s.id = m.sid left join 
    long_desc l 
    on l.id = m.lid and (s.lang = l.lang or s.lang is null); 
+0

这适用于条目,其中'ldesc'为空,但是它留下sdesc'为空的条目,如在“a2(null)L4”行([sqlfiddle](http://sqlfiddle.com/#!15/b3cf7/9/0))中。 –