乐途乐途
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
    • HTTP协议
  • 数据库

    • SQL
    • MySQL 5.7
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON
    • XML
  • 认证与安全

    • JWT
  • 工具

    • Markdown
  • Git

    • GitFlow
  • Quartz

    • Quartz
  • Java

    • MyBatis
    • Spring
    • Spring MVC
    • Maven 入门
    • Maven 进阶
    • Java 设计模式
  • 缓存

    • Redis
联系
阿里云
  • 学习路径
  • 第1章 数据库基础与安装

    • MySQL 简介
    • MySQL 5.6 到 5.7 到 8.0 关键差异速查
    • 安装 MySQL 5.7
    • 连接与断开服务器
    • 创建数据库
    • 创建数据表
    • 数据库与数据表
    • 加载数据
    • 获取数据库信息
    • 批处理模式
    • SHOW 语句汇总
    • FLUSH 与 RESET 语句
    • my.cnf 核心参数
    • 字符集与排序规则
  • 第2章 SQL基础查询

    • SELECT
    • WHERE
    • ORDER BY
    • LIMIT
    • COUNT
    • 聚合函数
    • 比较运算符
    • 逻辑运算符
    • 算术运算符
    • 模式匹配
    • NULL 值处理
    • UPDATE
    • DELETE
    • REPLACE
    • SELECT INTO
  • 第3章 数据类型与运算符

    • 数值类型
    • 字符串类型
    • 日期时间类型
    • BIT 类型
    • ENUM 类型
    • SET 类型
    • JSON 类型
    • 类型转换
  • 第4章 函数与表达式

    • 字符串函数
    • 数值函数
    • 日期函数
    • 全文检索函数
  • 第5章 高级查询与子查询

    • JOIN
    • 子查询
    • UNION
    • GROUP BY
    • HAVING
    • DISTINCT
  • 第6章 表与索引

    • 数据定义语言
    • 修改表结构
    • 视图
    • 修改视图与检查选项
    • 外键
    • 索引
    • 唯一索引
    • 复合索引
    • 存储引擎对比
    • 分区表
    • 第一范式与第二范式
    • 第三范式与 BC 范式
    • 反范式设计
  • 第7章 存储过程与函数

    • 存储过程
    • 存储函数
    • 变量
    • 流程控制
    • 游标
    • 预处理语句
  • 第8章 事务与锁

    • 事务
    • 事务隔离级别
    • 锁机制
    • MVCC
    • 死锁专题分析
    • LOCK TABLES
    • XA 事务
  • 第9章 用户管理与安全

    • 用户管理
    • 权限管理
    • 角色
    • SQL 注入防范
  • 第10章 性能优化入门

    • 执行计划
    • 索引优化
    • 查询优化
    • 查询优化器提示
    • 慢查询日志
    • InnoDB 深入机制
    • InnoDB 专项优化
    • Performance Schema
    • sys Schema
  • 第11章 复制与高可用

    • 主从复制原理
    • 半同步复制配置
    • binlog 开启与 point-in-time 恢复
    • mysqldump 全库备份
    • mysqldump 单表与条件备份
    • mysqldump 恢复与导入
    • xtrabackup 全量热备
    • xtrabackup 准备与恢复
    • xtrabackup 增量与流式备份
  • 第12章 触发器与事件

    • 触发器
    • 事件调度器
  • 参考

    • MySQL 5.7 专业术语大全
    • MySQL 5.7 关键字与保留字大全

SELECT INTO

导学

查询结果除了展示在屏幕上,还经常需要保存到变量供后续使用,或者导出到文件供其他系统消费。SELECT ... INTO 提供了两种能力:将查询结果赋值给用户变量,或将结果集写入外部文件。掌握这两种用法,能让 SQL 与应用程序、外部工具无缝协作。

定义

SELECT ... INTO:MySQL 提供的查询结果输出语法,支持两种形式:

  1. SELECT ... INTO @变量:将查询结果赋值给用户变量(仅限单行单列或单行多列)
  2. SELECT ... INTO OUTFILE:将查询结果导出到服务器端的文本文件

核心语法

-- 形式一:赋值给变量(单行结果)
SELECT 列名 INTO @变量 FROM 表名 WHERE 条件;

-- 形式二:赋值给多个变量(单行多列)
SELECT 列1, 列2 INTO @var1, @var2 FROM 表名 WHERE 条件;

-- 形式三:导出到文件
SELECT 列名 FROM 表名 INTO OUTFILE '/path/to/file.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n';
形式结果行数要求结果列数要求用途
INTO @var必须恰好 1 行1 列获取单个值到变量
INTO @var1, @var2必须恰好 1 行与变量数相同获取一行多列到多个变量
INTO OUTFILE任意行数任意列数导出结果集到文件

SQL 示例

场景一:查询结果赋值给单个变量

演示数据准备:

DROP TABLE IF EXISTS employees;

CREATE TABLE employees (
    emp_id INT PRIMARY KEY AUTO_INCREMENT,
    emp_name VARCHAR(20),
    dept VARCHAR(20),
    score DECIMAL(5,2)
);

INSERT INTO employees (emp_name, dept, score) VALUES
('大翔', '技术部', 100),
('白歌', '技术部', NULL);

当前 employees 表中的完整数据如下:

emp_idemp_namedeptscore
1大翔技术部100
2白歌技术部NULL

执行语句:

-- 查询大翔的分数,保存到变量
SELECT score INTO @daxiang_score
FROM employees WHERE emp_name = '大翔';

-- 查看变量值
SELECT @daxiang_score;

操作后结果:

@daxiang_score
100

结果解读:

  • SELECT ... INTO @变量 将查询结果的单行单列值赋给用户变量
  • 变量以 @ 开头,会话级生命周期,连接断开后消失
  • 后续 SQL 可以直接使用 @daxiang_score 参与计算

场景二:查询结果赋值给多个变量

当前数据状态:见上文 employees 表完整数据。

执行语句:

-- 查询大翔的部门和分数,分别保存到两个变量
SELECT dept, score INTO @daxiang_dept, @daxiang_score
FROM employees WHERE emp_name = '大翔';

-- 查看变量
SELECT @daxiang_dept, @daxiang_score;

操作后结果:

@daxiang_dept@daxiang_score
技术部100

结果解读:

  • 一行多列可以分别赋给多个变量,变量数量和列数必须一致
  • 变量赋值后可在同一事务或会话中复用,比如用于后续 INSERT 或 UPDATE 的条件

场景三:结果集导出到 CSV 文件

当前数据状态:见上文 employees 表完整数据。

执行语句:

-- 导出 employees 表到 CSV(需有 FILE 权限,且路径在 secure-file-priv 下)
SELECT emp_id, emp_name, dept, score
FROM employees
INTO OUTFILE '/var/lib/mysql-files/employees.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n';

查看导出文件内容(假设文件可读取):

"1","大翔","技术部","100"
"2","白歌","技术部",NULL

结果解读:

  • INTO OUTFILE 将查询结果写入服务器端文件,不是客户端本地
  • 文件路径必须在 secure_file_priv 指定的目录下(5.7 默认开启限制)
  • FIELDS TERMINATED BY 指定列分隔符,ENCLOSED BY 指定引号包裹,LINES TERMINATED BY 指定换行符
  • 需要 FILE 权限(通常只有 root 或授权用户有)

场景四:INTO OUTFILE 与 LOAD DATA INFILE 配合

当前数据状态:见上文 employees 表完整数据。

执行语句:

-- 步骤 1:导出技术部员工到文件
SELECT emp_id, emp_name, score
FROM employees
WHERE dept = '技术部'
INTO OUTFILE '/var/lib/mysql-files/tech_employees.csv'
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n';

-- 步骤 2:创建新表并导入
CREATE TABLE tech_backup (
    emp_id INT,
    emp_name VARCHAR(20),
    score DECIMAL(5,2)
);

LOAD DATA INFILE '/var/lib/mysql-files/tech_employees.csv'
INTO TABLE tech_backup
FIELDS TERMINATED BY ','
ENCLOSED BY '"'
LINES TERMINATED BY '\n';

-- 查看导入结果
SELECT * FROM tech_backup;

操作后结果:

emp_idemp_namescore
1大翔100
2白歌NULL

结果解读:

  • INTO OUTFILE 和 LOAD DATA INFILE 是数据迁移的黄金组合
  • 导出和导入的格式参数必须一致(分隔符、引号、换行符)
  • 这是 MySQL 内置的最快数据迁移方式,无需经过客户端中转

场景五:多行结果 INTO 变量的陷阱

当前数据状态:见上文 employees 表完整数据。

执行语句(错误示例):

-- 错误:查询返回多行,INTO 变量会报错
SELECT score INTO @all_scores FROM employees;

操作后结果:

ERROR 1172 (42000): Result consisted of more than one row

正确做法:

-- 只取一行
SELECT score INTO @first_score FROM employees LIMIT 1;

-- 或使用聚合函数保证单行
SELECT AVG(score) INTO @avg_score FROM employees;

操作后结果:

@first_score@avg_score
100100

结果解读:

  • SELECT ... INTO @变量 要求查询结果恰好一行
  • 返回 0 行:变量保持原值(或 NULL)
  • 返回多行:报错 ERROR 1172
  • 需要多行结果时,不能用 INTO 变量,应使用游标(Cursor)逐行处理

常见误区

误区正解
"SELECT INTO 和 SET @var = (SELECT ...) 一样"不一样。SELECT INTO 更严格(要求恰好一行),但语义更清晰;SET 配合子查询更灵活,但子查询返回多行时行为不同。
"INTO OUTFILE 可以写到客户端本地"不能。INTO OUTFILE 写到服务器端文件。客户端本地导出应使用命令行 mysql -e "SELECT ..." > file.csv。
"任何人都能用 INTO OUTFILE"不能。需要 FILE 权限,且路径受 secure_file_priv 限制。
"INTO OUTFILE 会覆盖已有文件"不会。如果文件已存在,MySQL 会报错。需要先删除或使用 SELECT ... INTO DUMPFILE(单文件)。
"SELECT ... INTO @var 可以存多行"不能。只能存单行结果。多行需要用游标或临时表。
"变量赋值后永久保存"不是。用户变量(@var)是会话级的,连接断开即消失。需要持久化应存入表或配置文件。

面试考点

Q:SELECT ... INTO @var 和 SET @var = (SELECT ...) 的区别?

SELECT ... INTO 要求查询恰好返回一行,语义明确,多行直接报错;SET @var = (SELECT ...) 中子查询返回多行时在某些场景下行为不一致(可能报错或取第一行)。SELECT INTO 是标准 SQL 写法,推荐用于明确的单行赋值。

Q:INTO OUTFILE 和 LOAD DATA INFILE 的路径限制?

两者都受 secure_file_priv 限制。SHOW VARIABLES LIKE 'secure_file_priv'; 查看允许路径。INTO OUTFILE 只能写到该目录;LOAD DATA INFILE 默认也只能从该目录读取(LOCAL 版本可从客户端上传)。

Q:如何将查询结果导出到客户端本地?

  1. 命令行:mysql -u root -p -e "SELECT * FROM employees" > local.csv;2. 客户端工具(Navicat、MySQL Workbench)的导出功能;3. 程序中使用驱动 API 读取结果集后写入本地文件。INTO OUTFILE 只能写服务器端。

Q:SELECT ... INTO 返回 0 行会怎样?

变量保持原值(如果之前未赋值则为 NULL)。不会报错。这与 SET @var = (SELECT ...) 中子查询返回 NULL 的行为类似。如果业务上要求必须查到数据,应加 LIMIT 1 并在应用层判断变量是否为 NULL。

Q:INTO OUTFILE 的格式参数怎么选?

根据下游消费方选择:Excel 常用 FIELDS TERMINATED BY ',' ENCLOSED BY '"';Linux 工具常用 FIELDS TERMINATED BY '\t'(制表符);Hive 导入常用 LINES TERMINATED BY '\n'。关键是导出和导入的格式必须一致。

小结

  • SELECT ... INTO @变量 将单行查询结果赋给用户变量,多行会报错
  • SELECT ... INTO OUTFILE 将结果集导出到服务器端文件,需 FILE 权限和 secure_file_priv 路径
  • INTO OUTFILE + LOAD DATA INFILE 是 MySQL 内置的最快数据迁移组合
  • 用户变量(@var)会话级生命周期,适合同一事务/会话内的数据传递
  • 多行结果不能用 INTO 变量,应使用游标或临时表

下一章引子:变量和文件操作让 SQL 更灵活,但在高并发场景下,显式锁表(LOCK TABLES)是另一种控制数据访问的手段——它比事务更粗粒度,但在特定场景下不可或缺。

上一页
REPLACE