左连接与全外连接相结合
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_desc
、long_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 │ ░░░░ │ ░░░░ ║
╚════╧═══════╧═════╧═════╧══════╧═══════╧═══════╝
有一个主要 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_desc
、long_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 │ ░░░░ │ ░░░░ ║
╚════╧═══════╧═════╧═════╧══════╧═══════╧═══════╝