SQL 进阶地图
与 sql-essentials 分工;RDBMS 节点概览。
SQL 进阶地图
这一课你将能做到
- 说清楚基础 SQL(SELECT、JOIN、GROUP BY)与本 track 进阶内容(DDL、DML、窗口函数、约束、事务、调优)的分工边界
- 在本机搭好
sql_practice练习库,建出cf_user、cf_problem、cf_submission、cf_practice_record四张做题平台业务表并灌入可复现的示例数据 - 跑通一条聚合自检查询,输出与本文给出的结果表完全一致,确认环境、数据、版本三者都就绪
为什么要学
「会写 SELECT」只是 SQL 的及格线。真正的分水岭在后面:表结构怎么设计才不返工、并发下数据怎么不错不乱、一条慢 SQL 怎么从 2 秒压到 20 毫秒、面试官问「RR 级别下幻读到底防没防」你能不能答出 MVCC 和间隙锁的分工。这些内容全部落在本 track 的 12 课里。
更实际的一点:这套课不是拿抽象的 A 表 B 表练手,而是用本站(CodeForge 刷题平台)的真实业务表贯穿——用户、题目、提交、刷题记录。学完每一课,你手里都有一套可以反复跑、反复改、结果可预期的练手库;以后你进入任何一家公司的项目,看到的表结构大概率就是这几张表的变体(用户表、内容表、行为流水表、配置表)。
人话:基础 SQL 让你「会查数据」,进阶 SQL 让你「设计表、保数据、提速度」。前者是入职门槛,后者是面试加分项和日常排障的看家本领。
先建立图像
整站核心表关系(第 8、10 课还会引入课时表与钱包表):
cf_user(用户)
│ 1:N
├──> cf_submission(提交)<── N:1 ── cf_problem(题目)
│ 1:N
└──> cf_practice_record(刷题记录)<── N:1 ──┘
四张表各自的职责:
| 表 | 干什么 | 关键列 |
|---|---|---|
| cf_user | 平台注册用户 | username、email、level、status |
| cf_problem | 题目元数据 | slug、difficulty(easy/medium/hard) |
| cf_submission | 每次判题机产生的提交流水 | user_id、problem_id、status、runtime_ms |
| cf_practice_record | 用户主动练习的行为记录 | user_id、problem_id、is_correct、duration_sec |
记住这个区分:submission 是机器写入的流水,practice_record 是用户行为。后面统计 AC 率用前者,统计学习时长用后者,混用是新手常犯的错。
跟着做
步骤 1:确认 MySQL 版本
SELECT VERSION();
输出必须是 8.0.x。窗口函数、CTE、CHECK 约束、角色管理都是 8.0 引入或加固的,5.7 跑本 track 会直接语法报错。
步骤 2:建库
CREATE DATABASE IF NOT EXISTS sql_practice
DEFAULT CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci;
USE sql_practice;
步骤 3:建四张业务表
CREATE TABLE IF NOT EXISTS cf_user (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '用户ID',
username VARCHAR(50) NOT NULL COMMENT '用户名',
email VARCHAR(255) NOT NULL COMMENT '邮箱',
level TINYINT UNSIGNED NOT NULL DEFAULT 1 COMMENT '等级 1-5',
status ENUM('active','banned') NOT NULL DEFAULT 'active' COMMENT '账号状态',
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '注册时间',
PRIMARY KEY (id),
UNIQUE KEY uk_username (username),
UNIQUE KEY uk_email (email)
) ENGINE=InnoDB COMMENT='用户表';
CREATE TABLE IF NOT EXISTS cf_problem (
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
slug VARCHAR(80) NOT NULL COMMENT '题目标识',
title VARCHAR(200) NOT NULL COMMENT '题目标题',
difficulty ENUM('easy','medium','hard') NOT NULL DEFAULT 'easy',
PRIMARY KEY (id),
UNIQUE KEY uk_slug (slug)
) ENGINE=InnoDB COMMENT='题目表';
CREATE TABLE IF NOT EXISTS cf_submission (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id BIGINT UNSIGNED NOT NULL,
problem_id INT UNSIGNED NOT NULL,
language VARCHAR(20) NOT NULL DEFAULT 'java',
status ENUM('accepted','wrong_answer','runtime_error','time_limit') NOT NULL,
runtime_ms INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '运行耗时毫秒',
submitted_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY idx_user_problem (user_id, problem_id)
) ENGINE=InnoDB COMMENT='提交表';
CREATE TABLE IF NOT EXISTS cf_practice_record (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
user_id BIGINT UNSIGNED NOT NULL,
problem_id INT UNSIGNED NOT NULL,
is_correct TINYINT(1) NOT NULL DEFAULT 0 COMMENT '本次练习是否做对',
duration_sec INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '用时秒',
practiced_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY idx_user_time (user_id, practiced_at)
) ENGINE=InnoDB COMMENT='刷题记录表';