暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

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

NineData 2025-01-03
256

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

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

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

参赛选手:傅昌林
个人简介: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:



参赛完整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_number
    FROM [game_ticket].[passenger] P WITH (NOLOCK)
    LEFT JOIN result R ON P.passenger_id = R.passenger_id
    LEFT JOIN to_seat_number T ON R.ans_id = T.ans_id
    ORDER BY P.passenger_id
    《数据库编程大赛-冠军挑战活动》

    时间截止2025年1月5日22:00时

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

    文章转载自NineData,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

    评论