左连接与全外连接相结合

Left join combined with full outer join

有一个主要 table,它引用了另外两个 table。这两个 table 每个引用可能有多个条目。

主要:

| 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)是独一无二的。

我想加入这三个 table 得到以下结果:

| 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

这给出了预期的结果,但对我来说,对于这样一个简单的 (?) 要求来说,这似乎太过分了。

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

您想要 left join 条件 lang 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;

这应该是金凤花的解决方案,行数恰到好处。

甚至:

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

IMO 您在数据模式中遗漏的是可用语言的字典。引入它,其他就简单多了。

所需的字典是:

  • array['en','de','fr']
  • 这样的简单常量
  • 真实的table
  • 视图从 table 中收集语言代码,例如 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)

如您所见,现在我们有每种语言的单独行,下一步和最后一步是使用 id 和 [=16 将其他两个 table 加入上面的查询=] 字段:

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   │ ░░░░  │ ░░░░  ║
╚════╧═══════╧═════╧═════╧══════╧═══════╧═══════╝