数据库编程大赛:只用一条 SQL 秒杀 100 万张火车票
2024 第二届数据库编程大赛于 12 月 5 日正式开启初赛!由 NineData 和云数据库技术社区主办,华为云、Doris等协办单位和媒体共同举办。比赛要求选手设计一套SQL算法,只用一条 SQL 秒杀 100 万张火车票,让乘客都都能顺利坐上火车回家过年。查看赛题详情
以下是本次决赛第5名,SQL编程大师傅昌林的参赛介绍:

1. 构建带座位类型的列车表:复制 train 表,新增 seat_type 列,0 代表有坐票,1 代表无坐票,无坐票数量为有坐票的 10%,新表命名为 train_with_emapy。
2. 计算运行总计座位数:在 train_with_emapy 表中,利用窗口求和函数,按 <车次, seat_type> 在 <出发站, 到达站> 组内生成运行总计座位数 running_seat_count。
3. 在乘客表中,通过窗口计数函数,为 <出发站, 到达站> 组内的乘客生成从 1 开始的编号 passenger_rid。
4. 根据每个乘客在<出发站, 到达站>组里的编号里和车次在<出发站, 到达站>组里的编号范围,求出具体的车次号, 车票类型,车箱号 coach_number 和乘客在这个车次的编号 ans_id, 命名为 result
5. 生成0..100对应的 seat_number 的查找表 to_seat_number
6. 原乘客表 LEFT JOIN result 和 LEFT JOIN to_seat_number 得出最终答案
以下是傅昌林选手的详细算法说明,结尾附完整SQL:














;WITH series_gen_cte AS (SELECT 0 AS xUNION ALLSELECT x + 1 FROM series_gen_cte WHERE x < 99), to_seat_number AS (SELECTx as ans_id,CAST(X/5 + 1 as nvarchar) +CASE X%5WHEN 0 THEN 'A'WHEN 1 THEN 'B'WHEN 2 THEN 'C'WHEN 3 THEN 'E'WHEN 4 THEN 'F'END as seat_numberFROM series_gen_cteUNIONSELECT 100, N'无座'), station_to_id AS (SELECT S.station_name, ROW_NUMBER() OVER(ORDER BY S.station_name) as station_idFROM (SELECT distinct [departure_station] as station_nameFROM [game_ticket].[train]UNIONSELECT distinct [arrival_station]FROM [game_ticket].[train]) S), train_with_empty AS (SELECTS1.station_id * 10000 + S2.station_id as route_id,[train_id],[seat_count],0 as seat_typeFROM [game_ticket].[train] TJOIN station_to_id S1 ON T.departure_station = S1.station_nameJOIN station_to_id S2 ON T.arrival_station = S2.station_nameUNION ALLSELECTS1.station_id * 10000 + S2.station_id as route_id,[train_id],[seat_count] / 10,1 as seat_typeFROM [game_ticket].[train] TJOIN station_to_id S1 ON T.departure_station = S1.station_nameJOIN station_to_id S2 ON T.arrival_station = S2.station_name), route_info AS (SELECTroute_id,train_id,seat_type,seat_count,SUM([seat_count]) OVER (PARTITION BY route_id ORDER BY route_id, seat_type, seat_count, train_id) as running_seat_countFROM train_with_empty T), passengers_in_scope AS (SELECTP.passenger_id,S1.station_id * 10000 + S2.station_id as route_id,COUNT(1) OVER (PARTITION BY S1.station_id, S2.station_id ORDER BY P.passenger_id) as passenger_ridFROM [game_ticket].[passenger] P WITH (NOLOCK)JOIN station_to_id S1 ON P.departure_station = S1.station_nameJOIN station_to_id S2 ON P.arrival_station = S2.station_name), result AS (SELECTS.passenger_id,R.train_id,CASEWHEN R.seat_type = 1 THEN NULLELSE 1 + (S.passenger_rid - R.running_seat_count + R.seat_count - 1) / 100END as coach_number,CASEWHEN R.seat_type = 1 THEN 100ELSE (S.passenger_rid - R.running_seat_count + R.seat_count - 1) % 100END as ans_idFROM passengers_in_scope SJOIN route_info RON S.route_id = R.route_idAND S.passenger_rid > R.running_seat_count - R.seat_countAND S.passenger_rid <= R.running_seat_count)SELECTP.passenger_id,P.departure_station,P.arrival_station,R.train_id,R.coach_number,T.seat_numberFROM [game_ticket].[passenger] P WITH (NOLOCK)LEFT JOIN result R ON P.passenger_id = R.passenger_idLEFT JOIN to_seat_number T ON R.ans_id = T.ans_idORDER BY P.passenger_id
时间截止2025年1月5日22:00时

感谢大家对本次《数据库编程大赛》的关注和支持,欢迎加入技术交流群,更多精彩活动不断,欢迎各路数据库爱好者来挑战!






