云数据库技术

SQL编程大师-傅昌林:利用 SQL Server 构建临时表与窗口函数,2.702 秒优化百万火车票分配

Image

数据库编程大赛:只用一条 SQL 秒杀 100 万张火车票

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

以下是本次决赛第5名,SQL编程大师傅昌林的参赛介绍:

Image
参赛选手:傅昌林
个人简介:HBI Solutions, Inc, VP Engineering
参赛数据库:SQLServer
性能评测:百万级数据代码性能评测 2.702 秒
综合得分:80.75
以下是傅昌林选手的代码说明思路简介:

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:


Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image
Image

参赛完整SQL:
;WITH series_gen_cte AS (    SELECT 0 AS x    UNION ALL    SELECT x + 1 FROM series_gen_cte WHERE x < 99), to_seat_number AS ( SELECT  x as ans_id,  CAST(X/5 + 1 as nvarchar) +  CASE X%5    WHEN 0 THEN 'A'    WHEN 1 THEN 'B'    WHEN 2 THEN 'C'    WHEN 3 THEN 'E'    WHEN 4 THEN 'F'   END as seat_number FROM series_gen_cte UNION SELECT 100, N'无座'), station_to_id AS ( SELECT S.station_name, ROW_NUMBER() OVER(ORDER BY S.station_name) as station_id FROM (  SELECT distinct [departure_station] as station_name  FROM [game_ticket].[train]  UNION  SELECT distinct [arrival_station]   FROM [game_ticket].[train] ) S), train_with_empty AS ( SELECT  S1.station_id * 10000 + S2.station_id as route_id,  [train_id],  [seat_count],  0 as seat_type FROM [game_ticket].[train] T JOIN station_to_id S1 ON T.departure_station = S1.station_name JOIN station_to_id S2 ON T.arrival_station = S2.station_name  UNION ALL SELECT  S1.station_id * 10000 + S2.station_id as route_id,  [train_id],  [seat_count] / 10,  1 as seat_type FROM [game_ticket].[train] T JOIN station_to_id S1 ON T.departure_station = S1.station_name JOIN station_to_id S2 ON T.arrival_station = S2.station_name ), route_info AS ( SELECT  route_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_count FROM train_with_empty T), passengers_in_scope AS ( SELECT   P.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_rid FROM [game_ticket].[passenger] P WITH (NOLOCK) JOIN station_to_id S1 ON P.departure_station = S1.station_name JOIN station_to_id S2 ON P.arrival_station = S2.station_name), result AS ( SELECT  S.passenger_id,  R.train_id,  CASE   WHEN R.seat_type = 1 THEN NULL   ELSE 1 + (S.passenger_rid - R.running_seat_count + R.seat_count - 1) / 100  END as coach_number,  CASE    WHEN R.seat_type = 1 THEN 100   ELSE (S.passenger_rid - R.running_seat_count + R.seat_count - 1) % 100  END as ans_id FROM passengers_in_scope S JOIN route_info R  ON S.route_id = R.route_id   AND S.passenger_rid > R.running_seat_count - R.seat_count   AND S.passenger_rid <= R.running_seat_count)SELECT  P.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时

Image

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

Image