如何将文本文件导入数据库?PostgreSQL导入TSV/CSV失败求助
看起来你在把IMDB的TSV文件导入PostgreSQL时踩了不少坑,我来帮你一步步搞定这个问题——先从通用的文本文件导入思路说起,再聚焦你的具体场景。
一、文本文件导入数据库的通用路子
一般来说,关系型数据库导入文本文件有两种主流方式:
- 数据库原生命令:比如PostgreSQL的
COPY/\copy、MySQL的LOAD DATA INFILE,这类命令效率拉满,特别适合大文件(比如你这个IMDB的数据集)。 - 脚本/工具辅助:用Python(搭配pandas+sqlalchemy)、Java写脚本读文件再插入,或者用ETL工具(比如Apache NiFi),适合需要先做数据清洗的场景。
二、你的PostgreSQL导入问题的具体解决方案
先给你拆解几个可能导致失败的坑点,再给你正确的操作步骤:
坑点1:混淆了服务器端COPY和客户端\copy
你之前用的COPY data FROM '/path/...'是服务器端命令,它是PostgreSQL服务进程(通常跑在postgres用户下)去读文件,所以这个用户得有文件的读取权限。如果你是在本地用psql操作,更推荐用客户端命令\copy——它用的是你当前操作系统用户的权限,不用给postgres额外授权,踩坑概率低很多。
坑点2:没处理压缩文件和空值
你下载的是.tsv.gz压缩包,PostgreSQL 9.3及以上版本能自动识别并解压,不用提前手动解压;另外IMDB的空值用\N表示,导入时得告诉PostgreSQL把这个当成NULL,不然会报错。
坑点3:字段映射不匹配
你的data表结构和IMDB的title.basics.tsv字段顺序、数量不一样,必须明确指定对应关系,不然数据会错位,直接导致导入失败。
正确操作步骤
第一步:确认字段对应关系
先理清楚IMDB文件和你的表的字段对应:
IMDB文件的字段顺序是:tconst, titleType, primaryTitle, originalTitle, isAdult, startYear, endYear, runtimeMinutes, genres
你的data表字段:Title_ID(应该是自增主键吧?), tconst, title(这里应该对应IMDB的titleType?如果不是你得调整), primaryTitle, originalTitle, isAdult, startYear, endYear, runtimeMinutes, genre(对应IMDB的genres)
假设Title_ID是自增类型(比如SERIAL),导入时不用管它,数据库会自动生成。
第二步:用\copy命令导入(推荐)
打开psql客户端,连接到你的数据库,执行这条命令:
\copy data (tconst, title, primaryTitle, originalTitle, isAdult, startYear, endYear, runtimeMinutes, genre) FROM '/home/pi/Downloads/title.basics.tsv.gz' WITH (FORMAT CSV, DELIMITER E'\t', HEADER, NULL '\\N');
给你解释下参数:
(tconst, title, ...):明确指定要导入的字段,对应表和文件的字段(如果你的title字段不是存titleType,记得调整这里的对应关系)FORMAT CSV + DELIMITER E'\t':虽然是TSV,但用CSV格式配合制表符分隔是PostgreSQL处理TSV的标准姿势HEADER:告诉数据库跳过文件第一行的字段名NULL '\\N':把IMDB的\N识别为NULL值
第三步:如果非要用服务器端COPY
如果你必须用服务器端的COPY,得先让postgres用户能读到文件:
# 把文件权限改成postgres用户可读 sudo chown postgres:postgres /home/pi/Downloads/title.basics.tsv.gz sudo chmod 644 /home/pi/Downloads/title.basics.tsv.gz
然后执行这条SQL:
COPY data (tconst, title, primaryTitle, originalTitle, isAdult, startYear, endYear, runtimeMinutes, genre) FROM '/home/pi/Downloads/title.basics.tsv.gz' WITH (FORMAT CSV, DELIMITER E'\t', HEADER, NULL '\\N');
第四步:排查剩余错误
如果还是失败,检查这几点:
- 确认
isAdult字段在文件里是0或1,PostgreSQL的boolean类型能自动识别这些值 startYear、endYear、runtimeMinutes的空值是\N,你的表是text类型,没问题- 如果报类型不匹配,检查表字段类型和文件内容是否兼容,比如可以在导入时用
CAST(isAdult AS boolean)强制转换
内容的提问来源于stack exchange,提问作者MaZzEe

