当前位置: 首页 > news >正文

还在手动拖拽画 ER 图?这款免费代码神器|DBML 语法 + 企业级实战,10 分钟搞定专业数据库设计!

🚀 个人主页 极客小俊
✍🏻 作者简介:web开发者、设计师、技术分享
🐋 希望大家多多支持, 我们一起学习和进步!
🏅 欢迎评论 ❤️点赞💬评论 📂收藏 📂加关注

为什么企业开发必须画 ER 图?

首先一个企业级开发的项目是必须要有数据库蓝图设计的~ 目的是为了让表结构、字段、关系一目了然,避免开发混乱

然后是为了方便团队沟通, 产品、前端、后端、DBA 快速对齐,减少扯皮的情况~

实际开发之前也可以防止一些坑:也就是提前发现设计漏洞, 比如: 冗余、关联错误、关系混乱等等..

对于项目的后期维护成本降低, 例如: `项目迭代、新人接手时,一看ER图就懂这个项目系统的大致结构~

dbdiagram.io 是什么

今天给大家推荐一个10 秒生成 ER 关系图的方法~ 并且可以免费一键生成高清图片~

它就是 dbdiagram.io 一款代码驱动在线数据库 ER 图设计工具~

简单的说它就是一个免费在线 ER 图工具,用一种类似于 SQL语法DBML写代码并且 自动生成专业 ER 图!

官网地址: https://dbdiagram.io/home

如图


当你点击Create your diagram(创建图表)的时候,就可以直接开始画图了~

如图

这完完全全是专门给开发者、数据分析师、做系统的人量身定做的神器啊~ 太狂喜了!

使用之前,最好注册登录一下~~

核心亮点✨

代码写图,不用手拖

不用像传统画图工具那样拖方框、拉线条,只需要写几行简单的语法,它就自动生成干净、专业的 ER 图,效率拉满~

完全免费,永久可用

2026年主打的就是个免费啊~
这款在线ER图工具你个人用完全免费,支持最多 10 个图表,能导出 PNG、PDF、SVG
还能直接生成建表 SQL, 对于个人项目太友好了~

如图

✅ 免费版完全够用

功能 免费版 对你的用处
代码画 ER 图 ✅ 无限用 写 DBML 代码生成汽修店三表关系图
导出 PNG/PDF/SVG ✅ 支持 截图、导出高清图给员工 / 开发看
SQL 导入 / 导出 ✅ 支持 一键生成 SQL 建表语句,直接用
在线保存图表 ✅ 最多 10 个 存系统的表结构,完全够
公开分享图表 ✅ 支持 发链接给外包开发,不用传文件

💸 付费版一般用不上

付费版是给企业团队、大项目用的,个人项目根本不需要!

唯一需要注意的:免费版最多存 10 个图表,个人小型系统表结构顶天也就 7~10 个!

双向同步,一键生成 SQL

你编写专属语法之后所生成的ER图,也能从图反向导出完整的SQL CREATE TABLE建表语句,简直不要太给力~ 🛠️

直接复制到 相应数据库中 就能用,这就相当于画图 = 写建表代码,完全提高效率了~

一些常见数据库都支持: SQL(SQL Server/MySQL)等等..

如图


当然同时也支持导入 SQL 逆向生成 ER 图

在线即用,不用安装

打开浏览器就能用,不用装客户端,还能一键分享链接给其他需要的人~

dbdiagram.io 完整语法核心 DBML 📚

既然要使用这个ER图工具,我们也要了解一下它的一些创建规则和语法!

它用的是 DBML 全称Database Markup Language 数据库标记语言,是专门用来定义数据库结构的极简语法,比 SQL 简单 10 倍

我们在左侧就可以直接开始书写规则~

如图

定义表

语法如下

Table 表名 {字段名 数据类型 [约束, 注释]字段名 数据类型 [约束, 注释]
}

详细解释

表名支持中文, 注意要加双引号 ,比如: Table "会员表"

数据类型:可以写INT/VARCHAR/MONEY/DATETIME 等,和 SQL 完全对应~学过SQL你自然懂!

常用约束:

语法 作用 案例
pk 代表主键(Primary Key) MID INT [pk]
not null 代表非空 MName VARCHAR [not null]
unique 代表唯一 MPhone VARCHAR [unique]
default: 值 代表默认值 MCreateTime DATETIME [default: GETDATE()]
note: '说明' 代表给字段加说明 最后在ER图里会显示! RMoney MONEY [note: '消费金额']
increment 自增 MID INT [pk, increment]

举个栗子 某汽修店的会员表~

Table "会员表" {MID INT [pk, note: '会员ID,主键']MName VARCHAR(10) [not null, note: '会员姓名']MPhone VARCHAR(11) [unique, note: '联系电话']MCreateTime DATETIME [default: `GETDATE()`, note: '开卡时间']
}

这样我们在右侧就能直接看到 效果了!

如图

定义表关系(外键)

这个一般分为三种关系:

< 表示: 一对多(1→N)
> 表示: 多对一(N→1)
- 表示: 一对一(1→1)

这些关系我在前面的数据库设计中也已经讲解过了,还不懂的 ,赶紧去补一补~

一般我们可以在关联字段上面直接写

✅ 内联写法

直接在字段后加 [ref: 关系标 关联表.关联字段],这样就会自动生成关系线!

例如

Table 用户表{id int [pk,note:'用户id']username varchar(10) [not null, note:'用户名']iphone varchar(13) [not null,note:'手机号码']
}Table 订单表 {id int [pk]Ordernumber int [not null,note:'订单号']user_id int [ref: > 用户表.id]
}

如图

定义多对多关系(中间表)

那么多对多这种关系,我们知道要用到一个中间表才行~

举个栗子 学生和课程: 一个学生可以选多门课,一门课可以被多个学生选,这就是多对多关系。

那这里的DBML语法结构如下

Table 学生表{sid int [pk,note:'学生id']uname char [not null,note:'学生姓名']age bit [default:'1',note:'学生性别']
}Table 课程表{cid int [pk,note:'课程id']cname varchar(10) [not null,note:'课程名称']
}Table 学生对应课程表{sid int [ref: > 学生表.sid]cid int [ref: > 课程表.cid]indexes { (sid, cid) [pk] } // 联合主键
}

如图

定义复合主键(多对多表用)

这里,我们来回忆以下 多对多关系

举个栗子

之前说过了,比如: 一个会员可以有多辆车,一辆车也可以被多个会员开 → 这就叫多对多

那么在这种多对多关系的情况下,复合主键就是:MID + 车牌 = 唯一标识
把两个标识绑在一起,绝对不重复、不混乱~

就像是租车公司的车辆一样,会员 ↔ 车辆
一个会员可以有好几辆车,一辆车也可以租给 好几个会员开, 这种关系就叫 多对多

但是多对多关系不能直接存在会员表,也不能直接存在车辆表 必须单独建一张 中间关联表~

也就是会员车辆关联表~

创建语法

indexes {(主键1, 主键2) [pk,name:'复合主键名称']
}

例如

 indexes {(MID, CPlate) [pk, name: 'pk_member_car']}

案例

// 1. 会员表(主表1)
Table "会员表" {MID INT [pk]MName VARCHAR(50)MPhone VARCHAR(20)
}// 2. 车辆表(主表2)
Table "车辆表" {CPlate VARCHAR(20) [pk]CBrand VARCHAR(50)CColor VARCHAR(10)
}// 3. 会员车辆关联表(多对多中间表,核心!)
Table "会员车辆关联表" {MID INT [ref: > "会员表".MID]CPlate VARCHAR(20) [ref: > "车辆表".CPlate]BindTime DATETIMEindexes {(MID, CPlate) [pk, name: 'pk_member_car']}
}

如图

小知识: 复合主键的用途

举个栗子 同一个会员,不能绑定同一个车牌两次!

会员 1001 + 车牌 川 A12345 → 可以存
会员 1001 + 车牌 川 A67890 → 可以存
会员 1002 + 车牌 川 A12345 → 可以存
~
但是:
会员 1001 + 车牌 川 A12345 再存一次 → 直接报错!

这就是复合主键的作用:防止重复绑定!保证数据干净、保证数据唯一、不乱套

就像是你的姓名会和别人的重复,但是你的姓名+你的身份证 = 你的唯一标识~

定义索引

indexes 是专门用来在 dbdiagram 里定义索引的代码块!

写在表里面,专门给字段加索引,让查询变快~

语法格式

indexes {(字段1, 字段2, ...)  [name: '索引名'] // 索引名必须用单引号包裹!
}

举个栗子

Table 订单 {id int [pk]user_id intstatus intindexes {(user_id, status) [name: 'idx_user_status'] }
}

当然,多个字段的索引,也可以分开写成如下形式:

indexes {(字段) [name: '索引名'](字段) [name: '索引名']
}

例如

indexes {(username) [name: 'idx_username'](phone) [name: 'idx_phone']
}

实战案例:企业级业务系统多表设计🚀

这里就用商城系统来举例~

表名 描述 作用
users 用户表 存所有注册用户的信息(账号、密码、手机号等)
categories 商品分类表 存商品的分类(比如「手机」「电脑」「配件」,还支持二级分类,比如「手机 - 苹果」)
products 商品表 存所有上架的商品(比如 iPhone 16、华为 Mate 70 这些具体商品)
orders 订单表 存用户下单的订单(比如「2026-04-15 张三买了 1 台 iPhone」这个订单的总信息)
order_items 订单商品明细表 存订单里的每一件商品(比如张三的订单里,除了 iPhone,还买了个手机壳,就拆成 2 条明细)

先创建这5个ER图, 不分先后~

如下

// 用户表
Table users {id int [pk, increment]username varchar(50) [not null, unique]password varchar(64) [not null]phone varchar(20) [unique]nickname varchar(50)status tinyint [default: 1]create_time datetime [default: `now()`]indexes {(username) [name: 'idx_username'](phone) [name: 'idx_phone']}
}// 商品分类表
Table categories {id int [pk, increment]name varchar(50) [not null]parent_id int [ref: > categories.id, default: 0]sort int [default: 0]indexes {(parent_id) [name: 'idx_parent']}
}// 商品表
Table products {id int [pk, increment]category_id int [ref: > categories.id]name varchar(100) [not null]price decimal(10,2) [not null]stock int [default: 0]status tinyint [default: 1]create_time datetime [default: `now()`]indexes {(category_id) [name: 'idx_category'](name) [name: 'idx_name']}
}// 订单表
Table orders {id int [pk, increment]order_sn varchar(32) [unique, not null]user_id int [ref: > users.id]total_amount decimal(10,2) [not null]pay_status tinyint [default: 0]order_status tinyint [default: 0]create_time datetime [default: `now()`]indexes {(user_id) [name: 'idx_user'](order_sn) [name: 'idx_order_sn']}
}// 订单商品表
Table order_items {id int [pk, increment]order_id int [ref: > orders.id]product_id int [ref: > products.id]product_name varchar(100) [not null]price decimal(10,2) [not null]quantity int [not null, default: 1]indexes {(order_id) [name: 'idx_order'](product_id) [name: 'idx_product']}
}

如图

表与表之间的关系拆解分析

~

1. 分类表 和 商品表一对多

因为1 个分类 下会存在 多个商品!

举个栗子

一个分类比如 手机下面,可以挂 N 个商品, 比如: iPhone、华为、小米
但是一个商品,比如 iPhone Pro max,只能属于一个手机分类下, 它不能同时属于手机电脑 对吧!

从现实角度来说,分类是商品的归属,用来做商品筛选、分类导航(

比如用户点手机分类,就能看到所有手机的商品,如果不建关系,商品就没地方归类,商城就乱成一锅粥~

ER图中所对应的关系如下描述:

1.商品表.分类id 要关联 分类表.id --> 实现多对一

category_id int [ref: > categories.id]
意思就是:商品表的category_id关联分类表的主键(categories.id),所以商品必须归属于某个分类

分类表商品表,这两个表的关系,我们也就梳理完成了!

如图

2.用户表 和 订单表一对多

1个用户 可以有 多个订单 对吧~

举个栗子

一个用户比如张三,他是不是可以下N个订单, 今天买手机、明天买电脑 对吧~

但一个订单,只能属于一个用户, 你总不能说是一个订单同时是张三和李四、王五的吧~

订单必须绑定用户,才能知道谁买的,用来做订单查询、用户中心、售后维权,等等...

没有用户关联的订单是无效且无意义的~

ER图所对应的关系如下

订单表.用户id 要关联 用户表.id : --> 实现多对一

orders.user_id int [ref: > users.id]

意思是:订单表的user_id,关联用户表的主键id,订单必须属于某个用户~才有意义

如图

3.订单表 和 订单商品明细表 一对多

一对多 1 个订单 可能会存在 多个商品明细!

一个订单, 比如张三的订单,里面可以有 N 件商品 手机 + 手机壳 + 充电器...

而每一件商品都拆成一条明细,但一条明细,只能属于一个订单!

这里我们来举个例子方便理解~

你想: 张三的订单里,买了「手机、手机壳、充电器」3 样东西,就生成 3 条明细。
这 3 条明细,全是张三这个订单的,跟李四、王五的订单半毛钱关系都没有!
不可能出现:一条手机的明细,同时算在·张三·和·李四·两个订单里,那账就彻底乱了~

订单表(orders)只存「总单信息」

只记「张三这个订单」的整体情况:谁买的、订单号、总共花了多少钱、什么时候买的,当然根据需求,也有其他的信息~

id user_id order_sn total_amount create_time
1 1001 DD2026001 6298.00 2026-04-15

订单明细表(order_items)存每一件商品的细节(3 条数据,对应 3 件商品)

id order_id product_id product_name price quantity
1 1 10001 iPhone 16 5999.00 1
2 1 10002 手机壳 99.00 1
3 1 10003 充电器 200.00 1

字段解释:

字段名 人话解释
id 这条明细自己的编号(唯一标识)
order_id 核心! 这条明细属于哪个订单(这里全是 1,说明都属于张三的订单)
product_id 买的是哪个商品(对应商品表的 ID)
product_name 商品名字(冗余存一份,防止商品改名后订单看不到历史名称)
price 下单时这件商品的单价(不是现在的价格,是当时买的价格)
quantity 买了几件(比如买 2 个手机壳,这里就是 2)

所以订单表只记总账,明细表记明细账,每一件商品单独一条记录,清清楚楚,不会乱。

ER图对应关联逻辑如下

订单明细表.订单id 关联 订单表.id --> 实现多对一

order_items.order_id int [ref: > orders.id]

明细表的order_id,关联订单表的主键id,明细必须属于某个订单。

如图


如果不拆明细表,直接把商品存在订单表里:一个订单买 3 件商品,就要存 3 条订单数据,订单号重复,统计、对账全乱~
也没法单独统计某件商品卖了多少, 只能看订单总金额, 如果说后续加商品、改商品、退单个商品,全没法操作。

拆成订单明细表,订单表存总信息,明细表存每件商品的细节,逻辑完全清晰~

4.商品表 和 订单商品明细表 一对多

1 个商品 可能存在 多个明细, 说到这里也可以解释刚刚上面有些问题给大家造成的疑惑~

也就是说一个商品 比如 iPhone 17,可以出现在N个订单明细里被张三买、李四买、王五购买

但一条明细,只能对应一个商品~

订单明细表必须绑定商品,这样才能知道订单里买的是什么商品,

后期用来做商品销量统计、库存扣减、售后换货,

如果没有商品关联的订单明细也是无效的~

这个应该很好理解吧!

ER图关系设计逻辑如下

订单明细表.商品id 要关联 商品表.id --> 实现多对一

order_items.product_id int [ref: > products.id]

如图

5.分类表 和 分类表

这里比较特殊, 这里我们要采取一种通用设计模式,自关联一对多

也就是1 个父分类 下面有 多个子分类~

举个栗子

分类表自己自己关联,这样就可以实现多级分类:也就是俗称的无限极分类

比如: 数码产品父分类,下面有手机、电脑子分类, ~~

然后呢手机又可以当父分类,下面有苹果、华为、三星、小米这些子分类~以此类推

到这里肯定有新手朋友会问,包括曾经的我也问过,为啥这么建? 为什么不用单独建父分类表?

那是因为一张表搞定所有层级,灵活扩展,商城分类想加多少级就加多少级,不用改表结构~

否则分类多了表也会增多,维护成本自然增加~

ER图设计逻辑如下

分类表.父级id 关联 分类表.id

categories.parent_id int [ref: > categories.id, default: 0]

分类表的父级parent_id,关联自己的主键id,0代表顶级分类 比如数码产品

那么非 0 代表子分类: 比如 手机的parent_id是数码产品的id~这样无限极递归下去,

这在我们实际业务代码开发中,就要使用到php、java、python来实现了!

如图

所以通过我们的ER图梳理清楚了表之间的关系之后,完整关系链路图就出来了,在项目开发中我们一眼就能看懂~

用户表(users) ↓ 一对多
订单表(orders) ↓ 一对多
订单商品明细表(order_items) ↓ 多对一
商品表(products) ↓ 多对一
商品分类表(categories) ← 自关联 → 自己(多级分类)

组成完整的业务流

用户 (users) → 下订单 (orders) → 订单里的商品明细 (order_items) → 明细对应商品 (products) → 商品属于分类 (categories)

设计ER图的终极目的

数据不冗余

比如用户信息只存在users表,订单里只存user_id,不用把用户名、手机号重复存到订单里,改用户信息只改一张表,不会出错, 查询通过关联查询就可以了!

业务逻辑清晰

订单、商品、用户完全解耦

后续加功能比如:优惠券、积分、售后,直接在对应表加字段,不影响其他表。

性能最优:

每个表只存自己的核心数据,查询、统计、修改都只操作对应关联的表,不会出现大表卡顿

同时也符合企业级项目数据库设计规范!

下单案例,把ER图逻辑关系彻底串起来

比如

张三(users表 id=1)
在手机分类(categories表 id=2)下面,
买了 1 台 iPhone 16(products表 id=100,价格 5999)
买了 1 个手机壳(products表 id=101,价格 29)

那么此时此刻生成订单:

orders表新增一条数据,user_id=1,总金额 6028,订单号20260415123456

同时明细表中生成明细:order_items表新增 2 条数据:

第一条:order_id=刚生成的订单id,product_id=100,价格 5999,数量 1
第二条:order_id=刚生成的订单id,product_id=101,价格 29,数量 1

最后商品归属:products表的category_id=2,对应分类表的手机分类~

✅ 最后总结

以上我讲到的DBML语法也只是一部分常用的

更多语法大家可以参考官方文档~ https://dbml.dbdiagram.io/home

总之dbdiagram.io 绝对是你 高效 + 免费 + 专业的企业级开发ER 图设计首选工具~

我个人向大家发出强烈建议:以后做项目先画 ER 图再写代码,结构清晰、少走弯路!





"👍点赞" "✍️评论" "收藏❤️"

大家的支持就是我坚持下去的动力!

如果以上内容有任何错误或者不准确的地方,🤗🤗🤗欢迎在下面 👇👇👇 留个言指出、或者你有更好的想法,
欢迎一起交流学习❤️❤️💛💛💚💚


更多好玩 好用 好看的干货教程可以点击下方关注❤️微信公众号❤️
说不定有意料之外的收获哦..🤗嘿嘿嘿、嘻嘻嘻🤗!
🌽🍓🍎🍍🍉🍇






http://www.jsqmd.com/news/651488/

相关文章:

  • 从零搭建智能语音设备:基于STM32的I2S音频接口完整配置流程
  • JiYuTrainer:极域电子教室控制解除工具,重新定义课堂自主权
  • MATLAB实战:从语音信号到Mel Spectrogram(梅尔频谱图)的完整实现与参数调优
  • 3步解锁Intel GPU的CUDA超能力:ZLUDA完整配置指南
  • OmenSuperHub终极指南:三步解锁惠普游戏本隐藏性能,告别官方软件臃肿体验
  • 【实战指南】VSCode Git集成失效排查与修复全记录(附环境差异分析)
  • 手把手教你用GCC打包自己的C++工具库:从源码到.so/.a,再到发布给同事用
  • 政治内容
  • 【评测系列2】从零实现 AgentBench评测系统:架构设计与实战
  • 轻量化ASR生态整合:SenseVoice-Small ONNX与Obsidian插件联动教程
  • 【STM32实战指南】SPI与8080双模式驱动OLED显示技术解析
  • LVDS技术在汽车视频传输中的应用与优化
  • 告别命令行恐惧:用Windows Terminal和VS Code图形化搞定Rust环境与第一个项目
  • 如何在Apple Silicon Mac上专业运行iOS游戏:PlayCover终极配置指南
  • HC-06蓝牙模块主从模式实战:从AT指令到双向通信
  • Elasticsearch安全认证实战:从零配置密码与Kibana集成
  • 中东电商入局指南:Noon vs Amazon,出海卖家该如何选择?
  • 朱雀AI检测率高怎么降?比话降AI图文教程:从56%降到0%
  • Windows 11终极优化指南:免费工具让系统运行速度提升51%
  • 手把手教你用MLU370-M8单卡跑通Wav2Lip口播模型(附中文优化思路)
  • 抖音小程序通用支付避坑指南:前端开发者如何用云开发搞定RSA签名难题
  • 快速上手:DCMTK工具包的安装与配置指南
  • 深入解析Nginx启动报错:libcrypto.so.1.1缺失的根源与系统级修复
  • 终极DLSS文件管理方案:5分钟搞定多平台游戏DLSS版本切换
  • 你的无刷电机为啥启动就抖?可能是电感法位置检测没调好(避坑指南)
  • Ubuntu 22.04 LTS 上快速部署Ollama的完整指南(含模型下载与WebUI配置)
  • torch-npu安装指南:从版本匹配到依赖解决
  • 如何让经典《植物大战僵尸》完美适配现代宽屏显示器?PvZWidescreen模组终极指南
  • UniCloud H5项目绑定阿里云域名全流程(含SSL证书踩坑实录)
  • Dism++:Windows系统维护的终极工具,如何用10个技巧提升电脑性能?