资讯详情 CRM数据库表设计实战:从需求说明书到可落地的17张SQL表
📅 2026/10/3 19:08:16
简介本资源是一份面向数据库设计初学者与CRM系统开发者的《CRM客户关系管理系统数据库表设计需求规格说明书》聚焦企业级权限管理、销售流程跟踪与客户信息建模等核心场景。文档完整定义了10张关键数据表含角色、菜单、权限、用户、销售机会、客户、联系人、交往记录、流失客户及开发计划表涵盖主外键约束、字段类型、长度、空值规则及业务语义说明可直接用于SQL Server或兼容数据库的建模与开发落地。资源为单文件Word文档.doc格式大小191KB结构清晰、字段注释详尽适合作为课程设计参考、毕业项目数据库设计蓝本或团队开发规范依据。目前已有296人学习下载读者可快速掌握CRM系统中多角色权限控制、销售漏斗数据关联、客户全生命周期字段体系等实战设计要点。1. 为什么一份像样的 CRM 数据库表设计文档比写十行 CRUD 代码还难落地你手头那份标着“CRM客户关系管理系统数据库表设计需求规格说明书(1).doc”的 Word 文件大概率不是被锁在共享盘角落吃灰就是刚被产品经理甩进钉钉群、附言“今晚下班前给开发看下”。但现实是90% 的 CRM 项目卡在第一关——不是接口联调失败不是前端样式错位而是用户信息表字段命名和 null 约束对不上业务口径线索表和商机表的生命周期状态机压根没对齐连“客户”到底指自然人还是企业法人都没共识。这不是文档格式问题是业务语义到数据结构的翻译断层。这份说明书真正的价值不在于它多厚、多规范而在于它能否让销售、客服、IT、DBA 在同一张 ER 图上说同一种话。本文不讲 ISO/IEC/IEEE 29148 标准怎么套模板只讲一线工程师怎么把“需求规格说明书”这六个字变成可建表、可索引、可查、可改、可审计的 17 张真实 SQL 表——从字段粒度、状态流转、历史留痕到权限隔离每一步都踩过坑、验过数、跑过压测。适合正在接手 CRM 改造、准备本地部署 Microsoft Dynamics CRM 替代方案、或想用免费 CRM 框架如 Odoo、EspoCRM做深度定制的后端/全栈工程师。2. 从需求说明书到物理表三步拆解核心实体与关联逻辑CRM 系统不是一堆表的堆砌而是业务动作在数据层的镜像。一份合格的需求规格说明书必须能映射出三类关键实体主体Who、对象What、动作When How。我们以说明书里高频出现的“第1关:数据库表设计 - 用户信息表”为锚点反向推导出最常被忽略的底层约束。2.1 主体层用户、客户、联系人三者为何不能合并在一张表很多团队图省事把user_id、customer_id、contact_id全塞进t_user表加个type字段区分。这是 CRM 数据库翻车的第一高发区。真实业务中用户User系统登录者有账号、密码、角色、部门归属受 RBAC 控制客户Customer销售跟进的目标单位可能是企业含统一社会信用代码、注册资本、行业分类或自然人需身份证号脱敏存储联系人Contact客户下的具体对接人一个客户可有多个联系人每个联系人有职位、手机、邮箱、微信等独立属性。三者存在1:N:N 关系链1 个 User 可代表多个 Customer如销售总监管理多个客户1 个 Customer 可有 N 个 Contact1 个 Contact 只属于 1 个 Customer。强行合并会导致权限控制失效给 User 分配的菜单权限无法精准作用于其负责的 Customer历史记录错乱某 Contact 的沟通记录被误归到其所属 Customer 的其他 Contact 下扩展性崩溃当需要为 Customer 增加“股权结构图”附件或为 Contact 增加“微信聊天截图”时字段爆炸式增长。提示不要用t_user表承载业务身份。标准做法是建三张独立表并通过外键明确关联t_user系统账户t_customer客户主数据t_contact联系人含customer_id外键2.2 对象层线索Lead、商机Opportunity、合同Contract的状态机如何设计才不漏单需求说明书里常写“线索可转为客户”但没写清楚“转”这个动作背后的数据迁移规则。我们按实际销售流程定义三张核心业务表及其状态字段表名核心状态字段典型值状态流转约束t_leadstatusnew,contacted,qualified,disqualified,convertedconverted后必须生成t_customert_contact记录且t_lead不可再编辑t_opportunitystageprospecting,proposal,negotiation,closed_won,closed_loststage变更需记录stage_updated_at和操作人stage_updated_byclosed_won必须关联t_contractt_contractstatusdraft,signed,executing,completed,cancelledsigned状态需校验sign_date非空且amount 0completed前必须有actual_end_date关键细节所有状态字段必须是 ENUM 或引用字典表如t_dict_status禁止用字符串硬编码。否则后续报表统计、BI 取数、前端下拉选项全部崩盘。例如t_opportunity.stage若存proposal而非2当销售流程升级新增demo阶段时旧数据无法自动兼容。2.3 动作层沟通记录、任务、文件附件如何实现“一次录入多处可见”CRM 的价值在于行为留痕。但很多设计把t_communication沟通记录简单设为content TEXT导致无法检索、无法分析、无法联动。正确做法是结构化拆解CREATE TABLE t_communication ( id BIGINT PRIMARY KEY AUTO_INCREMENT, type ENUM(call, email, meeting, wechat, sms) NOT NULL, -- 明确沟通类型 subject VARCHAR(255) NOT NULL, -- 主题用于列表页快速识别 content TEXT, -- 详细内容支持富文本时建议存 HTML 片段 duration_seconds INT DEFAULT 0, -- 通话时长/会议时长用于销售效能分析 related_to_type ENUM(lead, customer, opportunity, contact) NOT NULL, related_to_id BIGINT NOT NULL, -- 关联主键实现跨实体挂载 created_by BIGINT NOT NULL, -- 操作人 created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, INDEX idx_related (related_to_type, related_to_id), -- 关键查询索引 INDEX idx_user_time (created_by, created_at) -- 销售个人工作流索引 );这个设计让一条沟通记录可同时挂在“线索 A”、“客户 B”、“商机 C”下前端按需聚合展示后台按related_to_type related_to_id精准推送消息。同理t_task表需包含assignee_id指派人、due_date截止日、priority优先级、status待处理/进行中/已完成/已取消并强制要求status completed时必须填写completed_at和completed_by—— 这是销售过程数字化的底线。3. 字段级设计那些说明书里没写、但上线必炸的 7 类细节需求规格说明书往往只列“客户名称、联系电话、地址”却从不提“电话要不要分固话/手机地址要不要拆成省市区街道名称要不要支持繁体/生僻字”。这些看似琐碎的字段设计直接决定系统能否过等保、能否接 BI、能否支撑千人千面营销。3.1 字符串字段长度、编码、校验一个都不能少客户名称nameVARCHAR(200)是底线。理由中国公司全称最长可达 180 字如“北京中关村科技园区发展股份有限公司”加上英文/括号/符号200 安全。必须用utf8mb4编码否则 emoji、生僻字如“䶮”、“堃”存不进去。注意MySQL 5.7 默认innodb_large_prefixON但若用utf8mb4VARCHAR(200)索引前缀长度需显式指定如INDEX idx_name (name(191))否则建表报错。联系电话phone拆成mobile手机号、tel固话、wechat微信号三字段。mobile用CHAR(11) 正则校验^1[3-9]\d{9}$tel用VARCHAR(20)含区号、分机号如010-88881234-801wechat用VARCHAR(64)微信 ID 规则宽松但上限 64 字符。提示绝不允许phone单字段存多种格式否则导出 Excel 时固话被 Excel 自动转成科学计数法如02112345678→2.112345678E10销售投诉率飙升。地址address拆为province、city、district、street、postal_code五字段。province/city/district用字典表关联ID 名称street用VARCHAR(255)postal_code用CHAR(6)。好处地图打点、区域销售分析、物流配送路由全部可算。3.2 数值与时间字段精度、时区、默认值的血泪经验金额amountDECIMAL(18,2)是铁律。FLOAT或DOUBLE会导致 0.10.2≠0.3财务对账直接翻车。18 位总长含小数点后 2 位覆盖亿元级合同无压力。注意DECIMAL(10,2)看似够用但某客户签了 12.34 亿合同1234000000.0010 位不够存线上直接报错。日期时间created_at/updated_at统一用DATETIME非TIMESTAMP时区设为Asia/Shanghai。TIMESTAMP会随 MySQL 服务器时区变更自动转换导致日志时间错乱。DATETIME存的是绝对时间稳定可靠。提示所有业务时间字段如next_follow_up_time,contract_sign_date必须带_time或_date后缀避免与id、status等字段混淆。状态标识is_deleted/is_active用TINYINT(1)0/1绝不用BOOLEANMySQL 实际是TINYINT别名但 ORM 映射易出错。is_deleted默认0软删除时置1并加deleted_at DATETIME字段便于审计。避坑is_active和status字段不能共存status已含active/inactive/pending等值再加is_active属逻辑冗余维护成本翻倍。3.3 外键与索引不是所有关联都要加外键但所有查询都要有索引外键FOREIGN KEY仅在强一致性场景使用t_contact.customer_id → t_customer.id、t_opportunity.customer_id → t_customer.id。禁止在外键上设ON DELETE CASCADECRM 中删除客户必须走审批流而非数据库级级联删掉所有商机、合同、沟通记录。提示t_user.created_by不加外键。因为t_user表可能被定时归档而创建人记录需永久保留用逻辑外键应用层校验更安全。索引INDEX每张表至少有 3 类索引主键索引InnoDB 自带关联查询索引如t_communication的(related_to_type, related_to_id)高频查询索引如t_customer的(status, updated_at)查最近更新的活跃客户。注意LIKE %关键词%查询永远走不了索引搜索客户名必须用全文索引FULLTEXT或接入 Elasticsearch别指望name LIKE %华为%能快。4. 避坑指南CRM 数据库上线前必须验证的 5 个致命陷阱再完美的设计文档落地时也会因环境差异、认知偏差、历史包袱暴雷。以下是我在 7 个 CRM 项目中踩过的真坑按现象→原因→解决三步还原。4.1 现象销售反馈“找不到昨天新建的线索”但数据库里明明有记录原因t_lead.created_at字段用了CURRENT_TIMESTAMP默认值但应用服务器时区为UTC数据库时区为Asia/Shanghai导致时间差 8 小时前端按“今日”筛选时漏掉。解决所有created_at/updated_at字段默认值统一写为DEFAULT CURRENT_TIMESTAMP且应用层插入时显式传入NOW()时间戳由应用服务器生成杜绝时区依赖。4.2 现象导出客户列表 Excel 时部分姓名显示为??或乱码原因数据库字符集为utf8仅支持 3 字节 UTF-8但客户姓名含 4 字节 emoji如 或生僻字如 “龘”存入后截断。解决建库时指定CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci所有表、字段、连接字符串JDBC URL 加?characterEncodingutf8mb4同步升级。4.3 现象商机阶段变更后销售经理看不到下属的最新进展原因t_opportunity.updated_at字段未设ON UPDATE CURRENT_TIMESTAMP且应用层未主动更新该字段导致按“最后更新时间”排序失效。解决建表时强制添加updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMPORM 更新时忽略此字段由 DB 自动维护。4.4 现象搜索“上海”客户时返回北京、深圳的记录原因t_customer.city字段存的是“上海市”但前端搜索框输入“上海”WHERE city LIKE %上海%匹配到“北京市”含“上海”二字。解决城市字段用字典表 ID 存储city_id INT搜索时用JOIN t_dict_city ON t_customer.city_id t_dict_city.id WHERE t_dict_city.name 上海精确匹配。4.5 现象批量导入 10 万条联系人后t_contact表查询变慢 10 倍原因导入脚本未关闭唯一索引如mobile字段的UNIQUE约束每插一条都触发全表扫描去重O(n²) 复杂度。解决大批量导入前ALTER TABLE t_contact DROP INDEX uk_mobile导入完成后再ADD UNIQUE INDEX uk_mobile (mobile)或改用INSERT IGNORE 事后去重。5. 权限与审计让 CRM 数据库从“能用”走向“可信”CRM 存的是企业命脉——客户资源、销售过程、合同金额。一份合格的需求规格说明书必须包含数据权限与操作审计的设计条款。这不是锦上添花而是合规底线尤其金融、医疗类客户。5.1 行级权限RLS如何让销售只能看到自己名下的客户传统 RBAC角色权限只能控制“能看客户列表”无法控制“能看到哪些客户”。必须引入行级权限。MySQL 8.0 原生支持 RLS但多数项目用应用层模拟-- 在查询客户时动态拼接 WHERE 条件 SELECT * FROM t_customer WHERE status active AND (owner_id ? OR ? IN (SELECT user_id FROM t_user_role WHERE role_id IN (SELECT role_id FROM t_role_permission WHERE permission customer_view_all)));更优雅的做法是建视图v_customer_accessible将权限逻辑封装CREATE VIEW v_customer_accessible AS SELECT c.* FROM t_customer c JOIN t_user u ON c.owner_id u.id OR u.department_id c.department_id WHERE u.id current_user_id;应用层查询统一走SELECT * FROM v_customer_accessibleDBA 只需维护视图逻辑开发无需感知权限细节。5.2 操作审计谁在什么时候改了客户的手机号CRM 最怕“静默修改”——销售私下篡改客户联系方式导致市场部群发短信发错人。必须记录所有敏感字段变更CREATE TABLE t_customer_audit ( id BIGINT PRIMARY KEY AUTO_INCREMENT, customer_id BIGINT NOT NULL, field_name VARCHAR(50) NOT NULL, -- mobile, wechat, email old_value TEXT, new_value TEXT, operator_id BIGINT NOT NULL, operated_at DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_customer_time (customer_id, operated_at) ); -- 应用层在更新 t_customer.mobile 前先 INSERT 一条审计记录 INSERT INTO t_customer_audit (customer_id, field_name, old_value, new_value, operator_id) VALUES (?, mobile, ?, ?, ?);提示审计表不加外键避免锁表用异步写入如 Kafka Flink 日志管道保证主业务不被拖慢。5.3 敏感字段加密身份证号、银行卡号必须密文存储需求说明书里常写“存储客户身份证号”但没写“怎么存”。明文存储违反《个人信息保护法》必须加密方案选择AES-256-GCM加密认证密钥由 KMS密钥管理服务托管应用层调用 KMS API 加解密字段设计id_card_encrypted VARBINARY(512)id_card_iv VARBINARY(16)初始向量使用约束解密操作仅限特定接口如客户实名认证前端永远看不到明文。# Python 示例调用阿里云 KMS 解密 from aliyunsdkkms.request.v20160120 import DecryptRequest from aliyunsdkcore.client import AcsClient def decrypt_id_card(encrypted_data: bytes) - str: request DecryptRequest.DecryptRequest() request.set_CiphertextBlob(encrypted_data.hex()) response client.do_action_with_exception(request) return json.loads(response)[Plaintext]绝不允许在数据库里存 Base64 编码的“伪加密”字符串——Base64 不是加密只是编码毫无安全意义。6. 验证与演进用 3 个真实 SQL 脚本检验你的设计是否经得起实战设计再完美不跑 SQL 就是纸上谈兵。我习惯用以下 3 个脚本在本地 MySQL 实例上一键验证核心路径是否通畅。它们不是测试用例而是业务生命力的探测器。6.1 脚本 1模拟销售全流程验证状态机与关联完整性-- 1. 创建线索 INSERT INTO t_lead (name, mobile, status, created_by) VALUES (张三科技, 13800138000, new, 1001); -- 2. 转为客户触发业务逻辑 INSERT INTO t_customer (name, industry, status, owner_id) VALUES (张三科技, IT服务, active, 1001); INSERT INTO t_contact (customer_id, name, position, mobile, email) VALUES (LAST_INSERT_ID(), 张三, CEO, 13800138000, zhangzhan.com); -- 3. 创建商机 INSERT INTO t_opportunity (customer_id, name, amount, stage, owner_id) VALUES (LAST_INSERT_ID(), ERP系统采购, 1200000.00, prospecting, 1001); -- 4. 记录首次沟通 INSERT INTO t_communication (type, subject, content, related_to_type, related_to_id, created_by) VALUES (call, 初次电话沟通, 介绍产品功能预约演示, opportunity, LAST_INSERT_ID(), 1001); -- ✅ 验证查商机详情应自动关联客户、联系人、沟通记录 SELECT o.name AS opportunity_name, c.name AS customer_name, ct.name AS contact_name, com.subject AS last_communication FROM t_opportunity o JOIN t_customer c ON o.customer_id c.id JOIN t_contact ct ON c.id ct.customer_id LEFT JOIN t_communication com ON com.related_to_type opportunity AND com.related_to_id o.id WHERE o.id LAST_INSERT_ID();预期结果一行记录字段完整无 NULL。若customer_name或contact_name为 NULL说明外键关联或插入顺序有误。6.2 脚本 2压力测试索引有效性验证千万级数据查询性能-- 模拟 100 万条沟通记录生产环境常见量级 INSERT INTO t_communication (type, subject, content, related_to_type, related_to_id, created_by, created_at) SELECT ELT(FLOOR(1 RAND() * 5), call, email, meeting, wechat, sms), CONCAT(沟通主题-, seq), CONCAT(沟通内容详情编号, seq), ELT(FLOOR(1 RAND() * 3), lead, customer, opportunity), FLOOR(1 RAND() * 100000), FLOOR(1001 RAND() * 100), DATE_SUB(NOW(), INTERVAL FLOOR(RAND() * 365) DAY) FROM ( SELECT row : row 1 as seq FROM (SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t1, (SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9) t2, (SELECT row : 0) t3 LIMIT 1000000 ) seqs; -- ✅ 验证查某销售最近 100 条沟通应在 100ms 内返回 SELECT * FROM t_communication WHERE created_by 1001 ORDER BY created_at DESC LIMIT 100;预期结果执行时间 100ms。若超时检查idx_user_time (created_by, created_at)索引是否存在或created_by是否为BIGINT若误设为INT索引失效。6.3 脚本 3审计合规检查验证敏感操作可追溯-- 模拟修改客户手机号高危操作 UPDATE t_customer SET mobile 13900139000, updated_at NOW() WHERE id 1; -- ✅ 验证审计表必须有一条对应记录 SELECT ca.field_name, ca.old_value, ca.new_value, u.username AS operator_name FROM t_customer_audit ca JOIN t_user u ON ca.operator_id u.id WHERE ca.customer_id 1 AND ca.field_name mobile ORDER BY ca.operated_at DESC LIMIT 1;预期结果返回 1 行old_value为原手机号new_value为新手机号operator_name为操作人姓名。若无记录说明应用层未调用审计写入逻辑。做完这三步验证你的 CRM 数据库表设计就不再是 Word 文档里的静态文字而是一套能呼吸、可生长、抗压、合规的活系统。我坚持一个习惯每次需求评审会前先把这三段 SQL 贴到会议纪要里让产品经理、销售总监、CTO 一起看结果——数据不会说谎它比 PPT 上的“高可用”“高性能”更有说服力。希望帮到你。本文还有配套的精品资源点击获取