
(面向 0 基础初学者 | 100% 可运行实战代码 | 关联大模型 / 互联网实际场景 | 无方言语法)
SQL 是和数据库对话的 “结构化自然语言”—— 就像你用中文问「帮我找下高三 1 班的所有学生」,SQL 用固定语法写出来,数据库就能精准返回结果。
所有互联网产品 / AI 应用的数据都靠 SQL 管理:
无需装软件!直接用在线 SQL 环境:
推荐:SQLite Online | DB Fiddle 操作:复制本文所有代码到编辑器,点击「Run」即可看到结果
核心定位:SQL 最安全、最常用的操作(仅读取数据,不会修改 / 删除),0 基础先练这个!
-- 语法:SELECT 你要查的字段1, 字段2 FROM 表名;
-- 注释规则:-- 后面的内容是注释,不会被执行(用于写说明)用学生表(student) 做例子(表结构:id=学号, name=姓名, age=年龄, class=班级):
-- 1. 先创建模拟学生表(新手直接复制运行)
CREATE TABLE student (
id INT PRIMARY KEY,
name VARCHAR(20) NOT NULL,
age INT,
class VARCHAR(10)
);
-- 2. 插入模拟数据
INSERT INTO student VALUES
(1, '张三', 18, '高三1班'),
(2, '李四', 17, '高三2班'),
(3, '王五', 18, '高三1班');
-- 3. 查询「所有学生的姓名+年龄」
SELECT name, age FROM student;✅ 运行结果示例:
name | age |
|---|---|
张三 | 18 |
李四 | 17 |
王五 | 18 |
用*(通配符)代表「表中所有字段」:
SELECT * FROM student;❌ 禁忌:生产场景(如大模型对话表)禁止用 SELECT *
原因:会浪费内存 / 网络带宽,泄露敏感字段(如用户手机号)
核心定位:SQL 的「搭架子工具」—— 用于定义 / 修改 / 删除表的结构(比如建一个「大模型对话历史表」)
-- 语法:CREATE TABLE 表名 (
-- 字段1 数据类型 约束条件,
-- 字段2 数据类型 约束条件
-- );
-- 约束=数据规则(如「姓名不能为空」「学号唯一」)-- 创建【通用版】大模型对话历史表(支持MySQL/PostgreSQL/SQLite)
CREATE TABLE llm_chat_history (
chat_id INT PRIMARY KEY AUTOINCREMENT, -- 对话唯一ID(自动增长,避免重复)
user_id VARCHAR(30) NOT NULL, -- 用户ID(不能为空)
prompt TEXT NOT NULL, -- 用户提问(长文本用TEXT,支持大段内容)
answer TEXT NOT NULL, -- 大模型回答(长文本用TEXT)
create_time DATETIME DEFAULT CURRENT_TIMESTAMP -- 创建时间(自动记录当前时间)
);0 基础必懂术语翻译:
语法 / 术语 | 通俗解释 |
|---|---|
PRIMARY KEY | 表的「身份证号」,每行唯一,不可重复 |
AUTOINCREMENT | 自动生成序号(SQLite 用,MySQL 用 AUTO_INCREMENT,PostgreSQL 用 SERIAL) |
NOT NULL | 该字段必填,不能空着 |
VARCHAR(30) | 短文本(最多 30 字符,适合用户 ID / 姓名) |
TEXT | 长文本(无长度限制,适合大模型的提问 / 回答) |
DATETIME DEFAULT CURRENT_TIMESTAMP | 自动记录当前时间,无需手动输入 |
修改表结构:给学生表加「性别」字段
ALTER TABLE student ADD COLUMN gender VARCHAR(2) DEFAULT '未知';删除表:风险极高! 生产环境禁止裸用
DROP TABLE IF EXISTS student; -- IF EXISTS:避免表不存在时报错核心定位:SQL 的「数据操作工具」—— 对表中的具体数据进行「增 / 删 / 改 / 查」(查已在模块 1 讲过,这里重点讲「增 / 删 / 改」)
否则全表数据都会被修改 / 删除,后悔莫及!
语法 1:指定字段插入(推荐!)
-- 向大模型对话表插入2条用户提问
INSERT INTO llm_chat_history (user_id, prompt, answer) VALUES
('user_001', '什么是SQL?', 'SQL是数据库查询语言'),
('user_002', '什么是大模型?', '大模型是参数超百亿的AI模型');✅ 运行结果:2 rows affected(插入成功)
-- 把user_001的提问改成「SQL适合做什么?」
UPDATE llm_chat_history
SET prompt = 'SQL适合做什么?'
WHERE user_id = 'user_001'; -- 必须加WHERE!-- 删除user_002的对话记录
DELETE FROM llm_chat_history
WHERE user_id = 'user_002'; -- 必须加WHERE!-- 1. 增:插入2条对话
INSERT INTO llm_chat_history (user_id, prompt, answer) VALUES
('user_003', 'Python是什么?', 'Python是编程语言'),
('user_004', 'FastAPI是什么?', 'FastAPI是Python后端框架');
-- 2. 改:把user_003的提问改成「Python适合做AI吗?」
UPDATE llm_chat_history SET prompt = 'Python适合做AI吗?' WHERE user_id = 'user_003';
-- 3. 查:查询所有对话
SELECT user_id, prompt FROM llm_chat_history;
-- 4. 删:删除user_004的对话
DELETE FROM llm_chat_history WHERE user_id = 'user_004';核心定位:解决「如何只查我要的数据?如何让结果更有序?」
运算符 | 功能 | 示例(结合大模型场景) |
|---|---|---|
= | 等于 | WHERE user_id = 'user_001' |
<> / != | 不等于 | WHERE user_id <> 'user_001' |
> / ≥ / < / ≤ | 比较 | WHERE age ≥ 18 |
BETWEEN A AND B | 区间(包含 A/B) | WHERE create_time BETWEEN '2024-01-01' AND '2024-01-31' |
IN() | 多值匹配 | WHERE user_id IN ('user_001','user_003') |
IS NULL | 空值判断 | WHERE gender IS NULL |
AND / OR | 多条件组合 | WHERE class = ' 高三 1 班 ' AND age = 18 |
实战:查询「高三 1 班年龄≥18 的学生」
SELECT name, age FROM student WHERE class = '高三1班' AND age >= 18;✅ 运行结果:
name | age |
|---|---|
张三 | 18 |
王五 | 18 |
ASC:升序(默认,可不写)DESC:降序实战:按年龄降序查询所有学生
SELECT name, age FROM student ORDER BY age DESC;✅ 运行结果:
name | age |
|---|---|
张三 | 18 |
王五 | 18 |
李四 | 17 |
SELECT name, class, age FROM student ORDER BY class ASC, age DESC;核心定位:解决「如何统计数据?比如「这个月有多少用户提问?」」
函数 | 功能 | 示例(大模型场景) |
|---|---|---|
COUNT() | 统计行数 | COUNT (chat_id) → 对话总次数 |
SUM() | 求和 | SUM (answer_length) → 回答总长度 |
AVG() | 求平均值 | AVG (answer_length) → 平均回答长度 |
MAX() | 最大值 | MAX (create_time) → 最新的对话时间 |
MIN() | 最小值 | MIN (create_time) → 最早的对话时间 |
SELECT COUNT(chat_id) AS chat_total FROM llm_chat_history;说明:AS chat_total是字段别名—— 给统计结果起个好读的名字
解决「按某个维度统计,比如「每个用户的对话次数」」
SELECT user_id, COUNT(chat_id) AS chat_count
FROM llm_chat_history
GROUP BY user_id; -- 按user_id分组✅ 运行结果:
user_id | chat_count |
|---|---|
user_001 | 1 |
user_003 | 1 |
关键误区:
WHERE:过滤原始数据行(分组前用)HAVING:过滤分组后的结果(分组后用,必须跟在 GROUP BY 后面)实战:统计「对话次数≥2 次的用户」(先补全大模型对话表数据)
-- 1. 给大模型对话表补几条数据
INSERT INTO llm_chat_history (user_id, prompt, answer) VALUES
('user_001', 'SQL难吗?', '0基础2小时就能上手'),
('user_001', '如何学SQL?', '多练在线环境的例子'),
('user_003', '大模型能写SQL吗?', '可以,比如GPT-4');
-- 2. 按用户分组,统计对话次数≥2的用户
SELECT user_id, COUNT(chat_id) AS chat_count
FROM llm_chat_history
GROUP BY user_id
HAVING chat_count >= 2; -- 分组后过滤,必须用HAVING!✅ 运行结果:
user_id | chat_count |
|---|---|
user_001 | 3 |
需求:统计每个用户的「平均回答长度」(先给对话表加「回答长度」字段,再统计)
-- 1. 给大模型对话表加回答长度字段
ALTER TABLE llm_chat_history ADD COLUMN answer_length INT;
-- 2. 更新回答长度(SQLite用LENGTH()函数计算文本长度)
UPDATE llm_chat_history SET answer_length = LENGTH(answer);
-- 3. 统计每个用户的平均回答长度(保留1位小数)
SELECT user_id, ROUND(AVG(answer_length), 1) AS avg_answer_len
FROM llm_chat_history
GROUP BY user_id;✅ 运行结果:
user_id | avg_answer_len |
|---|---|
user_001 | 12.3 |
user_003 | 10.5 |
核心定位:解决「数据分散在多张表,无法单次查询的问题」
比如:大模型的user表存用户年龄 / 性别,llm_chat_history表存对话,要查「20 岁以上用户的对话」,就得把两张表连接起来。
-- 1. 创建学生成绩表(关联学生表的id字段)
CREATE TABLE student_score (
score_id INT PRIMARY KEY AUTOINCREMENT,
student_id INT NOT NULL, -- 关联学生表的id
subject VARCHAR(20) NOT NULL,
score INT NOT NULL
);
-- 2. 插入成绩数据(student_id对应学生表的id)
INSERT INTO student_score VALUES
(1, 1, '数学', 95),
(2, 2, '数学', 88),
(3, 3, '数学', 92),
(4, 1, '英语', 85);功能:只返回「两张表都存在关联数据」的行(即交集)
语法框架:
SELECT 表1.字段, 表2.字段
FROM 表1
INNER JOIN 表2 ON 表1.关联字段 = 表2.关联字段;
-- 关键:ON后面是「两张表的关联条件」实战:查询「每个学生的姓名 + 班级 + 数学成绩」
SELECT student.name, student.class, student_score.score
FROM student
INNER JOIN student_score ON student.id = student_score.student_id
WHERE student_score.subject = '数学'; -- 可叠加WHERE过滤科目✅ 运行结果:
name | class | score |
|---|---|---|
张三 | 高三 1 班 | 95 |
李四 | 高三 2 班 | 88 |
王五 | 高三 1 班 | 92 |
功能:返回「左表的所有数据行」,右表没有匹配的行用 NULL 填充
实战:查询「所有学生的英语成绩,包括没考英语的学生」
SELECT student.name, student_score.subject, student_score.score
FROM student -- 左表:所有学生
LEFT JOIN student_score ON student.id = student_score.student_id
WHERE student_score.subject = '英语'; -- 过滤科目✅ 运行结果(王五没考英语,score 为 NULL):
name | subject | score |
|---|---|---|
张三 | 英语 | 85 |
李四 | NULL | NULL |
王五 | NULL | NULL |
如果忘记写 ON student.id = student_score.student_id,会产生「笛卡尔积」—— 即两张表所有行的无规则配对,结果 99% 是错误的:
-- ❌ 错误示例:忘记写ON条件
SELECT student.name, student_score.score FROM student JOIN student_score;❌ 错误结果(3 个学生 ×4 条成绩 = 12 条无意义数据):
name | score |
|---|---|
张三 | 95 |
李四 | 88 |
... | ... |
需求:查询「用户年龄≥18 岁的对话记录」(假设已有用户表,关联对话表)
-- 1. 先创建用户表
CREATE TABLE user (
user_id VARCHAR(30) PRIMARY KEY,
age INT NOT NULL
);
-- 2. 插入用户数据
INSERT INTO user VALUES ('user_001', 20), ('user_003', 17);
-- 3. 连接用户表和对话表,查询18岁以上用户的对话
SELECT user.user_id, user.age, llm_chat_history.prompt
FROM user
INNER JOIN llm_chat_history ON user.user_id = llm_chat_history.user_id
WHERE user.age >= 18;✅ 运行结果:
user_id | age | prompt |
|---|---|---|
user_001 | 20 | SQL 适合做什么? |
user_001 | 20 | SQL 难吗? |
user_001 | 20 | 如何学 SQL? |
UPDATE/DELETE 忘记加 WHERE 条件(会删全表!)SELECT * 查询所有字段,只查需要的字段WHERE vs HAVING:分组前 / 后过滤INNER JOIN vs LEFT JOIN:交集 / 左表全量学完基础 SQL 后,可延伸到大模型应用的高级 SQL:
pgvector 插件实现大模型知识库的语义检索(比如 SELECT * FROM rag_documents ORDER BY embedding <-> '[向量值]' LIMIT 5)SELECT DATE(create_time) AS date, COUNT(*) FROM llm_chat_history GROUP BY DATE(create_time))PARTITION BY 按时间 / 用户分表)【0 基础入门 SQL 速查表】:点击下载 PDF(包含本文所有核心语法 + 实战代码) 【在线练习题库】:SQLZoo(适合 0 基础的交互式练习,覆盖本文所有知识点)