楼主: 〇〇

NineData第2届数据库编程大赛:一条SQL秒 杀100万张火车票

[复制链接]
论坛徽章:
169
SQL数据库编程大师
日期:2016-01-13 10:30:43SQL极客
日期:2013-12-09 14:13:35SQL大赛参与纪念
日期:2013-12-06 14:03:45最佳人气徽章
日期:2015-03-19 09:44:03现任管理团队成员
日期:2015-08-26 02:10:00秀才
日期:2015-07-28 09:12:12举人
日期:2015-07-13 15:30:15进士
日期:2015-07-28 09:12:58探花
日期:2015-07-28 09:12:58榜眼
日期:2015-08-18 09:48:03
31#
发表于 2024-12-22 21:36 来自手机 | 只看该作者
有一种,把train表跟pa表一起unionall的齿轮法

使用道具 举报

回复
论坛徽章:
519
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
32#
发表于 2024-12-23 01:05 | 只看该作者
Naldonado 发表于 2024-12-22 21:36
有一种,把train表跟pa表一起unionall的齿轮法

那是不是得用分析函数来取代连接?我感觉效率太低了,分析函数里面只能排序,不能HASH

我自己想的新方法是可以把切片变回200,这样HASH表就小了十倍,但是性能上没有任何提升,一半时间都花在解析座位号上面了,所以没啥意义。

使用道具 举报

回复
论坛徽章:
169
SQL数据库编程大师
日期:2016-01-13 10:30:43SQL极客
日期:2013-12-09 14:13:35SQL大赛参与纪念
日期:2013-12-06 14:03:45最佳人气徽章
日期:2015-03-19 09:44:03现任管理团队成员
日期:2015-08-26 02:10:00秀才
日期:2015-07-28 09:12:12举人
日期:2015-07-13 15:30:15进士
日期:2015-07-28 09:12:58探花
日期:2015-07-28 09:12:58榜眼
日期:2015-08-18 09:48:03
33#
发表于 2024-12-23 09:20 | 只看该作者
newkid 发表于 2024-12-23 01:05
那是不是得用分析函数来取代连接?我感觉效率太低了,分析函数里面只能排序,不能HASH我自己想的新方法是可 ...

也肯能跟扩展的倍数有关,train表的票数再多一些,车次再多一些,反而又有效果了。不过我觉得如果所有的都是刚刚好,100反而是不是**的分组?直接01A-20F,所有判断都省去了。

使用道具 举报

回复
论坛徽章:
407
紫蛋头
日期:2012-05-21 10:19:41迷宫蛋
日期:2012-06-06 16:02:49奥运会纪念徽章:足球
日期:2012-06-29 15:30:06奥运会纪念徽章:排球
日期:2012-07-10 21:24:24鲜花蛋
日期:2012-07-16 15:24:59奥运会纪念徽章:拳击
日期:2012-08-07 10:54:50奥运会纪念徽章:羽毛球
日期:2012-08-21 15:55:33奥运会纪念徽章:蹦床
日期:2012-08-21 21:09:51奥运会纪念徽章:篮球
日期:2012-08-24 10:29:11奥运会纪念徽章:体操
日期:2012-09-07 16:40:00
34#
 楼主| 发表于 2024-12-23 13:42 | 只看该作者
union 改写left join,效率和newkid方法1差不多

  1. with p as (
  2.     select
  3.       passenger_id,
  4.       departure_station,
  5.       arrival_station,
  6.       row_number() over (
  7.         partition by
  8.           departure_station,
  9.           arrival_station order by passenger_id
  10.       ) sid
  11.     from
  12.       passenger
  13.   ),
  14.   t0 as (
  15.     select
  16.       train_id,
  17.       departure_station,
  18.       arrival_station,
  19.       seat_count,
  20.       sum(seat_count) over (
  21.         partition by
  22.           departure_station,
  23.           arrival_station
  24.         order by
  25.           train_id
  26.       ) sum_sid,
  27.       sum(seat_count) over (
  28.         partition by
  29.           departure_station,
  30.           arrival_station
  31.       ) sum_d_a
  32.     from
  33.       train
  34.     ),
  35. t as(
  36.     select
  37.       train_id,
  38.       departure_station,
  39.       arrival_station,
  40.       sum_sid,
  41.       sum_sid-seat_count+1 start_sid,
  42.       sum_d_a+(sum_sid-seat_count) // 10 +1 start_stand_id,
  43.       sum_d_a+ sum_sid // 10 end_stand_id,
  44. sum_d_a+sum_d_a//10 sum_seat_number
  45.     from
  46.       t0
  47. )
  48. select
  49.   passenger_id,
  50.   p.departure_station,
  51.   p.arrival_station,
  52.   train_id,
  53.   (sid-start_sid) // 100 +1  coach_number ,
  54.   concat(cast((sid-1)%100 // 5 +1 as varchar), substr('ABCEF',(sid-1)%5+1,1))  seat_number
  55. from
  56.   p
  57.   join t on p.departure_station=t.departure_station and p.arrival_station=t.arrival_station and
  58.                  p.sid between start_sid and sum_sid

  59. union all
  60. select
  61.   passenger_id,
  62.   p.departure_station,
  63.   p.arrival_station,
  64.   train_id,
  65.   null coach_number ,
  66.   '无座' seat_number
  67. from
  68.   p
  69.   join t on p.departure_station=t.departure_station and p.arrival_station=t.arrival_station and
  70.                  p.sid between start_stand_id and end_stand_id
  71. union all
  72. select
  73.   passenger_id,
  74.   p.departure_station,
  75.   p.arrival_station,
  76.   NULL train_id, NULL coach_number,NULL seat_number
  77. from
  78.   p
  79.   join (select
  80.       departure_station,
  81.       arrival_station,
  82.       sum_seat_number
  83.    from t
  84.    group by departure_station,
  85.       arrival_station, sum_seat_number)t on p.departure_station=t.departure_station and p.arrival_station=t.arrival_station and
  86.                  p.sid >sum_seat_number
  87. order by passenger_id;
  88. -- select count(*),substr(seat_number,-1,1) from r group by substr(seat_number,-1,1) order by 2;
复制代码

使用道具 举报

回复
论坛徽章:
519
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
35#
发表于 2024-12-24 01:22 | 只看该作者
Naldonado 发表于 2024-12-23 09:20
也肯能跟扩展的倍数有关,train表的票数再多一些,车次再多一些,反而又有效果了。不过我觉得如果所有的都 ...

用100个座位(一节车厢)作为分片,无座票先按20个座位切片,然后每5条合并成一个记录:

with p as (
    select passenger.*
          ,row_number() over(partition by departure_station,arrival_station order by passenger_id) rn ---- 为每组起终点的乘客编一个序号
     from passenger)
,tr as (
    select departure_station,arrival_station,flag
          ,max(train_id) as train_id
          ,listagg(rpad(train_id,10)) within group(order by s2) as str
          ,case when flag=0 then s2 else ceil(s2/100)*100 end AS S2
          ,max(coach_number) as coach_number
       from (select train_id,departure_station,arrival_station,flag
                  ,sum(case when flag=0 then 100 else 20 end) over(partition by departure_station,arrival_station order by flag,train_id,coach_number) as s2  
                  ,seat_count
                  ,coach_number
             from train t
                 ,(select 0 flag from dual union all select 1 from dual) ---和两条记录的小集合做笛卡尔积,生成额外的车次记录
                 ,lateral(select level coach_number from dual connect by level<=case when flag=0 then seat_count/100 else trunc(seat_count*0.1/20) end)
             ) tr1
      group by departure_station,arrival_station,flag,case when flag=0 then s2 else ceil(s2/100)*100 end
)
select p.passenger_id,p.departure_station,p.arrival_station
      ,case when tr.flag=0 then tr.train_id else trim(substr(tr.str, (ceil((p.rn-tr.s2+100)/20)-1)*10+1,10)) end as train_id
      ,case when tr.flag=0 then tr.coach_number end as coach_number  
      ,case when tr.flag=0 then
            trim(substr('1A 1B 1C 1E 1F 2A 2B 2C 2E 2F 3A 3B 3C 3E 3F 4A 4B 4C 4E 4F 5A 5B 5C 5E 5F 6A 6B 6C 6E 6F 7A 7B 7C 7E 7F 8A 8B 8C 8E 8F 9A 9B 9C 9E 9F 10A10B10C10E10F11A11B11C11E11F12A12B12C12E12F13A13B13C13E13F14A14B14C14E14F15A15B15C15E15F16A16B16C16E16F17A17B17C17E17F18A18B18C18E18F19A19B19C19E19F20A20B20C20E20F'
                       ,mod(p.rn-1,100)*3+1,3))
            when tr.flag=1 then ' no seat'
       end as seat_number
  from p left join tr on p.departure_station =tr.departure_station and p.arrival_station =tr.arrival_station
       and ceil(p.rn/100) = tr.s2/100
       ;

测试结果:仍然使用了同样的时间,现在车厢号不用算了,座位号也有所简化,如果不算座位号,差不多可以省下一半时间。目前这个座位号需要做取模乘法加法然后SUBSTR的操作。还有什么招?

HASH表只要内存放得下,其大小并不会影响整体执行时间,因为时间主要花在扫描探测表(即乘客表)。

使用道具 举报

回复
论坛徽章:
407
紫蛋头
日期:2012-05-21 10:19:41迷宫蛋
日期:2012-06-06 16:02:49奥运会纪念徽章:足球
日期:2012-06-29 15:30:06奥运会纪念徽章:排球
日期:2012-07-10 21:24:24鲜花蛋
日期:2012-07-16 15:24:59奥运会纪念徽章:拳击
日期:2012-08-07 10:54:50奥运会纪念徽章:羽毛球
日期:2012-08-21 15:55:33奥运会纪念徽章:蹦床
日期:2012-08-21 21:09:51奥运会纪念徽章:篮球
日期:2012-08-24 10:29:11奥运会纪念徽章:体操
日期:2012-09-07 16:40:00
36#
 楼主| 发表于 2024-12-24 08:19 | 只看该作者
100个座位的版本在duckdb不错,如果在doris也快,可以领茅 台了
不排序 Run Time (s): real 2.324 user 16.016000 sys 0.708000
排序 Run Time (s): real 4.576 user 28.852000 sys 2.036000

使用道具 举报

回复
论坛徽章:
407
紫蛋头
日期:2012-05-21 10:19:41迷宫蛋
日期:2012-06-06 16:02:49奥运会纪念徽章:足球
日期:2012-06-29 15:30:06奥运会纪念徽章:排球
日期:2012-07-10 21:24:24鲜花蛋
日期:2012-07-16 15:24:59奥运会纪念徽章:拳击
日期:2012-08-07 10:54:50奥运会纪念徽章:羽毛球
日期:2012-08-21 15:55:33奥运会纪念徽章:蹦床
日期:2012-08-21 21:09:51奥运会纪念徽章:篮球
日期:2012-08-24 10:29:11奥运会纪念徽章:体操
日期:2012-09-07 16:40:00
37#
 楼主| 发表于 2024-12-24 18:13 来自手机 | 只看该作者
数组版本最高能达到排序后6秒左右

使用道具 举报

回复
论坛徽章:
519
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
38#
发表于 2024-12-31 05:18 | 只看该作者
用标量子查询来算座位号,因为可以利用缓存,1000万乘客能提高20秒左右:
with p as (
    select passenger.*
          ,row_number() over(partition by departure_station,arrival_station order by passenger_id) rn ---- 为每组起终点的乘客编一个序号
          ,mod(row_number() over(partition by departure_station,arrival_station order by passenger_id)-1,100) as m ---- 车厢内序号,这个值是高度重复的所以可以利用标量子查询的缓存
     from passenger)
,tr as (
    select departure_station,arrival_station,flag
         -- ,case when flag=0 then s2 else ceil(s2/100)*100 end/100 as chunk_id
          ,max(train_id) as train_id
          ,listagg(rpad(train_id,10)) within group(order by s2) as str
          ,case when flag=0 then s2 else ceil(s2/100)*100 end AS S2
          ,case when flag=0 then s2 else ceil(s2/100)*100 end-100 AS S1
          ,max(coach_number) as coach_number
       from (select train_id,departure_station,arrival_station,flag
                  ,sum(case when flag=0 then 100 else 20 end) over(partition by departure_station,arrival_station order by flag,train_id,coach_number) as s2  
                  ,seat_count
                  ,coach_number
             from train t
                 ,(select 0 flag from dual union all select 1 from dual) ---和两条记录的小集合做笛卡尔积,生成额外的车次记录
                 ,lateral(select level coach_number from dual connect by level<=case when flag=0 then seat_count/100 else trunc(seat_count*0.1/20) end)
             ) tr1
      group by departure_station,arrival_station,flag,case when flag=0 then s2 else ceil(s2/100)*100 end
)
select p.passenger_id,p.departure_station,p.arrival_station
      ,case when tr.flag=0 then tr.train_id else trim(substr(tr.str, (ceil((p.rn-tr.s1)/20)-1)*10+1,10)) end as train_id
      ,case when tr.flag=0 then tr.coach_number end as coach_number  
      ,case when tr.flag=0 then ---------- 利用标量子查询计算座位号
                 (select trim(substr('1A 1B 1C 1E 1F 2A 2B 2C 2E 2F 3A 3B 3C 3E 3F 4A 4B 4C 4E 4F 5A 5B 5C 5E 5F 6A 6B 6C 6E 6F 7A 7B 7C 7E 7F 8A 8B 8C 8E 8F 9A 9B 9C 9E 9F 10A10B10C10E10F11A11B11C11E11F12A12B12C12E12F13A13B13C13E13F14A14B14C14E14F15A15B15C15E15F16A16B16C16E16F17A17B17C17E17F18A18B18C18E18F19A19B19C19E19F20A20B20C20E20F'
                        ,p.m*3+1,3)) from dual
                  )  
            when tr.flag=1 then ' no seat'
       end as seat_number
  from p left join tr on p.departure_station =tr.departure_station and p.arrival_station =tr.arrival_station
       and ceil(p.rn/100) = tr.s2/100
       ;
      

使用道具 举报

回复
论坛徽章:
407
紫蛋头
日期:2012-05-21 10:19:41迷宫蛋
日期:2012-06-06 16:02:49奥运会纪念徽章:足球
日期:2012-06-29 15:30:06奥运会纪念徽章:排球
日期:2012-07-10 21:24:24鲜花蛋
日期:2012-07-16 15:24:59奥运会纪念徽章:拳击
日期:2012-08-07 10:54:50奥运会纪念徽章:羽毛球
日期:2012-08-21 15:55:33奥运会纪念徽章:蹦床
日期:2012-08-21 21:09:51奥运会纪念徽章:篮球
日期:2012-08-24 10:29:11奥运会纪念徽章:体操
日期:2012-09-07 16:40:00
39#
 楼主| 发表于 2025-1-1 07:14 | 只看该作者
标量子查询优化对duckdb**
用7.863
不用5.374

使用道具 举报

回复
论坛徽章:
519
奥运会纪念徽章:垒球
日期:2008-09-15 01:28:12生肖徽章2007版:鸡
日期:2008-11-17 23:40:58生肖徽章2007版:马
日期:2008-11-18 05:09:48数据库板块每日发贴之星
日期:2008-11-29 01:01:02数据库板块每日发贴之星
日期:2008-12-05 01:01:03生肖徽章2007版:虎
日期:2008-12-10 07:47:462009新春纪念徽章
日期:2009-01-04 14:52:28数据库板块每日发贴之星
日期:2009-02-08 01:01:03生肖徽章2007版:蛇
日期:2009-03-09 22:18:532009日食纪念
日期:2009-07-22 09:30:00
40#
发表于 2025-1-1 09:51 来自手机 | 只看该作者
鸭子是不是没有 row cache? Oracle 的执行计划可以查看子查询的启动次数,鸭子库不知道有没有

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表