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

如何在Postgres中构建Cruise与Proposal多对多关联的表结构?

实现Cruise与Proposal多对多关联的双向查询

1. 数据库表结构设计

多对多关系必须通过中间关联表来承载关联关系,三张表的核心结构示例如下:

Cruise表

  • cruise_id (INT, 主键, 自增):巡航记录唯一ID
  • cruise_name (VARCHAR):巡航名称
  • 其他业务字段(如巡航时间、路线等)

Proposal表

  • proposal_id (INT, 主键, 自增):提案唯一ID
  • proposal_title (VARCHAR):提案标题
  • 其他业务字段(如提案内容、提交时间等)

中间关联表(命名示例:cruise_proposal)

  • cruise_id (INT, 外键关联Cruise.cruise_id)
  • proposal_id (INT, 外键关联Proposal.proposal_id)
  • 联合主键:(cruise_id, proposal_id)(避免重复绑定同一组巡航与提案)

2. 原生SQL查询实现

查询指定Cruise及其关联的所有Proposal

通过JOIN关联三张表,可使用聚合函数将关联提案信息合并为列表,或直接返回关联结果集在业务层组装:

MySQL示例(聚合为字符串列表)

SELECT 
    c.cruise_id,
    c.cruise_name,
    GROUP_CONCAT(CONCAT(p.proposal_id, ':', p.proposal_title) SEPARATOR ', ') AS proposal_list
FROM Cruise c
LEFT JOIN cruise_proposal cp ON c.cruise_id = cp.cruise_id
LEFT JOIN Proposal p ON cp.proposal_id = p.proposal_id
WHERE c.cruise_id = 1 -- 指定要查询的巡航ID
GROUP BY c.cruise_id, c.cruise_name;

PostgreSQL示例(聚合为字符串列表)

SELECT 
    c.cruise_id,
    c.cruise_name,
    STRING_AGG(CONCAT(p.proposal_id, ':', p.proposal_title), ', ') AS proposal_list
FROM Cruise c
LEFT JOIN cruise_proposal cp ON c.cruise_id = cp.cruise_id
LEFT JOIN Proposal p ON cp.proposal_id = p.proposal_id
WHERE c.cruise_id = 1
GROUP BY c.cruise_id, c.cruise_name;

若需要获取每条提案的完整字段,去掉聚合函数直接查询即可,后续在业务代码中将同一巡航的提案整理为列表。

查询指定Proposal及其关联的所有Cruise

逻辑与上述一致,反向关联三张表即可:

MySQL示例

SELECT 
    p.proposal_id,
    p.proposal_title,
    GROUP_CONCAT(CONCAT(c.cruise_id, ':', c.cruise_name) SEPARATOR ', ') AS cruise_list
FROM Proposal p
LEFT JOIN cruise_proposal cp ON p.proposal_id = cp.proposal_id
LEFT JOIN Cruise c ON cp.cruise_id = c.cruise_id
WHERE p.proposal_id = 1 -- 指定要查询的提案ID
GROUP BY p.proposal_id, p.proposal_title;

3. ORM框架实现(常见框架示例)

Java JPA/Hibernate

通过@ManyToMany注解配置双向关联,自动处理中间表的操作:

Cruise实体类

@Entity
@Table(name = "Cruise")
public class Cruise {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer cruiseId;
    private String cruiseName;

    @ManyToMany(fetch = FetchType.LAZY)
    @JoinTable(
        name = "cruise_proposal",
        joinColumns = @JoinColumn(name = "cruise_id"),
        inverseJoinColumns = @JoinColumn(name = "proposal_id")
    )
    private List<Proposal> proposals = new ArrayList<>();

    // Getter、Setter、构造方法省略
}

Proposal实体类

@Entity
@Table(name = "Proposal")
public class Proposal {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer proposalId;
    private String proposalTitle;

    @ManyToMany(mappedBy = "proposals", fetch = FetchType.LAZY)
    private List<Cruise> cruises = new ArrayList<>();

    // Getter、Setter、构造方法省略
}

查询时直接通过实体关联获取列表:

// 查询Cruise及其关联的Proposal
Cruise cruise = entityManager.find(Cruise.class, 1);
List<Proposal> linkedProposals = cruise.getProposals();

// 查询Proposal及其关联的Cruise
Proposal proposal = entityManager.find(Proposal.class, 1);
List<Cruise> linkedCruises = proposal.getCruises();

Python SQLAlchemy

通过relationship和中间表配置双向多对多:

定义实体与关联表

from sqlalchemy import Table, Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship, sessionmaker
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

# 中间关联表
cruise_proposal = Table(
    'cruise_proposal', Base.metadata,
    Column('cruise_id', Integer, ForeignKey('cruise.cruise_id'), primary_key=True),
    Column('proposal_id', Integer, ForeignKey('proposal.proposal_id'), primary_key=True)
)

class Cruise(Base):
    __tablename__ = 'cruise'
    cruise_id = Column(Integer, primary_key=True)
    cruise_name = Column(String)
    proposals = relationship('Proposal', secondary=cruise_proposal, back_populates='cruises')

class Proposal(Base):
    __tablename__ = 'proposal'
    proposal_id = Column(Integer, primary_key=True)
    proposal_title = Column(String)
    cruises = relationship('Cruise', secondary=cruise_proposal, back_populates='proposals')

查询示例:

# 创建数据库会话
Session = sessionmaker(bind=engine)
session = Session()

# 查询Cruise及其关联的Proposal
target_cruise = session.query(Cruise).get(1)
print([p.proposal_title for p in target_cruise.proposals])

# 查询Proposal及其关联的Cruise
target_proposal = session.query(Proposal).get(1)
print([c.cruise_name for c in target_proposal.cruises])

内容的提问来源于stack exchange,提问作者Margaux Flores

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 07:01:11