Select 使用左连接的一对多关系中的第一条记录
Select the first record in a one to many relation using left join
我正在尝试使用左联接来联接两个 table。并且结果集必须仅包含“正确”连接的第一条记录 table.
假设我有两个 tables 产品和 product_translations,如下所示;
Table 'products'
id
price
1
100
2
150
Table 'product_translations'
id
product_id
lang
column
value
1
1
en
title
Product 1 Title
2
2
en
title
Product 2 Title
3
1
en
description
Product 1 Description
4
2
en
description
Product 2 Description
5
1
de
title
Produkt 1 Titel
6
2
de
title
Produkt 2 Titel
7
1
de
description
Produkt 1 Beschreibung
8
2
de
description
Produkt 2 Beschreibung
预期输出
id
price
title
description
1
100
Product 1 Title
Product 1 Description
2
150
Product 2 Title
Product 2 Description
您似乎不需要“第一”行,而只需要两个基于语言的不同行。所以你需要加入翻译table:一个用于“标题”,一个用于“描述”
select p.*, t.description as title, d.description
from product p
left join product_translations t
on t.product_id = p.id
and t.language = 'en'
and t.column = 'title'
left join product_translations d
on d.product_id = p.id
and d.language = 'en'
and d.column = 'description'
如果你想用“主要”和“后备”语言检索它,你可以这样做:
with product_texts as (
select t.product_id, t.value as title, d.value as description, t.lang
from product_translations t
join product_translations d
on d.product_id = t.product_id
and d.lang = t.lang
and d."column" = 'description'
where t."column" = 'title'
and t.product_id = 3
and t.lang in ('de', 'en')
)
select t.*
from product_texts t
order by case
when t.lang = 'de' then 1
else 2
end
limit 1
由于总是加入反对它有点复杂,您可以创建一个函数来执行此操作:
create function get_translations(p_product_id int, p_lang text, p_fallback_lang text default 'en')
returns table (product_id int, title text, description text, lang text)
as
$$
with product_texts as (
select t.product_id, t.value as title, d.value as description, t.lang
from product_translations t
join product_translations d
on d.product_id = t.product_id
and d.lang = t.lang
and d."column" = 'description'
where t."column" = 'title'
and t.product_id = 3
and t.lang in (p_lang, p_fallback_lang)
)
select t.*
from product_texts t
order by case
when t.lang = p_lang then 1
else 2
end
limit 1
$$
language sql
rows 1
stable;
那么你可以这样做:
select *
from products p
left join lateral get_translations(p.id, 'de', 'en') on true
调用函数的开销接近 none,因为这将被内联 - 就像您将函数的查询写入主查询一样。
为了获得良好的性能,您需要在 (product_id, lang, "column")
上建立索引,甚至可能需要两个过滤索引:
create index product_lang_title
on product_translations (product_id, lang)
where "column" = 'title';
create index product_lang_descr
on product_translations (product_id, lang)
where "column" = 'description';
使用row_number定义第一行。我假设您的意思是“第一条记录”是“id”值最低的记录。
with prod_t as
(
select t1.*, row_number() over (partition by product_id, t1.column order by id) as rn
from product_translations t1
)
select p1.*, t2.value as title, t3.value as desctiption
from products p1
left join prod_t t2
on t2.product_id = p1.id
and t2.column = 'title'
and rn =1
left join prod_t t3
on t3.product_id = p1.id
and t3.column = 'description'
and rn =1
此外,最好避免使用“column”作为列名
我正在尝试使用左联接来联接两个 table。并且结果集必须仅包含“正确”连接的第一条记录 table.
假设我有两个 tables 产品和 product_translations,如下所示;
Table 'products'
id | price |
---|---|
1 | 100 |
2 | 150 |
Table 'product_translations'
id | product_id | lang | column | value |
---|---|---|---|---|
1 | 1 | en | title | Product 1 Title |
2 | 2 | en | title | Product 2 Title |
3 | 1 | en | description | Product 1 Description |
4 | 2 | en | description | Product 2 Description |
5 | 1 | de | title | Produkt 1 Titel |
6 | 2 | de | title | Produkt 2 Titel |
7 | 1 | de | description | Produkt 1 Beschreibung |
8 | 2 | de | description | Produkt 2 Beschreibung |
预期输出
id | price | title | description |
---|---|---|---|
1 | 100 | Product 1 Title | Product 1 Description |
2 | 150 | Product 2 Title | Product 2 Description |
您似乎不需要“第一”行,而只需要两个基于语言的不同行。所以你需要加入翻译table:一个用于“标题”,一个用于“描述”
select p.*, t.description as title, d.description
from product p
left join product_translations t
on t.product_id = p.id
and t.language = 'en'
and t.column = 'title'
left join product_translations d
on d.product_id = p.id
and d.language = 'en'
and d.column = 'description'
如果你想用“主要”和“后备”语言检索它,你可以这样做:
with product_texts as (
select t.product_id, t.value as title, d.value as description, t.lang
from product_translations t
join product_translations d
on d.product_id = t.product_id
and d.lang = t.lang
and d."column" = 'description'
where t."column" = 'title'
and t.product_id = 3
and t.lang in ('de', 'en')
)
select t.*
from product_texts t
order by case
when t.lang = 'de' then 1
else 2
end
limit 1
由于总是加入反对它有点复杂,您可以创建一个函数来执行此操作:
create function get_translations(p_product_id int, p_lang text, p_fallback_lang text default 'en')
returns table (product_id int, title text, description text, lang text)
as
$$
with product_texts as (
select t.product_id, t.value as title, d.value as description, t.lang
from product_translations t
join product_translations d
on d.product_id = t.product_id
and d.lang = t.lang
and d."column" = 'description'
where t."column" = 'title'
and t.product_id = 3
and t.lang in (p_lang, p_fallback_lang)
)
select t.*
from product_texts t
order by case
when t.lang = p_lang then 1
else 2
end
limit 1
$$
language sql
rows 1
stable;
那么你可以这样做:
select *
from products p
left join lateral get_translations(p.id, 'de', 'en') on true
调用函数的开销接近 none,因为这将被内联 - 就像您将函数的查询写入主查询一样。
为了获得良好的性能,您需要在 (product_id, lang, "column")
上建立索引,甚至可能需要两个过滤索引:
create index product_lang_title
on product_translations (product_id, lang)
where "column" = 'title';
create index product_lang_descr
on product_translations (product_id, lang)
where "column" = 'description';
使用row_number定义第一行。我假设您的意思是“第一条记录”是“id”值最低的记录。
with prod_t as
(
select t1.*, row_number() over (partition by product_id, t1.column order by id) as rn
from product_translations t1
)
select p1.*, t2.value as title, t3.value as desctiption
from products p1
left join prod_t t2
on t2.product_id = p1.id
and t2.column = 'title'
and rn =1
left join prod_t t3
on t3.product_id = p1.id
and t3.column = 'description'
and rn =1
此外,最好避免使用“column”作为列名