如何使用shell脚本从数据库取数并拆分生成多个CSV文件
Shell脚本拆分导出数据库数据为多CSV文件实现方案
核心逻辑采用分页查询+直接写入对应文件的方式,避免先导出全量大文件再拆分带来的额外IO开销,100万条数据量级下执行效率很高。
基础规则对齐
- 单文件存储记录数:100000(1 lakh)
- 总生成文件数:10个
- 文件命名规则:
data-1.csv、data-2.csv……data-10.csv - 以下示例以MySQL数据库为例,PostgreSQL、Oracle等其他数据库只需要替换对应的查询命令,分页拆分逻辑完全通用。
踩坑提醒:不要先把100万条数据全量导出为单个CSV再用
split命令拆分,一方面会多一次全量文件读写的磁盘开销,另一方面拆分后的文件默认不会带CSV表头,后续处理很麻烦。
可直接复用的脚本
#!/bin/bash # -------------------------- 按需修改配置项 -------------------------- DB_HOST="127.0.0.1" # 数据库连接地址 DB_PORT="3306" # 数据库端口 DB_USER="export_user" # 数据库账号 DB_PASS="your_password" # 数据库密码 DB_NAME="your_db" # 目标库名 TARGET_TABLE="your_table" # 要导出的目标表名 PER_FILE_LINES=100000 # 单文件记录数 TOTAL_FILES=10 # 总文件数 # ------------------------------------------------------------------- # 自动获取目标表的CSV表头 CSV_HEADER=$(mysql -h${DB_HOST} -P${DB_PORT} -u${DB_USER} -p${DB_PASS} ${DB_NAME} -N -e \ "SELECT GROUP_CONCAT(COLUMN_NAME SEPARATOR ',') FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = '${DB_NAME}' AND TABLE_NAME = '${TARGET_TABLE}' ORDER BY ORDINAL_POSITION;") # 循环生成分页文件 for ((file_seq=1; file_seq<=TOTAL_FILES; file_seq++)) do offset=$(( (file_seq - 1) * PER_FILE_LINES )) current_file="data-${file_seq}.csv" # 先写入表头 echo "${CSV_HEADER}" > ${current_file} # 分页查询数据追加到文件,--csv参数会自动处理字段内的逗号、引号、换行等特殊字符,输出标准CSV格式 # tail -n +2 是为了跳过查询结果自带的表头行 mysql -h${DB_HOST} -P${DB_PORT} -u${DB_USER} -p${DB_PASS} ${DB_NAME} --csv -e \ "SELECT * FROM ${TARGET_TABLE} LIMIT ${offset}, ${PER_FILE_LINES};" | tail -n +2 >> ${current_file} echo "已生成 ${current_file},写入${PER_FILE_LINES}条记录" done echo "所有CSV文件导出完成"
优化&适配说明
- 如果使用MySQL 5.7及以下版本不支持
--csv参数,可以替换为--batch --raw参数,查询后用sed把制表符替换为逗号即可,注意处理特殊字符转义。 - 如果使用PostgreSQL,把mysql命令替换为psql命令,加上
--csv参数即可输出标准CSV,分页逻辑完全不用改。 - 当表存在连续自增主键时,可以把
LIMIT 偏移量,条数的深分页写法改为主键ID区间查询(比如WHERE id >= 起始ID AND id < 结束ID),能大幅提升大偏移量下的查询速度。 - 如果不需要每个CSV文件都携带表头,删除脚本中写入
CSV_HEADER的行,同时去掉查询语句后的tail -n +2即可。 - 脚本建议直接在数据库所在服务器执行,能避免网络传输带来的性能损耗。
内容的提问来源于stack exchange,提问作者akash chitlu
相关产品推荐
相关产品推荐

