【问题标题】:左外连接先聚合
【发布时间】:2022-01-21 13:01:05
【问题描述】:

我有以下表格

CREATE TABLE categories(
  id SERIAL,
);

CREATE TABLE category_translations(
  id SERIAL,
  name varchar not null,
  locale varchar not null, 
  category_id integer not null
);

CREATE TABLE products(
  id SERIAL,
  category_id integer not null
);

CREATE TABLE line_items(
  id SERIAL,
  total_cents integer
  product_id integer not null
);

我要做的是将每个类别名称的映射输出到与其关联的line_itemstotal_cents 的总和。比如:

name sum_total_cents
Fresh foods 100000
Dry products 532000

有一个唯一性约束,即每个语言环境只存储一个名称。因此,category 将在 category_translations 表中存储的每个区域设置一行

我目前拥有的是

SELECT SUM(line_items.total_cents) AS sum_total_cents, ???
FROM line_items INNER JOIN products ON products.id = line_items.product_id
INNER JOIN categories ON categories.id = products.category_id
LEFT OUTER JOIN category_translations ON category_translations.category_id = categories.id 
WHERE category_translations.locale ='en'
GROUP BY categories.id

我正在寻找一个聚合函数来返回该类别的第一个 name。唯一缺少的是要写什么而不是???,因为我遇到了很多must appear in the GROUP BY clause or be used in an aggregate function 错误。在伪代码中,我正在寻找可以使用的 PostgreSQL 中的 FIRST() 聚合方法

【问题讨论】:

  • 避免使用旧的SERIAL,改用IDENTITY
  • "...类别的名字..." -- 没有这样的东西。你的意思是随机的名字吗?请记住,关系数据库中的行没有固有的顺序。
  • @TheImpaler 我有一个唯一性约束,即每个语言环境只有一个名称将存储在表中。因此,category 将在 category_translations 表中存储的每个区域设置一行
  • 这完全没问题,但每个类别仍然有多个名称。你想选哪一个?所有这些多个名称都没有顺序,所以没有“第一个”。
  • 如果您有WHERE category_translations.locale ='en',您实际上已将LEFT JOIN 更改为INNER JOIN。您可以将MAX(category_translations.name) AS name 添加到选择列表中以避免错误。该错误只是指功能依赖要求。不需要子查询。

标签: sql postgresql left-join aggregate-functions


【解决方案1】:

假设您想要一个来自任何语言环境的 随机 名称,您可以这样做:

select
  c.id,
  (select name from category_translations t 
   where t.category_id = c.id limit 1) as name,
  sum(i.total_cents) as sum_total_cents
from categories c
left join products p on p.category_id = c.id
left join line_items i on i.product_id = p.id
group by c.id, name

或者,如果您想要区域设置“en”的类别名称,那么您可以这样做:

select
  c.id,
  (select t.name from category_translations t 
   where t.category_id = c.id and t.locale ='en') as name,
  sum(i.total_cents) as sum_total_cents
from categories c
left join products p on p.category_id = c.id
left join line_items i on i.product_id = p.id
group by c.id, name

【讨论】:

  • 您的回答有效。作为替代方案:sql SELECT SUM(line_items.total_cents) AS sum_total_cents, (array_agg(name ORDER BY category_translations.id ASC))[1] as name FROM line_items INNER JOIN products ON products.id = line_items.product_id INNER JOIN categories ON categories.id = products.category_id LEFT OUTER JOIN category_translations ON category_translations.category_id = categories.id WHERE category_translations.locale ='en' GROUP BY categories.id 也很简单,谢谢!
  • @bmaw 只是一个注释。您在那里使用INNER JOIN,当一个类别至少有 1 个产品并且每个产品至少有 1 行时,这将很有效。如果不是这种情况,您可能想改用LEFT JOIN
猜你喜欢
  • 1970-01-01
  • 2015-03-11
  • 1970-01-01
  • 2021-11-07
  • 2018-03-15
  • 1970-01-01
  • 2014-02-06
  • 2014-09-23
  • 1970-01-01
相关资源
最近更新 更多