【问题标题】:How to copy into Postgres table from csv with added column?如何从 csv 复制到 Postgres 表并添加列?
【发布时间】:2019-03-21 12:20:35
【问题描述】:

我在Postgres 中有一张表,我想从csv 文件中copy into。我通常是这样做的:

\copy my_table from '/workdir/some_file.txt' with null as 'NULL' delimiter E'|' csv header;

现在的问题是 my_table 有额外的一列我想在 copy 上手动填写,具有相同的值“b”。这是我的表格:

some_file.txt:
col1 | col2 | col3
  0     0      1
  0     1      3

my_table :
xtra_col | col1 | col2 | col3
   a        5      2       5
   a        6      2       5
   a        7      2       5

Desired my_table after copy into:
xtra_col | col1 | col2 | col3
   a        5      2       5
   a        6      2       5
   a        7      2       5
   b        0      0       1
   b        0      1       3

有没有办法在列“xtra_col”的复制语句中提及持久的“b”值。如果没有,我应该如何解决这个问题?

【问题讨论】:

    标签: postgresql copy


    【解决方案1】:

    我通常将文件加载到临时表中,然后从那里插入(或更新)。在这种情况下,

    CREATE TEMP TABLE input (LIKE my_table);
    ALTER TABLE input DROP xtra_col;
    
    \copy input from 'some_file.txt' ...
    
    INSERT INTO my_table
    SELECT 'b', * FROM input;
    

    INSERT 语句看起来很整洁,但只有当您要排除的列位于my_table 的任一端时才能真正实现。在您的(可能是简化的)示例中,xtra_col 位于前面,因此我们可以使用 * 快速附加其余列。

    如果 CSV 文件列的排列方式与 my_table 的差异远不止此,您将需要开始输入列名。

    【讨论】:

      【解决方案2】:

      您可以为xtra_col 设置(临时)默认值:

      ALTER TABLE my_table ALTER COLUMN xtra_col SET DEFAULT 'b';
      COPY my_table (col1, col2, col3) FROM '/workdir/some_file.txt' WITH (FORMAT CSV, DELIMITER '|', NULL 'NULL', HEADER true);
      ALTER TABLE my_table ALTER COLUMN xtra_col DROP DEFAULT;
      

      有没有办法不重复 my_table 中的列?真正的 my_table 有 20 列,我不想调用所有列。

      如果my_table 有很多列并且您希望避免输入所有列名, 您可以像这样动态生成 COPY 命令:

      SELECT format($$COPY my_table(%s) FROM '/workdir/some_file.txt' WITH (FORMAT CSV, DELIMITER '|', NULL 'NULL', HEADER true);$$
                    , string_agg(quote_ident(attname), ','))
      FROM   pg_attribute
      WHERE attrelid = 'my_table'::regclass
          AND attname != 'xtra_col'
          AND attnum > 0
      

      然后您可以复制并粘贴 SQL 来运行它。


      或者,为了完全免提操作,您可以创建一个函数来生成 SQL 并执行它:

      CREATE OR REPLACE FUNCTION test_func(filepath text, xcol text, fillval text)
      RETURNS void
      LANGUAGE plpgsql
      AS $func$
      DECLARE sql text;
      BEGIN
        EXECUTE format($$ALTER TABLE my_table ALTER COLUMN %s SET DEFAULT '%s';$$, xcol, fillval);
      
        SELECT format($$COPY my_table(%s) FROM '%s' WITH (FORMAT CSV, DELIMITER '|', NULL 'NULL', HEADER true);$$
              , string_agg(quote_ident(attname), ','), filepath)
        INTO sql
        FROM   pg_attribute
        WHERE attrelid = 'my_table'::regclass
            AND attname != 'xtra_col'
            AND attnum > 0;
        EXECUTE sql;
      
        EXECUTE format($$ALTER TABLE my_table ALTER COLUMN %s DROP DEFAULT;$$, xcol);
      END;
      $func$;
      
      SELECT test_func('/workdir/some_file.txt', 'xtra_col', 'b');
      

      这是我用来测试上面解决方案的sql:

      DROP TABLE IF EXISTS test;
      CREATE TABLE test (
          xtra_col text
          , col1 int
          , col2 int
          , col3 int
      );
      INSERT INTO test VALUES
      ('a', 5, 2, 5)
      , ('a', 6, 2, 5)
      , ('a', 7, 2, 5);
      

      /tmp/data 的内容是

      col1 | col2 | col3
        0  |   0  |   1
        0  |   1  |   3
      

      然后

      SELECT test_func('/tmp/data', 'xtra_col', 'b');
      SELECT * FROM test;
      

      结果

      +----------+------+------+------+
      | xtra_col | col1 | col2 | col3 |
      +----------+------+------+------+
      | a        |    5 |    2 |    5 |
      | a        |    6 |    2 |    5 |
      | a        |    7 |    2 |    5 |
      | b        |    0 |    0 |    1 |
      | b        |    0 |    1 |    3 |
      +----------+------+------+------+
      (5 rows)
      

      关于pg.dropped 列:

      test_func 调用似乎没有产生pg.dropped 列,至少在上面使用的test 表上:

      unutbu=# SELECT *
          FROM pg_attribute
          WHERE attrelid = 'test'::regclass;
      
      +----------+----------+----------+---------------+--------+--------+----------+-------------+-----------+----------+------------+----------+------------+-----------+-------------+--------------+------------+-------------+--------------+--------+------------+---------------+
      | attrelid | attname  | atttypid | attstattarget | attlen | attnum | attndims | attcacheoff | atttypmod | attbyval | attstorage | attalign | attnotnull | atthasdef | attidentity | attisdropped | attislocal | attinhcount | attcollation | attacl | attoptions | attfdwoptions |
      +----------+----------+----------+---------------+--------+--------+----------+-------------+-----------+----------+------------+----------+------------+-----------+-------------+--------------+------------+-------------+--------------+--------+------------+---------------+
      |    53393 | tableoid |       26 |             0 |      4 |     -7 |        0 |          -1 |        -1 | t        | p          | i        | t          | f         |             | f            | t          |           0 |            0 |        |            |               |
      |    53393 | cmax     |       29 |             0 |      4 |     -6 |        0 |          -1 |        -1 | t        | p          | i        | t          | f         |             | f            | t          |           0 |            0 |        |            |               |
      |    53393 | xmax     |       28 |             0 |      4 |     -5 |        0 |          -1 |        -1 | t        | p          | i        | t          | f         |             | f            | t          |           0 |            0 |        |            |               |
      |    53393 | cmin     |       29 |             0 |      4 |     -4 |        0 |          -1 |        -1 | t        | p          | i        | t          | f         |             | f            | t          |           0 |            0 |        |            |               |
      |    53393 | xmin     |       28 |             0 |      4 |     -3 |        0 |          -1 |        -1 | t        | p          | i        | t          | f         |             | f            | t          |           0 |            0 |        |            |               |
      |    53393 | ctid     |       27 |             0 |      6 |     -1 |        0 |          -1 |        -1 | f        | p          | s        | t          | f         |             | f            | t          |           0 |            0 |        |            |               |
      |    53393 | xtra_col |       25 |            -1 |     -1 |      1 |        0 |          -1 |        -1 | f        | x          | i        | f          | f         |             | f            | t          |           0 |          100 |        |            |               |
      |    53393 | col1     |       23 |            -1 |      4 |      2 |        0 |          -1 |        -1 | t        | p          | i        | f          | f         |             | f            | t          |           0 |            0 |        |            |               |
      |    53393 | col2     |       23 |            -1 |      4 |      3 |        0 |          -1 |        -1 | t        | p          | i        | f          | f         |             | f            | t          |           0 |            0 |        |            |               |
      |    53393 | col3     |       23 |            -1 |      4 |      4 |        0 |          -1 |        -1 | t        | p          | i        | f          | f         |             | f            | t          |           0 |            0 |        |            |               |
      +----------+----------+----------+---------------+--------+--------+----------+-------------+-----------+----------+------------+----------+------------+-----------+-------------+--------------+------------+-------------+--------------+--------+------------+---------------+
      (10 rows)
      

      据我所知,删除一列时,pg.dropped 列是 a natural result of how PostgreSQL works。所以不需要修复。 attname 包含 pg.dropped 的行也有一个否定的 attnum

      这就是为什么在test_func 中使用attnum > 0 来从生成的列名列表中删除这些行。

      我对 Postgresql 的经验有限,所以我可能错了。如果你能生成一个例子,它生成一个pg.dropped“列”和正面attnum,我非常希望看到它。

      【讨论】:

      • 看起来很有趣,我得试试看。一件小事,有没有办法不重复 my_table 中的列?真正的 my_table 有 20 列,我不想调用所有列
      • 你的函数有问题,因为它用 '...pg.dropped27...' 替换了 'xtra_col'。看起来它不是用任何东西替换列,而是用数字替换它。有什么想法可以解决这个问题吗?
      • AFAIK, pg.dropped "columns" 在从表中删除列并且是 natural part of how PostgreSQL operates 时发生,因此它们不应该引起任何问题。我相信当pg_attribute.attname 包含pg.droppedpg_attribute.attnum 应该始终为负数,因此可以使用条件attnum > 0 处理这些行。此外,由于test_func 不会删除任何列,我认为它不会生成pg.dropped 列。
      • 我添加了代码,显示了我如何测试test_func 以及我看到的结果——没有pg.dropped 列。如果您可以展示test_func 生成pg.dropped 列的设置,我非常希望看到它。
      • 是的,我不知道问题是什么,无论如何,我已经添加了AND NOT attname LIKE '%.....% 并且似乎解决了它。但是我现在遇到的另一个错误是无法打开文件“/some_filepath”,尽管文件存在。你知道是什么原因造成的吗?
      猜你喜欢
      • 2017-10-30
      • 1970-01-01
      • 1970-01-01
      • 2018-03-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-09-23
      相关资源
      最近更新 更多