使用PostgreSQL COPY时出现"错误:上一个预期列之后的额外数据"

dna*_*nak 7 postgresql database-design dynamic-sql sqlbulkcopy

请耐心等待,因为这是我的第一篇文章.

我正在尝试在PostgreSQL-9.2中运行COPY命令,将.txt文件中的制表符分隔表添加到PostgreSQL数据库,例如:

COPY raw_data FROM '/home/Projects/TestData/raw_data.txt' WITH (DELIMITER ' ');
Run Code Online (Sandbox Code Playgroud)

我已经使用SQL命令在数据库中创建了一个名为"raw_data"的空表:

CREATE TABLE raw_data ();
Run Code Online (Sandbox Code Playgroud)

尝试运行该COPY命令时,我不断收到以下错误消息:

ERROR:  extra data after last expected column
CONTEXT:  COPY raw_data, line 1: "  1   2   3   4   5   6   7   8   9   10  11  12  13  14  15  16  17  18  19  20  21  22  23  24  25  26  27  28  29  30  31  32  33  34  35  36  ..."
Run Code Online (Sandbox Code Playgroud)

(这里的数字应该是列标题)

我不确定它是否因为我在创建db表时没有指定表列但是我试图避免手动输入800或列.

对于如何解决这个问题,有任何的建议吗?

以下是.txt文件的示例:

        1   2   3   4   5   6   7   8   9
binary1 1   1   0   1   1   1   1   1   1
binary2 1   0   0   1   0   1   1   0   0
binary3 1   0   1   1   1   0   0   1   0
binary4 1   1   1   1   0   1   0   1   0
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 12

空表不行.

CREATE TABLE raw_data (
  col1 int
, col2 int
  ...
);
Run Code Online (Sandbox Code Playgroud)

您需要与导入数据的结构匹配的表.就像是

COPY raw_data FROM '/home/Projects/TestData/raw_data.txt';
Run Code Online (Sandbox Code Playgroud)

您不需要声明tabDELIMITER,因为它是默认值:

1   2   3   4  -- first row contains "column names"
1   1   0   1  -- tab separated
1   0   0   1
1   0   1   1
Run Code Online (Sandbox Code Playgroud)

你说800列?在10个案例中的9个案例中,这表明您的设计存在问题.通常你不会有那么多列.无论如何,肯定有一些方法可以自动创建CREATE TABLE脚本.

自动化

假设简化了原始数据

CREATE TEMP TABLE tmp_data (raw text);

COPY tmp_data FROM '/home/Projects/TestData/raw_data.txt' WITH (DELIMITER '§');
Run Code Online (Sandbox Code Playgroud)

定义一个不同的DELIMITER(根本不会在导入数据中出现),并导入到具有单个text列的临时登台表:

SELECT 'CREATE TABLE tbl (col' || replace (raw, E'\t', ' bool, col') || ' bool)'
FROM   (SELECT raw FROM tmp_data LIMIT 1) t;
Run Code Online (Sandbox Code Playgroud)

此查询创建CREATE TABLE脚本:

SELECT 'CREATE TABLE tbl('
    ||  string_agg(quote_ident('col' || col), ' bool, ' ORDER  BY ord)
    || ' bool);'
FROM  (SELECT raw FROM tmp_data LIMIT 1) t
     , unnest(string_to_array(t.raw, E'\t')) WITH ORDINALITY c(col, ord);
Run Code Online (Sandbox Code Playgroud)

返回:

CREATE TABLE tbl (col1 bool, col2 bool, col3 bool, col4 bool);
Run Code Online (Sandbox Code Playgroud)

执行(验证有效性后).
然后CREATE TABLE使用此查询的数据:

DO
$$BEGIN
EXECUTE (
   SELECT 'CREATE TABLE tbl (col' || replace(raw, ' ', ' bool, col') || ' bool)'
   FROM  (SELECT raw FROM tmp_data LIMIT 1) t
   );
END$$;
Run Code Online (Sandbox Code Playgroud)

或者,更短/更快INSERT:

INSERT INTO tbl
SELECT (('(' || replace(replace(replace(
                  raw
                , '1',   't')
                , '0',   'f')
                , E'\t', ',')
             || ')')::tbl).*
FROM   (SELECT raw FROM tmp_data OFFSET 1) t;
Run Code Online (Sandbox Code Playgroud)

这会将字符串转换为兼容格式,以将其强制转换为新创建的表类型,然后将其展开translate().

全部完成.

您可以将所有这些放入plpgsql函数中,但是您需要防止SQL注入.我在SO上发布了一些类似的解决方案.尝试搜索.
我将不得不留下四件事来解决......

- > SQLfiddle演示