yukiの宝藏之地
👥累计访客:--
首页项目归档照片墙音乐灵境说说杂谈友链关于
封面👁--阅读

MySQL 数据库操作指令

写作时间:2026-08-19 23:30:13
# MySQL
# CTF
# web

CTF Web 方向的 MySQL 基础:先认识数据库与 SQL 的基本概念,再到连接、库表操作、增删改查、SELECT 详解与常用查询指令。本文是 SQL 注入学习的前置知识。


1. 认识数据库与 SQL

在讲 SQL 之前,不妨先想想数据库是什么。

你每天打开 B 站刷视频、在淘宝下单、在微博发帖——这些看起来再普通不过的操作背后,其实都有一个数据库在默默运转。你的账号信息、浏览记录、订单数据、评论内容,全都存在数据库里:网站需要的时候就去查,你更新了就去写,你删了就去删。数据库,其实就是整个 Web 应用真正意义上的"记忆"。

没有数据库,网站不过是一张静态的纸;有了数据库,它才真正活起来。

‍

1.1 数据库长什么样?

其实关系型数据库的结构没有想象中复杂,可以这样理解:

层级 说明
数据库(Database) 最顶层的容器,一个网站通常会有一个或多个数据库
数据表(Table) 数据库里的表格,比如用户表、订单表、文章表
字段(Column) 表格的列,比如用户表里的 id、username、password、email
记录(Row) 表格的行,每一行就是一条完整的数据,比如某个用户的全部信息

拿一张简单的用户表举例,大概是这个样子:

id username password email
1 小明 xxxxxx xm@qq.com
2 小红 xxxxxx xh@qq.com

这张表里存着所有注册用户的信息。你登录的时候,网站要做的事其实很简单:去这张表里查一查,你输入的用户名和密码对不对。

‍

1.2 SQL 是什么?

SQL,全称 Structured Query Language(结构化查询语言),就是用来跟数据库"对话"的工具。

它不属于哪一家公司,而是几乎所有关系型数据库通用的沟通方式。无论是 CTF 里最常见的 MySQL,还是企业级的 PostgreSQL、MSSQL,或者轻量级的 SQLite,背后都在用 SQL 跟数据打交道。

SQL 要做的事,说到底只有四件

操作 关键字 含义
增(Create) INSERT 往数据库里写新数据
查(Read) SELECT 从数据库里取数据
改(Update) UPDATE 修改已有的数据
删(Delete) DELETE 删掉数据

这四个操作合称 CRUD,也就是我们常说的"增删改查"——它们是所有数据库操作的基础,后面学到的注入手法,本质上也都是围绕这四件事做文章。

其中 SELECT(查)是我们打交道最多的一个,几乎所有的注入手法最终都是围绕着一句 SELECT 语句展开的。下面就从连接数据库开始,把这些指令逐一过一遍。


2. 连接与退出

# 连接数据库(-h 主机,-P 端口,-u 用户,-p 密码)
mysql -h 127.0.0.1 -P 3306 -u root -p

# 退出
exit;
quit;

3. 数据库操作

-- 查看所有数据库
SHOW DATABASES;

-- 创建数据库
CREATE DATABASE ctf;

-- 切换数据库
USE ctf;

-- 删除数据库
DROP DATABASE ctf;

-- 查看当前使用的数据库
SELECT DATABASE();

4. 表操作

-- 查看当前库中所有表
SHOW TABLES;

-- 创建表
CREATE TABLE users (
    id INT PRIMARY KEY AUTO_INCREMENT,
    username VARCHAR(50) NOT NULL,
    password VARCHAR(50) NOT NULL,
    email VARCHAR(50)
);

-- 查看表结构
DESC users;
-- 或
SHOW COLUMNS FROM users;

-- 删除表
DROP TABLE users;

5. 增删改查(CRUD)

对应第 1 节提到的四个基本操作,语法如下:

增:插入数据(INSERT)

INSERT INTO users (username, password, email) VALUES ('小刚', '123456', 'xg@qq.com');

往 users 表里插入一条新记录,对应的就是"注册"这个动作。

查:查询数据(SELECT)

-- 查询所有
SELECT * FROM users;

-- 条件查询
SELECT * FROM users WHERE id = 1;

-- 指定字段
SELECT username, password FROM users WHERE username = 'admin';

-- 排序与限制
SELECT * FROM users ORDER BY id DESC LIMIT 10;

-- 模糊查询
SELECT * FROM users WHERE username LIKE '%admin%';

SELECT 的用法远不止这些,第 6、7 节会单独展开细说。

改:更新数据(UPDATE)

UPDATE users SET email = 'new@qq.com' WHERE username = '小明';

把用户名是"小明"的那条记录,email 字段改成新的值。

删:删除数据(DELETE)

DELETE FROM users WHERE username = '小明';

把用户名是"小明"的那条记录删掉。

6. SELECT 查询详解

之所以把 SELECT 单独拎出来讲,是因为不管是正常的业务查询,还是后面要学的 SQL 注入,绝大部分场景都是在跟一句 SELECT 语句打交道。把这部分吃透,后面理解注入原理会顺畅很多。

基础查询词语:select / from / where

这三个词是 SELECT 语句的骨架,缺一不可。

SELECT username, email FROM users WHERE id = 1;
  • SELECT:告诉数据库"我要查哪些字段",比如这里的 username、email;想要全部字段就写 *
  • FROM:告诉数据库"去哪张表里查",这里是 users 表
  • WHERE:告诉数据库"查询的条件是什么",这里是 id 等于 1

三者组合起来读,就是:从 users 表里,找出 id 等于 1 的那一行,把它的 username 和 email 取出来。网站的登录逻辑,背后往往就是一句类似的 SELECT:

SELECT * FROM users WHERE username = '小明' AND password = '123456';

能查到匹配的记录,就说明用户名密码正确。

子查询

子查询,简单说就是"查询里面还嵌着一个查询",外层查询要用到内层查询的结果。

SELECT * FROM users WHERE id = (SELECT id FROM users WHERE username = '小明');

括号里的 SELECT id FROM users WHERE username = '小明' 会先执行,查出小明的 id,然后外层再拿这个 id 去查完整信息。子查询在 SQL 注入里出现的频率很高,因为很多时候我们要在一条语句里,先"问"出一个信息,再拿这个信息去做下一步查询。

7. 常用查询指令进阶

以下指令在数据查询和筛选中非常常用,建议逐个动手试一试。为便于演示,假设有如下 users 表:

id username password age city
1 admin 123456 25 北京
2 小明 xxxxxx 20 上海
3 小红 xxxxxx 22 北京
4 小刚 xxxxxx 20 上海

AND / OR:组合条件

-- AND:两个条件同时满足
SELECT * FROM users WHERE age = 20 AND city = '上海';
-- 结果:小明、小刚

-- OR:满足任意一个条件即可
SELECT * FROM users WHERE city = '北京' OR age = 22;
-- 结果:admin、小红

-- 混合使用时建议加括号明确优先级(AND 优先级高于 OR)
SELECT * FROM users WHERE city = '上海' AND (age = 20 OR age = 25);

ORDER BY:排序

-- 按年龄升序(ASC 可省略,默认升序)
SELECT * FROM users ORDER BY age ASC;

-- 按年龄降序
SELECT * FROM users ORDER BY age DESC;

-- 多字段排序:先按城市升序,同城再按年龄降序
SELECT * FROM users ORDER BY city ASC, age DESC;

-- 也可以按列的位置排序(第 2 列即 username)
SELECT * FROM users ORDER BY 2;

GROUP BY:分组统计

-- 按城市分组,统计每个城市有多少用户
SELECT city, COUNT(*) FROM users GROUP BY city;
-- 结果:北京 2,上海 2

-- 按年龄分组,查看每个年龄的用户数
SELECT age, COUNT(*) AS num FROM users GROUP BY age ORDER BY num DESC;

-- 配合 HAVING 过滤分组结果(HAVING 作用于分组后,WHERE 作用于分组前)
SELECT age, COUNT(*) AS num FROM users GROUP BY age HAVING num > 1;
-- 结果:只有 age = 20(有 2 人)

[!tip]
CTF 里经常会用 ORDER BY 数字或GROUP BY 数字 来判断当前查询结果一共有几列,这是联合查询注入的关键一步。

LIMIT:限制返回行数

-- 只取前 2 条
SELECT * FROM users LIMIT 2;

-- LIMIT 偏移量, 条数:跳过前 1 条,取 2 条(即从第 0 条开始数的第 1、2 条)
SELECT * FROM users LIMIT 1, 2;
-- 结果:小明、小红

-- 常与 ORDER BY 配合:取年龄最大的 1 个人
SELECT * FROM users ORDER BY age DESC LIMIT 1;

GROUP_CONCAT:分组字段拼接

-- 把每个城市的所有用户名拼成一行
SELECT city, GROUP_CONCAT(username) FROM users GROUP BY city;
-- 结果:北京 → admin,小红    上海 → 小明,小刚

-- 自定义分隔符
SELECT GROUP_CONCAT(username SEPARATOR ' | ') FROM users;
-- 结果:admin | 小明 | 小红 | 小刚

如果不用 GROUP_CONCAT(),一次查询只能拿到一行结果;而在注入场景里,我们往往一次只有一个"出口"能把数据带出来,这时候 GROUP_CONCAT() 就能把原本分散在多行里的信息(比如所有表名、所有用户名)压缩进一行,一次性带出来,非常关键。

‍

UNION:联合查询

-- 合并两次查询的结果(列数必须一致,类型尽量兼容)
SELECT username, city FROM users WHERE age = 20
UNION
SELECT username, city FROM users WHERE city = '北京';

-- UNION 默认去重,想保留重复行用 UNION ALL
SELECT city FROM users UNION ALL SELECT city FROM users;

-- 测试列数是否匹配的常用写法
SELECT username, password FROM users UNION SELECT 1, 2;

[!note] UNION 的要点

  • 前后两个 SELECT 的列数必须相同,否则报错
  • 可以用 UNION SELECT 1, 2, 3 快速试探查询的列数
  • 显示位置不够时,可以用 NULL 占位

DATABASE() / VERSION():常用内置函数

-- 当前正在使用的数据库名
SELECT DATABASE();

-- MySQL 版本号
SELECT VERSION();

-- 其他常见内置函数
SELECT USER();        -- 当前登录用户
SELECT NOW();         -- 当前时间
SELECT LENGTH('abc'); -- 字符串长度:3

‍

avatar

Yuki

在代码、学术与网安间穿梭的普通人。

RECOMMENDED

分享我的博客搭建与部署~

2026-08-18 20:37:28

Table of Contents