限时手写 ①:窗口函数专场

手速是练回来的。纸上、限时、闭卷--还原白板面试的压迫感。

学 0 min
练 70 min
盘 50 min
共 120 分钟

规则

  1. 01纸上手写,不许运行、不许查资料,60 分钟 8 题

练 · 120 min

  1. 60 分钟闭卷手写 8 题(从 D27 的题型里随机抽)
    参考答案

    先判断题型属于哪个家族,SQL 就是框架默写--这是限时手写的全部秘密。口述模板:子查询排好编号 / 算好累计,外层再筛。

    -- D27 的题型基本落在四个家族,闭卷时先认题型再套框架:
    -- ① 分组内编号(每组 TOP N / 第 N 个)② 连续区间(差值分组法)
    -- ③ 累计(sum over)④ 偏移(lag/lead,如环比、连续 3 天上涨)
    
    -- ① 每个用户消费额前 3 的订单
    select id, user_id, total_amount
    from (select id, user_id, total_amount,
                 row_number() over (partition by user_id order by total_amount desc) as rn
          from orders) t
    where rn <= 3;
    
    -- ② 连续登录 7 天及以上的用户(日期 - 编号,连续的日子里这个差值不变)
    select user_id
    from (select user_id,
                 login_at::date - (row_number() over (partition by user_id order by login_at::date))::int as grp
          from (select distinct user_id, login_at::date from user_logins) t) g
    group by user_id, grp
    having count(*) >= 7;
    
    -- ③ 贡献 80% GMV 的头部用户(累计占比)
    with u as (select user_id, sum(total_amount) as gmv from orders where status = 2 group by 1)
    select user_id, gmv
    from (select user_id, gmv,
                 sum(gmv) over (order by gmv desc) * 100.0 / sum(gmv) over () as 累计占比
          from u) t
    where 累计占比 <= 80;
    
    -- ④ 每个用户的第 2 笔订单
    select id, user_id, created_at
    from (select id, user_id, created_at,
                 row_number() over (partition by user_id order by created_at) as rn
          from orders) t
    where rn = 2;
  2. 上机逐题验证,记录一次通过率
    参考答案

    「一次通过」的标准从严:上机不做任何修改、直接跑出正确结果才算。行数对不上就是没通过,别用「只是少写了个 distinct」自我安慰。

  3. 错题当场重写到正确为止
    参考答案

    重写时先默写考试时的原始答案,再并排写正确版--两版的差异在哪一步,那一步就是下次的检查点。

  4. 把错因归类:语法记错 / 框架用错 / 思路偏了
    参考答案

    三类错因三种处方:语法记错(rank 三兄弟的差别之类)-> 连续 3 天每天默写一次;框架用错(忘了默认框架是 RANGE)-> 回 D23–D25 学栏重看并口述一遍;思路偏了 -> 练五步拆解,先把需求写成人话再翻译成 SQL。

  5. 更新 mistakes.md
    参考答案

    mistakes.md 每题记三样:题型名、错因归类、触发词(看到哪几个词就该想起哪个框架:「连续」「前 N」「第 2 笔」「环比」「累计占比」)。触发词是这个文件最值钱的部分。

过关标准 8 题一次通过 ≥ 6 题。

今日复盘

  1. 设计点赞系统(注意热点行与计数)
    参考答案

    要点:复合主键 (user_id, target_type, target_id) 天然幂等,重复点赞插不进去,不用先查再插;点赞数单独做计数表或缓存,别每次实时 count;三个典型查询:某内容的点赞数、我点过没有(主键直接命中)、我点赞过的列表(按 user_id 开头的索引,需要和主键顺序权衡);扩展性风险:热点内容的计数行竞争 -> 计数分桶 / 走缓存(D48 的思路)。DDL 参考上方学栏示例。

  2. 把页面底部「性能与索引」8 问 + 自己补 12 问,写成 20 题
    参考答案

    页面 8 问之外,自补 12 问按三类分:① 原理(B-tree 为什么快、PG 的索引与堆是什么关系);② 失效场景(列上套函数、前导 % 模糊、<>、类型不匹配);③ 实战(EXPLAIN 怎么读、cost 估算和 actual 偏差说明统计信息过期、三种 join 算法的选择依据、索引的写放大代价)。每题先写一句话答案钉住结论,口述稿再扩。

  3. 视图不存数据、物化视图存数据
    易错点

    视图改名/换定义不影响底表;但「以为视图能提速」是最常见误解--它是查询的别名,不是缓存。