直接回答:先判断缺的是“可空扩展位”还是“新实体关系”。如果新需求只是给现有对象补一两个可选属性,优先加字段或扩展表;如果新需求会改变一对多、多对多关系,或需要独立生命周期,就应新建关联表并迁移,而不是继续往主表塞字段。判断依据是数据是否共享、是否可空、是否参与查询排序,以及旧数据能否在不停机窗口内补齐默认值。
假设一个茂名本地服务站的咨询表单最初只记录姓名、电话、需求描述。上线后运营希望增加“意向区域”和“可联系时段”。这两个字段都依附于同一条咨询记录,没有独立生命周期,旧记录可以统一填“未填写”。此时选择在主表增加可空列,并给旧数据补默认值,比拆表更省事。
实施动作要分三步:先在开发库加列并写迁移脚本;再用ALTER TABLE inquiry ADD COLUMN intent_area VARCHAR(64) NULL这类语句在预发布环境演练;最后在写入接口和后台展示同时补上字段映射。动作的结果会影响下一步:如果预发布演练发现旧查询语句使用了SELECT *,就要先改成显式列名,否则上线后新增列可能让旧报表错位。这个顺序不能颠倒,因为字段扩展的风险通常不在加列本身,而在依赖全列查询的旧代码。
例外是字段需要参与唯一约束或高频过滤。若“意向区域”要和手机号组成唯一键,或每天按区域统计,就不能只加可空列,还要评估索引长度和写入放大。此时应把区域抽成字典表,主表只存区域ID。
另一种情况是上线后发现一条咨询记录需要对应多次跟进,每次跟进有跟进人、时间、结果。跟进记录不属于原咨询对象,数量也不固定。继续在主表加“跟进1、跟进2”字段,会很快遇到上限,查询也变得别扭。此时应新建inquiry_follow_up表,用inquiry_id关联原记录。
选择依据可以看三个信号:同一对象是否需要无限追加子项;子项是否要单独按时间排序或按跟进人筛选;子项是否会被单独删除或修改。只要命中两个,就应建关联表。实施时先建新表并双写,再回填历史数据,最后切换读取路径。双写期间要记录写入失败日志,因为回填和双写不一致时,后续切换读取会暴露脏数据。这个动作的结果决定能否进入下一步:只有双写对账差异收敛到可解释范围,才适合停掉旧字段写入。
假设示例:原表已有三百条咨询,其中八十条在备注里写了多次跟进。迁移脚本不能直接按备注拆分,因为格式不统一。更稳妥的做法是先保留备注原文,新建跟进表只回填可识别的首条记录,其余由人工确认。这里的数字只用于说明比较方法,不代表真实项目规模。
SELECT *或位置绑定,加字段前先改查询方式。这组证据不能只看一条。比如“可联系时段”虽然可空,但如果要按时间段做排班统计,它就会从普通属性变成查询维度,这时加字段后仍要补索引,甚至拆成时段字典表。判断的落点是查询路径,而不是字段名称。
无论加字段还是拆表,都要先写回滚方案。加字段的回滚相对简单,停写新字段并保留列即可;拆表的回滚更麻烦,因为新表可能已经产生旧表没有的数据。因此拆表时应保留一段时间的双写,并让新表主键可追溯来源。
回填动作的结果直接决定切换时机。若回填后抽查发现旧记录关联数量对不上,应先修数据而不是继续切换读取。另一个实际动作是给新字段或新表补监控:统计空值率、关联失败数和写入延迟。空值率高不一定是迁移失败,也可能是用户本来就不填;关联失败数持续上升才更值得先查外键和事务边界。
例外情况是站点仍在上线初期、数据量很小。此时可以停写几分钟做一次性迁移,但仍要提前导出备份,并确认备份可恢复。数据量小不等于可以跳过备份验证,因为恢复流程没演练过,真出问题时仍然会卡住。
本地项目常见的情况是上线后业务方临时增加筛选条件或统计口径。若字段扩展不影响现有页面,优先加可空列并补默认值,能让功能先可用;若新需求已经让主表出现重复组或逗号分隔值,就应停止继续加列,转为关联表。逗号分隔值看似省事,但会让按区域统计、按跟进人筛选变得不可靠,后续每次查询都要拆字符串。
取舍标准可以落到一个问题上:新数据是否会被单独更新或删除。会,就拆表;不会,就先加字段。这个标准不需要等到数据量很大才用,越早判断,迁移成本越低。