{
  "assessmentTests": {
    "sql_test": {
      "name": "SQL 测试",
      "desc": "30 道关于筛选、联接、NULL 语义、聚合、子查询和窗口函数的情景题 — 判断你对 SQL 的理解是否与职位要求的 SQL 工作能力相符。",
      "recommendation": "你的 SQL 技能概览",
      "results": {
        "beginner": {
          "name": "初级",
          "desc": "你能写出一个有效的 SELECT 语句并在几个条件上进行筛选，但你错的题往往聚焦在 NULL 处理和联接机制上，而不是语法——把一列与 NULL 用 = 比较、LEFT JOIN 实际保留什么、BETWEEN 的边界如何工作。这都不是说你数据库能力不行；这些是学 SQL 时通过试错而不是通过标准学来的人容易踩坑的特定规则。在工作中很重要，因为每一个这样的坑都是查询返回看似合理但其实错误的行数的地方。",
          "recommendation": "先集中学三样东西，按这个顺序：为什么 WHERE col = NULL 永远匹配不了任何东西（必须用 IS NULL），INNER JOIN 如何丢弃不匹配的行而 LEFT JOIN 保留它们，以及 BETWEEN 的确切边界。Mode Analytics 的 SQL 教程和 PostgreSQL 文档都用可运行的例子讲了这三个。"
        },
        "intermediate": {
          "name": "中级",
          "desc": "你日常报表查询写得很顺手 — 联接、GROUP BY、直白的筛选 — 日常仪表板或临时分析工作不会让你放慢速度。从这里到高级的差距主要在子句互相作用会发生什么：一个写在联接右表上的 WHERE 子句偷偷把 LEFT JOIN 转回 INNER JOIN、一个联接在应用聚合前扇出行数、一个 NOT IN 因为一个 NULL 偷偷滑进子查询就无声地返回什么都不是。这些是通过眼神检查就能过关但只有在某人跟另一份报表对比总数时才会显露的 bug。",
          "recommendation": "专注子句如何互相作用而不是每个子句单独做什么：WHERE 在 LEFT JOIN 右表上筛选、一对多联接在聚合前扇出行数、为什么 NOT IN 在 NULL 存在时会坏掉（EXISTS 通常不会）。然后学 HAVING vs WHERE，因为那个拆分容易绊倒已经分别知道两个子句的人。"
        },
        "advanced": {
          "name": "高级",
          "desc": "这是大多数职位招聘信息说的「懂 SQL」的等级。你读一个多联接查询能在运行它前预测它的行数，你知道为什么一个结果集改变了而不只是知道它改变了，你遇到清晰工具的地方会选择 CTE 或窗口函数而不是嵌套子查询。把这个等级和顶级分开的是防守工作的一侧：知道什么时候有并列值关键的排名函数是哪个、什么时候一个事务隔离出并发会话的内容、一个缺失的 ON DELETE 规则在数据库级别真正做什么。",
          "recommendation": "深入那些保护别人也要动的数据的部分：RANK、DENSE_RANK 和 ROW_NUMBER 在有并列值时的区别、COMMIT 实际让谁看到什么、外键在 DELETE 上的行为。用 The Index 通讯或 SQL for Devs 在窗口函数和事务隔离上的深度潜水作为这两个的下一步。"
        },
        "expert": {
          "name": "专家",
          "desc": "你在每一部分都拿了高分 — 筛选和 NULL 语义、联接和集合运算、聚合和子查询、窗口函数、CTE 和约束。实际上这意味着你可以被扔进一个陌生人的多联接报表查询，解释为什么它返回它返回的行数，而不只是语法说它应该返回什么，后者才是更难、更有价值的技能。在这个等级，查询语言很少是限制因子；限制通常来自模式设计或它下面数据的大小。",
          "recommendation": "收益现在来自执行计划和设计：在假设一个查询慢前读 EXPLAIN 输出、索引策略作为对写成本的权衡而不是免费胜利、和随着模式增长而坚持的归一化决定。如果你在因一个职位而被筛选，讲一个你在生产中找到的查询 bug，比如联接扇出或 NOT IN/NULL 陷阱，而不是列举 SQL 特性 — 这展示的是你的推理能力，不只是词汇。"
        }
      },
      "questions": [
        {
          "question": "一个表有一个可空的 age 列。你运行 SELECT * FROM users WHERE age = NULL。这会返回多少行，即使有几行的 age 被设成 NULL？",
          "options": [
            {
              "icon": "",
              "label": "age 是 NULL 的每一行，因为 = 像对待任何其他值一样对待 NULL"
            },
            {
              "icon": "",
              "label": "零行 — 用 = 把任何东西与 NULL 比较产生 UNKNOWN，从不 TRUE，所以没有行匹配；必须用 IS NULL"
            },
            {
              "icon": "",
              "label": "语法错误 — NULL 不能出现在 = 的右边"
            },
            {
              "icon": "",
              "label": "表里的每一行，因为 NULL 比较默认为 TRUE"
            }
          ]
        },
        {
          "question": "你运行 SELECT DISTINCT department, role FROM employees。DISTINCT 移除什么的重复？",
          "options": [
            {
              "icon": "",
              "label": "department 和 role 合在一起的组合 — 只有当两列都与另一行完全匹配时才会移除一行"
            },
            {
              "icon": "",
              "label": "只有 department 的重复值，保留每个 role"
            },
            {
              "icon": "",
              "label": "只有 role 的重复值，保留每个 department"
            },
            {
              "icon": "",
              "label": "什么都不是 — DISTINCT 只在有一列时才有效"
            }
          ]
        },
        {
          "question": "一个 name 列用 WHERE name LIKE 'A_' 筛选。这些值中哪个匹配：'A'、'Al'、'Ana'、'Ally'？",
          "options": [
            {
              "icon": "",
              "label": "'A' — 下划线是可选的，匹配零个或更多字符"
            },
            {
              "icon": "",
              "label": "'Al' — 下划线匹配恰好一个字符，所以 LIKE 'A_' 匹配任何以 A 开头的两字符字符串"
            },
            {
              "icon": "",
              "label": "'Ana' 和 'Ally' — 下划线匹配任何数量的后续字符"
            },
            {
              "icon": "",
              "label": "四个值都匹配，因为 LIKE 忽略长度"
            }
          ]
        },
        {
          "question": "一个 price 列用 WHERE price BETWEEN 10 AND 20 筛选。price 恰好 10 或恰好 20 的行会被包含吗？",
          "options": [
            {
              "icon": "",
              "label": "不会 — BETWEEN 在两个端点上都排除"
            },
            {
              "icon": "",
              "label": "会 — BETWEEN 在两端都包含，等价于 price >= 10 AND price <= 20"
            },
            {
              "icon": "",
              "label": "只有 price = 10 被包含；上界是排除的"
            },
            {
              "icon": "",
              "label": "只有 price = 20 被包含；下界是排除的"
            }
          ]
        },
        {
          "question": "一个表 orders 有 10 行，其中 3 行的 shipped_date 是 NULL。SELECT COUNT(*) FROM orders 返回什么，SELECT COUNT(shipped_date) FROM orders 返回什么？",
          "options": [
            {
              "icon": "",
              "label": "10，然后 10 — COUNT 不管 NULL 总是计数行"
            },
            {
              "icon": "",
              "label": "7，然后 7 — 两种形式都跳过 NULL 行"
            },
            {
              "icon": "",
              "label": "10，然后 7 — COUNT(*) 计数每一行，COUNT(column) 只计数那列不是 NULL 的行"
            },
            {
              "icon": "",
              "label": "10，然后 3 — COUNT(column) 只计数 NULL 值"
            }
          ]
        },
        {
          "question": "你运行 SELECT name, salary FROM employees ORDER BY 2 DESC。2 指的是什么？",
          "options": [
            {
              "icon": "",
              "label": "语法错误 — ORDER BY 只接受列名，不接受数字"
            },
            {
              "icon": "",
              "label": "结果的第二行"
            },
            {
              "icon": "",
              "label": "文字值 2，用作并列符"
            },
            {
              "icon": "",
              "label": "SELECT 列表中的第二列，salary — ORDER BY 接受列的位置数作为简写"
            }
          ]
        },
        {
          "question": "在标准 SQL 中，'Ana' || ' ' || 'Lee' 计算为什么？",
          "options": [
            {
              "icon": "",
              "label": "语法错误 — SQL 没有连接运算符"
            },
            {
              "icon": "",
              "label": "3 — || 被当作布尔 OR 并返回计数"
            },
            {
              "icon": "",
              "label": "'AnaLee' — || 连接而不保留空格"
            },
            {
              "icon": "",
              "label": "'Ana Lee' — || 是标准 SQL 字符串连接运算符"
            }
          ]
        },
        {
          "question": "哪个完全等价于 WHERE status IN ('open', 'pending', 'review')？",
          "options": [
            {
              "icon": "",
              "label": "WHERE status = 'open' AND status = 'pending' AND status = 'review'"
            },
            {
              "icon": "",
              "label": "WHERE status != 'open' OR status != 'pending' OR status != 'review'"
            },
            {
              "icon": "",
              "label": "WHERE status = 'open' OR status = 'pending' OR status = 'review'"
            },
            {
              "icon": "",
              "label": "WHERE status LIKE 'open,pending,review'"
            }
          ]
        },
        {
          "question": "customers 有 100 行；其中 20 行从未下过订单。你运行 SELECT c.id FROM customers c INNER JOIN orders o ON c.id = o.customer_id。这 20 个没有订单的客户会出现在结果中吗？",
          "options": [
            {
              "icon": "",
              "label": "会，每个一次，o.id 显示为 NULL"
            },
            {
              "icon": "",
              "label": "会，但只有他们也出现在 WHERE 子句中时"
            },
            {
              "icon": "",
              "label": "不会 — INNER JOIN 只返回两个表中都有匹配的行，所以零订单的客户被完全丢弃"
            },
            {
              "icon": "",
              "label": "会，在 orders 中的每列复制一次"
            }
          ]
        },
        {
          "question": "你想要每个客户不管他们是否有订单，所以你写 SELECT c.id, o.total FROM customers c LEFT JOIN orders o ON c.id = o.customer_id WHERE o.total > 100。这仍然会返回零订单的客户吗？",
          "options": [
            {
              "icon": "",
              "label": "不会 — 在 WHERE 子句中筛选 o.total 会丢弃 LEFT JOIN 为不匹配客户产生的 NULL 行，所以它表现得像 INNER JOIN"
            },
            {
              "icon": "",
              "label": "会 — LEFT JOIN 总是保留 customers 中的每一行不管后面是什么"
            },
            {
              "icon": "",
              "label": "会，o.total 对没有订单的客户显示为 0"
            },
            {
              "icon": "",
              "label": "不会 — 当 WHERE 子句被加上时 LEFT JOIN 无声地把自己转成 RIGHT JOIN"
            }
          ]
        },
        {
          "question": "一个 employees 表有一个 id 列和一个指向另一行 id 的 manager_id 列。要列出每个员工旁边他们的经理名字，你把表联接到自身：SELECT e.name, m.name FROM employees e JOIN employees m ON e.manager_id = m.id。这个模式叫什么？",
          "options": [
            {
              "icon": "",
              "label": "一个叉积 — 每个员工与每个经理匹配"
            },
            {
              "icon": "",
              "label": "一个递归联接 — 它获取完整的管理链"
            },
            {
              "icon": "",
              "label": "一个自联接 — 同一个表用两个不同的别名被联接到自身"
            },
            {
              "icon": "",
              "label": "这是无效的 SQL — 一个表不能被联接到自身"
            }
          ]
        },
        {
          "question": "两个有相同列的 SELECT 查询用 UNION 组合。如果两个查询都返回相同的行，那行在最终结果中会出现多少次？",
          "options": [
            {
              "icon": "",
              "label": "一次 — UNION 移除跨组合结果的重复行；UNION ALL 会保留两份"
            },
            {
              "icon": "",
              "label": "两次 — UNION 保留两个查询的每一行"
            },
            {
              "icon": "",
              "label": "零次 — UNION 移除任何出现在两个查询中的行"
            },
            {
              "icon": "",
              "label": "取决于哪个查询先列出那行"
            }
          ]
        },
        {
          "question": "表 sizes 有 3 行，colors 有 4 行。SELECT * FROM sizes CROSS JOIN colors 返回多少行？",
          "options": [
            {
              "icon": "",
              "label": "7 — CROSS JOIN 把行数加在一起"
            },
            {
              "icon": "",
              "label": "12 — 一个 CROSS JOIN 返回两个表的行的每个可能组合（3 x 4）"
            },
            {
              "icon": "",
              "label": "3 — CROSS JOIN 对第一个表的每一行返回一行"
            },
            {
              "icon": "",
              "label": "0 — CROSS JOIN 需要一个 ON 条件不然返回什么都不是"
            }
          ]
        },
        {
          "question": "orders 对订单 #100 有 1 行，order_items 对订单 #100 有 3 行（每行商品一行）。你运行 SELECT o.id, o.total FROM orders o JOIN order_items i ON o.id = i.order_id WHERE o.id = 100。有多少行来自订单 #100？",
          "options": [
            {
              "icon": "",
              "label": "3 — 联接对每个匹配 order_items 行产生一个输出行，所以订单 #100 的单行被重复每个行商品一次"
            },
            {
              "icon": "",
              "label": "1 — orders 只对订单 #100 有一行，所以联接不能产生更多"
            },
            {
              "icon": "",
              "label": "4 — 订单一行加上每个行商品一行"
            },
            {
              "icon": "",
              "label": "0 — 把一个一行表联接到一个三行表在非唯一键上失败"
            }
          ]
        },
        {
          "question": "SELECT c.name, o.id FROM customers c RIGHT JOIN orders o ON c.id = o.customer_id 返回和下面哪个一样的行？",
          "options": [
            {
              "icon": "",
              "label": "SELECT c.name, o.id FROM customers c INNER JOIN orders o ON c.id = o.customer_id"
            },
            {
              "icon": "",
              "label": "SELECT c.name, o.id FROM orders o LEFT JOIN customers c ON c.id = o.customer_id — 交换表顺序并用 LEFT JOIN 代替 RIGHT JOIN 是等价的"
            },
            {
              "icon": "",
              "label": "SELECT c.name, o.id FROM customers c LEFT JOIN orders o ON c.id = o.customer_id"
            },
            {
              "icon": "",
              "label": "SELECT c.name, o.id FROM orders o CROSS JOIN customers c"
            }
          ]
        },
        {
          "question": "在标准 SQL 中，你运行 SELECT department, name, AVG(salary) FROM employees GROUP BY department。这是有效的查询吗？",
          "options": [
            {
              "icon": "",
              "label": "是的 — GROUP BY 只需要包含 department 因为它首先被列出"
            },
            {
              "icon": "",
              "label": "不是 — name 被选中但既没被聚合也没在 GROUP BY 中列出，标准 SQL 要求每个非聚合的选中列在 GROUP BY 子句中出现"
            },
            {
              "icon": "",
              "label": "是的 — SQL 自动给每个 department 挑一个任意的 name"
            },
            {
              "icon": "",
              "label": "不是 — AVG() 不能在同一个查询中和 GROUP BY 组合"
            }
          ]
        },
        {
          "question": "你想要平均薪资超过 80000 的部门。哪个子句在分组后筛选一个像 AVG(salary) 这样的聚合值 — WHERE 还是 HAVING？",
          "options": [
            {
              "icon": "",
              "label": "WHERE — HAVING 只与 UNION 查询一起用"
            },
            {
              "icon": "",
              "label": "任何一个都与聚合函数相同工作"
            },
            {
              "icon": "",
              "label": "都不是 — 聚合筛选需要一个子查询"
            },
            {
              "icon": "",
              "label": "HAVING — WHERE 在分组发生前筛选单个行，HAVING 在聚合后筛选组"
            }
          ]
        },
        {
          "question": "你写 SELECT salary * 1.1 AS new_salary FROM employees WHERE new_salary > 50000。这会运行吗？",
          "options": [
            {
              "icon": "",
              "label": "是的 — 在 SELECT 中定义的别名总是在同个查询中对 WHERE 可用"
            },
            {
              "icon": "",
              "label": "是的，但只对数字别名"
            },
            {
              "icon": "",
              "label": "不是 — AS 在被 WHERE 筛选的查询中不被允许"
            },
            {
              "icon": "",
              "label": "不是 — WHERE 在 SELECT 给别名 new_salary 分配前被计算，所以那时别名还不存在"
            }
          ]
        },
        {
          "question": "SELECT name FROM employees e WHERE salary > (SELECT AVG(salary) FROM employees WHERE department = e.department)。为什么这叫一个关联子查询？",
          "options": [
            {
              "icon": "",
              "label": "因为它用一个 JOIN 而不是一个 WHERE 子句"
            },
            {
              "icon": "",
              "label": "内层子查询引用了外层查询的 e.department，所以它必须对外层查询考虑的每一行重新计算"
            },
            {
              "icon": "",
              "label": "因为它返回超过一列"
            },
            {
              "icon": "",
              "label": "因为它在外层查询开始前恰好运行一次"
            }
          ]
        },
        {
          "question": "一个子查询 SELECT manager_id FROM employees 返回一些 NULL 值和实际 ids。你运行 SELECT name FROM employees WHERE id NOT IN (SELECT manager_id FROM employees)。会发生什么？",
          "options": [
            {
              "icon": "",
              "label": "它返回不是经理的每个员工，忽视 NULL"
            },
            {
              "icon": "",
              "label": "它返回零行 — NOT IN 列表中的单个 NULL 让每个比较都是 UNKNOWN，所以没有行能满足条件"
            },
            {
              "icon": "",
              "label": "它抛出一个错误因为 NOT IN 不能与子查询一起用"
            },
            {
              "icon": "",
              "label": "它返回每个员工，因为 NULL 被当作通配符"
            }
          ]
        },
        {
          "question": "SELECT name FROM customers c WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.id)。这返回什么？",
          "options": [
            {
              "icon": "",
              "label": "至少有一行在 orders 中的每个客户 — EXISTS 只检查子查询是否返回任何行，不检查它们包含什么值"
            },
            {
              "icon": "",
              "label": "每个客户，因为 SELECT 1 总是返回 true"
            },
            {
              "icon": "",
              "label": "一个错误，因为子查询选择一个数字而不是列名"
            },
            {
              "icon": "",
              "label": "只有恰好有一个订单的客户"
            }
          ]
        },
        {
          "question": "你写 SELECT name, (SELECT order_id FROM orders WHERE customer_id = c.id) AS last_order FROM customers c，并且一个给定的客户有 3 行在 orders 中。当这个查询运行时会发生什么？",
          "options": [
            {
              "icon": "",
              "label": "它返回第一个匹配 order_id 并无声地忽视其他两个"
            },
            {
              "icon": "",
              "label": "它返回一个逗号分隔的全部三个 order_ids 列表"
            },
            {
              "icon": "",
              "label": "它为那个客户返回 3 行，每个订单一行"
            },
            {
              "icon": "",
              "label": "它在运行时抛出一个错误 — SELECT 列表中的标量子查询必须返回最多一行，这个返回三行"
            }
          ]
        },
        {
          "question": "四行并列最高分。使用 RANK() ORDER BY score DESC，全部四行得到 rank 1。下一行得到什么 rank？",
          "options": [
            {
              "icon": "",
              "label": "5 — RANK() 在继续前留下一个等于并列行数的间隙"
            },
            {
              "icon": "",
              "label": "2 — RANK() 在任何并列后总是恰好增加一"
            },
            {
              "icon": "",
              "label": "1 — 每个后续行也得到 rank 1"
            },
            {
              "icon": "",
              "label": "4 — RANK() 从并列行数重启计数"
            }
          ]
        },
        {
          "question": "四行并列最高分。使用 DENSE_RANK() ORDER BY score DESC，全部四行得到 rank 1。下一行得到什么 rank？",
          "options": [
            {
              "icon": "",
              "label": "5 — DENSE_RANK() 表现得完全像 RANK()"
            },
            {
              "icon": "",
              "label": "1 — DENSE_RANK() 给每个剩下的行赋相同 rank"
            },
            {
              "icon": "",
              "label": "2 — DENSE_RANK() 永不留间隙，所以下个不同值总是得到下个连续 rank"
            },
            {
              "icon": "",
              "label": "3 — DENSE_RANK() 每个并列组跳过一个 rank"
            }
          ]
        },
        {
          "question": "SUM(amount) OVER (PARTITION BY region ORDER BY amount) 被加到一个查询。PARTITION BY region 在这里做什么？",
          "options": [
            {
              "icon": "",
              "label": "它为每个 region 分别重启累积和，而不是跨整个结果集累积"
            },
            {
              "icon": "",
              "label": "它筛选结果到单个 region"
            },
            {
              "icon": "",
              "label": "它像 GROUP BY 一样把行分组并坍塌成每个 region 一行"
            },
            {
              "icon": "",
              "label": "它在求和前按地区字母排序"
            }
          ]
        },
        {
          "question": "ROW_NUMBER() OVER (ORDER BY score DESC) 被应用到五行，其中两行在分数上完全并列。两行会得到相同的行号吗？",
          "options": [
            {
              "icon": "",
              "label": "是的 — 并列行总是共享相同的行号"
            },
            {
              "icon": "",
              "label": "只有 PARTITION BY 也被用时"
            },
            {
              "icon": "",
              "label": "取决于并列是否在第一个还是最后位置"
            },
            {
              "icon": "",
              "label": "不是 — ROW_NUMBER() 总是给每一行赋一个唯一的、严格递增的整数，即使值被并列"
            }
          ]
        },
        {
          "question": "WITH high_earners AS (SELECT * FROM employees WHERE salary > 100000) SELECT department, COUNT(*) FROM high_earners GROUP BY department。high_earners 是什么？",
          "options": [
            {
              "icon": "",
              "label": "一个公用表表达式（CTE） — 一个命名的、临时结果集，查询的其余部分可以像一个表一样引用"
            },
            {
              "icon": "",
              "label": "一个在数据库中创建的永久表"
            },
            {
              "icon": "",
              "label": "一个在查询完成后坚持的视图"
            },
            {
              "icon": "",
              "label": "一个必须分别被调用的存储过程"
            }
          ]
        },
        {
          "question": "一个列被声明为 PRIMARY KEY。你能插入一行其中那列是 NULL 吗？",
          "options": [
            {
              "icon": "",
              "label": "是的 — PRIMARY KEY 只强制唯一性，不是 NULL 性"
            },
            {
              "icon": "",
              "label": "是的，但只允许一个 NULL 行，和一个 UNIQUE 约束一样"
            },
            {
              "icon": "",
              "label": "不是 — 一个 PRIMARY KEY 列隐含 NOT NULL，所以插入 NULL 到它被拒绝"
            },
            {
              "icon": "",
              "label": "取决于列是否也有默认值"
            }
          ]
        },
        {
          "question": "在一个打开的事务内，你运行一个 UPDATE 但还没运行 COMMIT。从第二个分开的数据库连接，那个更新可见吗？",
          "options": [
            {
              "icon": "",
              "label": "是的 — 所有连接看到每个写就在它运行时"
            },
            {
              "icon": "",
              "label": "是的，但只有第二个连接也打开一个事务时"
            },
            {
              "icon": "",
              "label": "仅取决于哪个表被更新"
            },
            {
              "icon": "",
              "label": "不是 — 一个未提交的改动只在做改动的事务内可见，直到 COMMIT 让它持久并对其他人可见"
            }
          ]
        },
        {
          "question": "products.category_id 有一个引用 categories.id 的 FOREIGN KEY 约束。你试图删除一行从 categories，它仍有 products 指向它，没有指定 ON DELETE 规则。会发生什么？",
          "options": [
            {
              "icon": "",
              "label": "category 行被删除并匹配的 products.category_id 值被自动设为 NULL"
            },
            {
              "icon": "",
              "label": "category 行被删除并引用它的每个 product 也被删除"
            },
            {
              "icon": "",
              "label": "DELETE 被拒绝 — 默认外键行为在还有相关行指向它时阻止删除一个被引用的行"
            },
            {
              "icon": "",
              "label": "DELETE 无声地成功，留下 products 的 category_id 指向一个不再存在的 category"
            }
          ]
        }
      ],
      "optionOrderVersion": "e0466e547cfd8a45"
    }
  }
}
