Erdmates Logo
Erdmates
返回博客
数据建模模式层次结构多语言标签PostgreSQL

实战数据建模模式:层次结构、标签、多语言的正确设计方法

通过PostgreSQL示例,学习层次结构(树形)、标签/分类、多语言支持等反复出现的数据建模模式。

2026年3月21日10 分钟阅读

为什么要学习数据建模模式?

在数据库设计中,某些结构会反复出现:层次结构(菜单、分类、组织架构)、标签/标签、多语言支持、历史记录管理等。了解这些模式的最佳实践方案,可以减少试错,设计出更高效的Schema。

模式1:层次结构(树形结构)

分类、菜单、组织架构、评论线程等具有父子关系的树形结构。

方法1:邻接表(Adjacency List)— 最简单

CREATE TABLE categories (
  id        INT PRIMARY KEY,
  name      VARCHAR(100) NOT NULL,
  parent_id INT REFERENCES categories(id)  -- 自引用
);
  • 优点:结构简单,直接父/子查询快速
  • 缺点:获取完整树需要递归查询
-- PostgreSQL递归CTE获取完整树
WITH RECURSIVE category_tree AS (
  SELECT id, name, parent_id, 0 AS depth
  FROM categories WHERE parent_id IS NULL
  UNION ALL
  SELECT c.id, c.name, c.parent_id, ct.depth + 1
  FROM categories c
  JOIN category_tree ct ON c.parent_id = ct.id
)
SELECT * FROM category_tree ORDER BY depth, name;

方法2:闭包表(Closure Table)— 最灵活

CREATE TABLE category_closure (
  ancestor_id   INT REFERENCES categories(id),
  descendant_id INT REFERENCES categories(id),
  depth         INT,
  PRIMARY KEY (ancestor_id, descendant_id)
);

将所有祖先-后代关系存储在独立表中,读写均高效。

模式2:标签(Tag)系统

在文章、商品等上添加多个标签,典型的N:M关系。

基本结构

CREATE TABLE tags (
  id   INT PRIMARY KEY,
  name VARCHAR(50) UNIQUE NOT NULL
);

CREATE TABLE post_tags (
  post_id INT REFERENCES posts(id) ON DELETE CASCADE,
  tag_id  INT REFERENCES tags(id) ON DELETE CASCADE,
  PRIMARY KEY (post_id, tag_id)
);

标签检索

-- 包含所有指定标签的文章(AND条件)
SELECT p.title
FROM posts p
JOIN post_tags pt ON p.id = pt.post_id
JOIN tags t ON pt.tag_id = t.id
WHERE t.name IN ('database', 'sql')
GROUP BY p.id, p.title
HAVING COUNT(DISTINCT t.name) = 2;

PostgreSQL数组简化方案

CREATE TABLE posts (
  id    INT PRIMARY KEY,
  title VARCHAR(255),
  tags  TEXT[]
);

CREATE INDEX idx_posts_tags ON posts USING GIN(tags);
SELECT title FROM posts WHERE tags @> ARRAY['database', 'sql'];

模式3:多语言(i18n)支持

方法1:翻译表

CREATE TABLE products (
  id    UUID PRIMARY KEY,
  price DECIMAL(10,2)
);

CREATE TABLE product_translations (
  product_id  UUID REFERENCES products(id) ON DELETE CASCADE,
  language    CHAR(2) NOT NULL,
  name        VARCHAR(255) NOT NULL,
  description TEXT,
  PRIMARY KEY (product_id, language)
);
-- 获取中文,无则回退到英文
SELECT p.id, COALESCE(t_zh.name, t_en.name) AS name
FROM products p
LEFT JOIN product_translations t_zh ON p.id = t_zh.product_id AND t_zh.language = 'zh'
LEFT JOIN product_translations t_en ON p.id = t_en.product_id AND t_en.language = 'en';

方法2:JSONB列(PostgreSQL)

CREATE TABLE products (
  id    UUID PRIMARY KEY,
  price DECIMAL(10,2),
  name  JSONB  -- {"zh": "智能手机", "en": "Smartphone", "ko": "스마트폰"}
);

CREATE INDEX idx_products_name ON products USING GIN(name);
SELECT name->>'zh' AS name_zh FROM products;

模式4:软删除(Soft Delete)

不物理删除,而是用deleted_at时间戳标记已删除。

CREATE TABLE users (
  id         UUID PRIMARY KEY,
  email      VARCHAR(255),
  deleted_at TIMESTAMP  -- NULL=活跃,有值=已删除
);

SELECT * FROM users WHERE deleted_at IS NULL;

-- 允许删除后重新使用邮箱的部分唯一索引
CREATE UNIQUE INDEX idx_users_email_active
  ON users(email) WHERE deleted_at IS NULL;

使用Erdmates可视化模式

Erdmates的ERD编辑器帮助您可视化设计这些复杂的数据建模模式——闭包表的自引用关系、翻译表的复合键结构等,并将其以直观的图表形式与团队共享。

总结

选择正确的数据建模模式直接影响应用程序的性能和可维护性。由于每种情况的权衡取舍不同,请综合考虑数据的读写比例、查询模式和数据量,选择最优方案。

立即使用Erdmates开始

使用AI驱动的ERD编辑器开始数据库设计。免费使用。

免费试用演示免费开始

相关文章

bigint、UUID v4 还是 UUID v7?主键选择指南(含 PostgreSQL 18 的 uuidv7())

比较 bigint、UUID v4 和 UUID v7 主键的优缺点,介绍 PostgreSQL 18 内置的 uuidv7() 带来的变化,以及不同场景下的选择方法。

被AI代理读取的数据库:MCP时代的模式设计原则

AI代理通过MCP直接连接数据库的情况越来越多。本文整理如何设计名称、注释和约束,让代理不会误解模式,以及如何安全地划分权限。

PostgreSQL 19预览:影响模式设计与运维的变化(基于Beta 4)

以官方发布说明(Beta 4)为准,整理即将正式发布的PostgreSQL 19的变更中直接影响表设计、迁移和运维的内容。同时介绍在Beta阶段被撤回的功能,以及升级前需要确认的行为变化。

Erdmates Logo
Erdmates

使用自然语言设计数据库,通过AI驱动的ERD编辑器与团队实时协作。

服务

  • 功能介绍
  • 价格方案
  • 演示
  • SQL 转 ERD

资源

  • 博客
  • 帮助
  • 联系我们

法律声明

  • 服务条款
  • 隐私政策
  • 第三方提供
公司名称: Produx|代表人: 최정회
地址: 강원특별자치도 원주시 솔우물2길 4-1 (무실동)
电子邮件: master@erdmates.com
隐私保护负责人: 최정회|托管服务提供商: Amazon Web Services

© 2026 Produx. 版权所有。