Copy records with dynamic column names

后端 未结 1 1387
野性不改
野性不改 2021-01-24 22:15

I have two tables with different columns in PostgreSQL 9.3:

CREATE TABLE person1(
   NAME           TEXT    NOT NULL,
   AGE            INT     NOT NULL
);

CREA         


        
相关标签:
1条回答
  • 2021-01-24 22:39

    You seem to assume that SELECT INTO in SQL would assign a variable. But that is not so.

    It creates a new table and its use is discouraged in Postgres. Use the superior CREATE TABLE AS instead. Not least, because the meaning of SELECT INTO inside plpgsql is different:

    • Combine two tables into a new one so that select rows from the other one are ignored

    Concerning SQL variables:

    • User defined variables in PostgreSQL

    Hence you cannot call the function like this:

    SELECT f_insert_these_columns(VARIADIC cols2);

    This would work:

    SELECT f_insert_these_columns(VARIADIC (TABLE cols2 LIMIT 1));
    

    Or cleaner:

    SELECT f_insert_these_columns(VARIADIC array)  -- "array" being the unfortunate column name
    FROM   cols2
    LIMIT  1;
    

    About the short TABLE syntax:

    • Is there a shortcut for SELECT * FROM?

    Better solution

    To copy all rows with columns sharing the same name between two tables:

    CREATE OR REPLACE FUNCTION f_copy_rows_with_shared_cols(
        IN  _tbl1 regclass
      , IN  _tbl2 regclass
      , OUT rows int
      , OUT columns text)
      LANGUAGE plpgsql AS
    $func$
    BEGIN
       SELECT INTO columns         -- proper use of SELECT INTO!
              string_agg(quote_ident(attname), ', ')
       FROM  (
          SELECT attname
          FROM   pg_attribute
          WHERE  attrelid IN (_tbl1, _tbl2)
          AND    NOT attisdropped  -- no dropped (dead) columns
          AND    attnum > 0        -- no system columns
          GROUP  BY 1
          HAVING count(*) = 2
          ) sub;
    
       EXECUTE format('INSERT INTO %1$s(%2$s) SELECT %2$s FROM %3$s'
                      , _tbl1, columns, _tbl2);
    
       GET DIAGNOSTICS rows = ROW_COUNT;  -- return number of rows copied
    END
    $func$;
    

    Call:

    SELECT * FROM f_copy_rows_with_shared_cols('public.person2', 'public.person1');
    

    Result:

    rows | columns
    -----+---------
    3    | name, age
    

    Major points

    • Note the proper use of SELECT INTO for assignment inside plpgsql.

    • Note the use of the data type regclass. This allows to use schema-qualified table names (optionally) and defends against SQL injection attempts:

    • Table name as a PostgreSQL function parameter

    • About GET DIAGNOSTICS:

      • Count rows affected by DELETE
    • About OUT parameters:

      • Returning from a function with OUT parameter
    • The manual about format().

    • Information schema vs. system catalogs.

    0 讨论(0)
提交回复
热议问题