扩展订单表以支持取货选项

MySQLBeginner
立即练习

介绍

一家小店正在添加取货点。旧版结账客户端提供订单 ID、顾客、整数分金额和备注;新版还提供取货代码。现有订单及其商品关联必须继续可用。默认值能兼容省略新列的写入,却无法告诉我们历史顾客实际选择了哪里取货。

你将交付 expand.sql 和已扩展的实际订单表,使用两个预备客户端,再观察数据事务与 MySQL DDL 之间的真实边界。前置知识为基本终端导航,以及 MySQL SELECT/WHERE、INSERT/UPDATE、列、主键和默认值。无需 Python 或应用开发。本独立单元提供专用 Oracle MySQL 和 Terminal;通用数据库服务与本课程无关。

只在 /home/labex/project/pickup-desk 中工作。可以向 orders 添加指定取货列,并只为演示更新或扩展隔离的 ddl_demo。保留所有原始订单 ID、顾客、金额、备注、商品、取货点和审批表,以及提供的工具。只允许添加下文指定的三笔练习订单。这是受控维护窗口,不是在线、无锁或零停机迁移。作者估计时长包含阅读和普通错误处理,尚未经初学者计时测试。

在两个客户端保持可用时扩展表结构

本步骤中,你将创建正常旧版订单,添加兼容的取货列,并读取两个客户端版本创建的真实订单。

从 Terminal 的 shell 提示符开始。cd 改变当前目录,绝对路径可从任意起始目录使用。./shop 运行该目录中准备好的命令。这些是 shell 命令,不是 MySQL 提示符下的输入:

cd /home/labex/project/pickup-desk
./shop status
./shop points

原始订单头为 101 / Sam / 2100、102 / Sam / 3400 和 103 / Lee / 0,各有自己的备注和商品。两笔 Sam 订单具有不同标识。金额以整数分保存,零也是有效值。status 和 show 读取实际存储值,绝不迁移记录或替换取货选择。

预备旧版客户端接受 old order_id customer total_cents sku quantity。它在同一原生事务中,用明确的旧列名插入订单头,并插入一条商品明细。成功调用会提交;原生 SQL 错误会停止批处理并关闭连接,回滚待提交数据变更。创建允许的普通订单,再读取它:

./shop old 201 Mina 1600 MUG 1
./shop show 201

订单头包含 ID 201、Mina、1600 和 Created by legacy client;明细包含 MUG,数量为 1。返回的 pickup_column: false 表示此字段尚未部署。新版客户端使用相同参数,后面再加取货代码。下一命令预期故意失败,原生错误 1054 表示未知列 pickup_code,退出状态非零。它不能留下订单头或明细:

./shop new 203 Pia 1900 CARD 2 garden
./shop show 203

读取显示空订单头和商品列表。缺列是表结构错误,不是未知取货代码,也不是成功的空结账。

添加可为空、明确默认为 counter 的新列。ALTER TABLE 修改定义,ADD COLUMN 添加字段。VARCHAR(16) 保存简短取货代码。NULL 在扩展阶段暂时允许明确的未知值。旧客户端省略列时,DEFAULT 'counter' 提供兼容值。扩展后,预先存在的行也显示此值,因此它不能证明历史选择。下一个独立实验会先处理已批准历史,再收紧最终规则。

保存完整 SQL 文件。cat > expand.sql 替换这个文件。带引号的 here-document 标记 'SQL' 让 shell 按字面复制各行,直到末尾独占一行的 SQL。每条 SQL 语句以分号结束:

cat > expand.sql <<'SQL'
ALTER TABLE orders ADD COLUMN pickup_code VARCHAR(16) NULL DEFAULT 'counter';
SQL

apply 以课程迁移角色,在一个原生 MySQL 批处理连接中原样运行保存文件。它不推断、不回填,也不修复缺失 SQL:

./shop apply expand.sql
./db -e "SHOW COLUMNS FROM orders;"

./db 连接本课程专用数据库;-e 执行后面的带引号 SQL 后退出。双引号让空格和分号保留在同一个 shell 参数中。读取取货列类型、是否可空和默认值:varchar(16)、YES、counter。其他原始字段仍存在。

再次使用旧版客户端,并重试先前被拒绝的新版订单:

./shop old 202 Omar 1800 BAG 1
./shop new 203 Pia 1900 CARD 2 garden
./shop show 201
./shop show 202
./shop show 203
./shop status

订单 201 和 202 实际存储默认值 counter;203 存储 garden。所有订单头和真实商品行都保留。原来的三笔订单现在显示扩展默认值 counter,不要声称每笔历史订单都在那里取货。预备审批映射保持不变。

普通 ./shopp 拼写错误时,用 ls,再按正确名称使用 ./shop。SQL 报告语法错误时,检查 cat expand.sql,修正保存的完整语句,并只在列仍不存在时应用。重复扩展报错 1060 表示列已存在;检查 SHOW COLUMNS,不要删除它或重置数据库。重复已成功的客户端订单会产生重复 ID 错误:用 show 读取已有订单,不要编造另一个 ID。保留指定允许订单和原始数据。

观察回滚无法撤销的内容

本步骤中,你将在单独演示表上观察原生 ALTER 边界,同时保持订单系统完整。

继续使用 Terminal,工作目录为 /home/labex/project/pickup-desk。ddl_demo 是明确可丢弃的单行教学表,与所有订单分开。初始备注为 original:

./db -e "SELECT * FROM ddl_demo;"

事务将数据变更组织起来,直到 COMMIT 或 ROLLBACK。START TRANSACTION 开始事务,UPDATE 修改选中行,ROLLBACK 通常丢弃未提交数据变更。MySQL DDL,即数据定义语言,包括 ALTER TABLE,具有隐式提交边界。ALTER 在改变定义前提交先前工作,后续用户 ROLLBACK 无法撤销它。原子 DDL 恢复与允许用户回滚 DDL 并不是同一回事。

下面文件故意先只修改演示备注,再添加可为空的整数列 extra,然后请求 ROLLBACK。运行前先预测备注和新列是否会保留:

cat > ddl-demo.sql <<'SQL'
START TRANSACTION;
UPDATE ddl_demo SET note='committed before ALTER' WHERE id=1;
ALTER TABLE ddl_demo ADD COLUMN extra INT NULL;
ROLLBACK;
SQL
./shop apply ddl-demo.sql
./db -e "SELECT * FROM ddl_demo;"
./db -e "SHOW COLUMNS FROM ddl_demo;"

该行仍具有备注 committed before ALTER,extra 为 NULL。SHOW COLUMNS 仍列出 extra。较早的数据 UPDATE 已由 ALTER 提交,新定义也保留。ROLLBACK 成功返回并没有撤销它们。这是真实服务器行为,不是伪造的输出文件。

再次读取真实订单:

./shop show 101
./shop show 201
./shop show 203

原始订单头值、备注、商品和已存储取货代码均保留。切勿在订单备注上执行此演示。真实迁移会把事务性数据回填与后续收紧规则的 DDL 分开,并相应规划维护和修复边界;把每个 ALTER 放入 BEGIN 并不能提供自动回滚。

演示文件是从指定初始定义运行的一次性脚本。不小心运行两次时,extra 已存在。检查实际演示定义和行,不要删除它来让第二次看似全新运行。在首次成功应用前,用完整代码块恢复输错的文件。本单元终点是兼容扩展与如实观察 DDL,而不是宣称已在持续生产流量下完成部署。

总结

你交付了兼容的订单表扩展,观察了真实的新旧客户端写入,并保留原始订单明细。隔离的原生实验展示了为什么成功执行 ROLLBACK 不能撤销 ALTER,也不能撤销它之前已提交的数据变更。历史取货选择仍需使用下一单元讲解的已批准映射。