You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL多对多关联查询:同时关联演员与制片商表问题

PostgreSQL多表多对多关联查询JSON结果解决方案

问题根源

同时关联两个多对多中间表(movies_actors和movies_studios)时,会产生笛卡尔积:假设某部电影有2个演员、3个制片商,JOIN后会生成6条重复的电影记录。直接用GROUP BY movies.id要么会出现聚合结果重复(演员/制片商多次出现在数组里),要么因非聚合列未包含在GROUP BY中触发PostgreSQL的语法限制(除非movies.id是主键,但重复数据问题依然存在)。

推荐解决方案

方案1:子查询预聚合后关联

先分别对演员、制片商按电影ID聚合生成JSON数组,再和主表关联,从根源避免笛卡尔积。

SELECT
  m.*,
  -- 处理无演员的情况,返回空数组
  COALESCE(a.actors, '[]'::JSON) AS actors,
  -- 处理无制片商的情况,返回空数组
  COALESCE(s.studios, '[]'::JSON) AS studios
FROM movies m
-- 关联预聚合的演员数据
LEFT JOIN (
  SELECT
    ma.movie_id,
    JSON_AGG(
      JSON_BUILD_OBJECT(
        'id', a.id,
        'name', a.name,
        'gender', a.gender -- 根据actors表实际字段调整
      )
    ) AS actors
  FROM movies_actors ma
  JOIN actors a ON ma.actor_id = a.id
  GROUP BY ma.movie_id
) a ON m.id = a.movie_id
-- 关联预聚合的制片商数据
LEFT JOIN (
  SELECT
    ms.movie_id,
    JSON_AGG(
      JSON_BUILD_OBJECT(
        'id', s.id,
        'name', s.name,
        'location', s.location -- 根据studios表实际字段调整
      )
    ) AS studios
  FROM movies_studios ms
  JOIN studios s ON ms.studio_id = s.id
  GROUP BY ms.movie_id
) s ON m.id = s.movie_id;

方案2:使用LATERAL JOIN

通过LATERAL JOIN为每一部电影单独查询对应的演员和制片商列表,同样避免笛卡尔积,逻辑更直观。

SELECT
  m.*,
  COALESCE(a.actors, '[]'::JSON) AS actors,
  COALESCE(s.studios, '[]'::JSON) AS studios
FROM movies m
-- 为每个电影查询演员列表
LEFT JOIN LATERAL (
  SELECT JSON_AGG(
    JSON_BUILD_OBJECT(
      'id', a.id,
      'name', a.name,
      'gender', a.gender
    )
  ) AS actors
  FROM movies_actors ma
  JOIN actors a ON ma.actor_id = a.id
  WHERE ma.movie_id = m.id
) a ON true
-- 为每个电影查询制片商列表
LEFT JOIN LATERAL (
  SELECT JSON_AGG(
    JSON_BUILD_OBJECT(
      'id', s.id,
      'name', s.name,
      'location', s.location
    )
  ) AS studios
  FROM movies_studios ms
  JOIN studios s ON ms.studio_id = s.id
  WHERE ms.movie_id = m.id
) s ON true;

不推荐的临时方案(仅用于小数据量)

如果数据量很小,也可以通过JSON_AGG(DISTINCT ...)去重,但性能较差,不适合大数据场景:

SELECT
  m.id,
  m.title,
  m.release_year, -- 列出movies表所有需要的字段
  JSON_AGG(DISTINCT JSON_BUILD_OBJECT('id', a.id, 'name', a.name)) AS actors,
  JSON_AGG(DISTINCT JSON_BUILD_OBJECT('id', s.id, 'name', s.name)) AS studios
FROM movies m
LEFT JOIN movies_actors ma ON m.id = ma.movie_id
LEFT JOIN actors a ON ma.actor_id = a.id
LEFT JOIN movies_studios ms ON m.id = ms.movie_id
LEFT JOIN studios s ON ms.studio_id = s.id
GROUP BY m.id, m.title, m.release_year; -- 必须包含所有非聚合字段

内容的提问来源于stack exchange,提问作者Nicola Gaioni

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 08:40:32