外键关联场景下航班预订系统批量购票数据原子插入实现问询
航班预订系统的事务性批量处理方案(含存储过程实现)
完全可以把插入机票和关联购票人的操作做成一个原子性的整体,存储过程是非常合适的选择,其实数据传递并没有你想象的那么复杂。我结合MySQL的场景给你详细拆解一下:
核心思路:用事务保证原子性
不管用存储过程还是应用层代码,事务是关键——它能确保「插入ticket」和「插入purchase」这两步要么全部成功,要么全部失败,不会出现只有ticket没有关联purchase,或者反过来的脏数据。
存储过程实现示例
假设你的表结构大概是这样(可根据实际需求调整字段):
ticket表:ticket_id(自增主键)、flight_number(航班号)、passenger_name(乘客姓名)、passenger_id_card(乘客身份证)purchase表:purchase_id(自增主键)、user_id(购票用户ID)、ticket_id(关联ticket的外键)、order_unique_id(统一订单ID)
批量处理的存储过程
这个存储过程支持一次性传入多条乘客数据,自动完成ticket插入和purchase关联,并且全程在事务中执行:
DELIMITER // CREATE PROCEDURE BookFlightTickets( IN p_user_id INT, -- 购票的注册用户ID IN p_order_unique_id VARCHAR(50), -- 统一订单ID(同一事务的标识) IN p_flight_number VARCHAR(20), -- 航班号 IN p_passenger_info JSON -- 多条乘客信息,用JSON格式传递 ) BEGIN DECLARE v_ticket_id INT; DECLARE v_error INT DEFAULT 0; -- 捕获SQL异常,标记错误状态 DECLARE CONTINUE HANDLER FOR SQLEXCEPTION SET v_error = 1; -- 开启事务,所有操作要么全成要么全败 START TRANSACTION; -- 遍历JSON中的每一条乘客数据,逐个处理 WHILE JSON_LENGTH(p_passenger_info) > 0 DO -- 提取第一条乘客的信息 SET @current_passenger = JSON_EXTRACT(p_passenger_info, '$[0]'); SET @passenger_name = JSON_UNQUOTE(JSON_EXTRACT(@current_passenger, '$.name')); SET @passenger_id_card = JSON_UNQUOTE(JSON_EXTRACT(@current_passenger, '$.id_card')); -- 插入ticket记录,生成唯一的ticket_id INSERT INTO ticket(flight_number, passenger_name, passenger_id_card) VALUES(p_flight_number, @passenger_name, @passenger_id_card); -- 获取刚插入的ticket的主键(MySQL用274957) SET v_ticket_id = 274957; -- 插入purchase记录,关联用户、机票和统一订单ID INSERT INTO purchase(user_id, ticket_id, order_unique_id) VALUES(p_user_id, v_ticket_id, p_order_unique_id); -- 移除已经处理完的第一条乘客数据,继续循环 SET p_passenger_info = JSON_REMOVE(p_passenger_info, '$[0]'); END WHILE; -- 根据错误状态决定提交还是回滚 IF v_error = 1 THEN ROLLBACK; SELECT '预订失败,已回滚所有操作' AS result; ELSE COMMIT; SELECT '预订成功,所有机票已完成关联' AS result; END IF; END // DELIMITER ;
如何调用这个存储过程
你只需要在应用端把用户ID、订单ID、航班号,以及整理成JSON格式的乘客数据传递进去就行:
CALL BookFlightTickets( 1001, -- 示例购票用户ID 'ORDER_20240520_0001', -- 统一订单ID 'CA1234', -- 航班号 '[{"name":"张三","id_card":"110101199001011234"},{"name":"李四","id_card":"110101199102025678"}]' );
关键细节说明
- 事务的作用:如果其中某一条ticket插入失败,或者对应的purchase插入失败,整个事务会回滚,之前插入的所有数据都会被撤销,保证数据一致性。
- 获取自增ID:用
274957可以准确获取当前会话中刚插入的ticket的主键,不会和其他会话的插入操作冲突。 - 批量处理:用JSON传递多条乘客数据,避免多次调用存储过程,减少网络开销。如果是其他数据库(比如SQL Server),可以用表值参数来实现更优雅的批量传递。
备选方案:应用层实现事务
如果你暂时不想用存储过程,也可以在应用代码中实现:
- 开启数据库事务
- 循环遍历每条乘客数据:插入ticket → 获取生成的ticket_id → 插入purchase
- 所有操作完成后提交事务;如果中途出错,就回滚事务
不过存储过程的优势在于把业务逻辑放在数据库端,减少应用和数据库之间的交互次数,尤其是批量处理时效率更高。
内容的提问来源于stack exchange,提问作者codeMike




