测试数据:
create temp table l (id integer,name text);
create temp table t (id integer);
create temp table t_i18n(id integer,l_id integer,t_id integer,name text);
insert into l(id, name) values
(1, 'lang_1'),
(2, 'lang_2');
insert into t(id) values(1);
insert into t_i18n(id, l_id, t_id, name) values
(1, 1, 1, 'Text in the language one'),
(2, 2, 1, 'Text in the language two');
执行此查询后:
select *
from t
inner join t_i18n i18n
on i18n.t_id = t.id;
我有这个结果:
id | id | l_id | t_id | name
----+----+------+------+--------------------------
1 | 1 | 1 | 1 | Text in the language one
1 | 2 | 2 | 1 | Text in the language two
是否可以修改上面的查询以获得下面的结果?
/*Expected result*/
id | name_lang_1 | name_lang_2
----+--------------------------+--------------------------
1 | Text in the language one | Text in the language two
答案 0 :(得分:1)
使用自我加入
select t1.id,
i18n1.l_id l_id1,
i18n1.t_id t_id1,
i18n1.name name1,
i18n2.l_id l_id2,
i18n2.t_id t_id2,
i18n2.name name2
from t t1
inner join t_i18n i18n1 on i18n1.t_id = t1.id and i18n1.l_id = 1
inner join t t2 on t1.id = t2.id
inner join t_i18n i18n2 on i18n2.t_id = t2.id and i18n2.l_id = 2
;
然而,它只能假设t表中的每个记录在t_i18n表中总是有两个相应的记录(每种语言一个记录)。
如果某些记录只能有一种语言(lang-1或lang-2),那么你必须使用像这个查询中的外部联接:
select t1.id,
i18n1.l_id l_id1,
i18n1.t_id t_id1,
i18n1.name name1,
i18n2.l_id l_id2,
i18n2.t_id t_id2,
i18n2.name name2
from t t1
left join t_i18n i18n1 on i18n1.t_id = t1.id and i18n1.l_id = 1
full outer join t t2 on t1.id = t2.id
left join t_i18n i18n2 on i18n2.t_id = t2.id and i18n2.l_id = 2
以下是SQLFiddle demo,其中包含第二种情况和两种查询的示例数据,请查看它们如何处理此数据。
答案 1 :(得分:1)
通常 crosstab()
最快。您需要在数据库中安装附加模块tablefunc。
SELECT * FROM crosstab(
'SELECT t.id, l.name AS lang_name, i.name AS lang
FROM t
JOIN t_i18n i ON i.t_id = t.id
JOIN l ON l.id = i.l_id' -- could also just be "ORDER BY 1" here
,$$VALUES ('lang_1'::text), ('lang_2')$$)
AS l (id text, lang_1 text, lang_2 text);
如果您的案例实际上就像那样简单(几乎没有),那么使用CASE
语句的查询就可以:
SELECT t.id
, min(CASE WHEN i.l_id = 1 THEN i.name ELSE NULL END) AS lang_1
, min(CASE WHEN i.l_id = 2 THEN i.name ELSE NULL END) AS lang_2
FROM t
LEFT JOIN t_i18n i ON i.t_id = t.id
LEFT JOIN l ON l.id = i.l_id
GROUP BY 1
ORDER BY 1;
此相关问题下两种解决方案的详情:
PostgreSQL Crosstab Query
由于每个SQL查询和每个函数都必须具有明确定义的返回类型,因此无法在单个查询中对动态数量的语言名称执行此操作。您可以编写一个函数来动态创建语句,并在第二次调用中执行该语句。
还有多态类型的高级技术,我在这里写了一个全面的答案:
Dynamic alternative to pivot with CASE and GROUP BY
但是,上面的简单crosstab()
查询适用于语言名称的超集。非语言语言的字段会返回NULL
。看看provided link。