使用PostgreSQL COPY FROM命令导入CSV数据时文本中双引号丢失的问题求助
这个问题我之前帮不少开发者排查过,本质是PostgreSQL的COPY ... CSV模式严格遵循RFC 4180的CSV规范,你的CSV内容格式没符合规范,才导致双引号被解析丢失了。咱们一步步拆解解决方案:
问题根源
PostgreSQL在CSV模式下处理双引号的规则是:
- 如果字段内容包含分隔符(比如你用的逗号)、双引号或者换行符,必须用双引号把整个字段包起来。
- 字段内容里的双引号必须转成两个连续的双引号(
""),导入后PostgreSQL会自动把它还原成单个双引号。
你现在的CSV内容是"Hi, "vignesh". How do you do",这里内部的"vignesh"只用了单个双引号,PostgreSQL会把这个单个双引号当成字段的结束标记,解析时就自动去掉了这些引号,最终导致内容里的双引号丢失。
解决方案
有两种方式可以解决,优先推荐第一种:
1. 修正CSV文件格式(最稳妥)
按照CSV规范调整你的内容,把字段内的单个双引号改成两个,同时确保整个字段用双引号包围:
"Hi, ""vignesh"". How do you do"
这样执行COPY命令后,PostgreSQL会自动把""解析成单个",最终存储的内容就是你想要的Hi, "vignesh". How do you do。
2. 修改COPY命令参数(无法修改CSV时用)
如果你没办法调整CSV文件的格式,可以修改COPY命令的QUOTE选项,指定一个CSV内容里不存在的字符作为字段包围符,这样PostgreSQL就不会把内容里的双引号当成语法标记了。
比如你可以把字段包围符改成单引号(假设你的CSV里没用到单引号),修改代码里的copyQuery部分:
原代码:
copyQuery="COPY "+this.tableName+"("+columns+") FROM STDIN WITH DELIMITER '"+this.delimit+"' CSV HEADER FORCE NULL " + strListString;
修改后:
copyQuery="COPY "+this.tableName+"("+columns+") FROM STDIN WITH DELIMITER '"+this.delimit+"' CSV HEADER QUOTE '''' FORCE NULL " + strListString;
这里QUOTE ''''是指定单引号作为字段包围符(Java字符串里需要用三个单引号转义表示一个单引号)。这样PostgreSQL会把内容里的双引号当成普通文本保留下来。
⚠️ 注意:如果你的CSV内容本身包含单引号,这种方式会引发新的解析问题,所以优先选第一种方法。
额外小提示
顺便提一句,你代码里判断字段类型的部分有个小坑:Java里比较字符串内容要用equals(),而不是==(==比的是对象引用),可能会导致FORCE NULL选项没有被正确添加。把这段代码改一下:
原代码:
if(rsmd.getColumnTypeName(i) == "varchar" || rsmd.getColumnTypeName(i) == "text" || rsmd.getColumnTypeName(i) == "char") {
改成:
String colType = rsmd.getColumnTypeName(i); if("varchar".equals(colType) || "text".equals(colType) || "char".equals(colType)) {
这样能确保正确匹配字段类型,避免之前的空字符串转NULL问题复发。
内容的提问来源于stack exchange,提问作者vigneshwar reddy

