使用动态列名复制记录

时间:2016-06-19 12:22:20

标签: arrays postgresql variables plpgsql dynamic-sql

我在PostgreSQL 9.3中有两个不同列的表:

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

CREATE TABLE person2(
   NAME           TEXT    NOT NULL,
   AGE            INT     NOT NULL,
   ADDRESS        CHAR(50),
   SALARY         REAL
);

INSERT INTO person2 (Name,    Age, ADDRESS, SALARY)
             VALUES ('Piotr', 20, 'London', 80);

我想将记录从person2复制到person1,但是列名可以在程序中更改,因此我想在程序中选择联合列名。所以我创建了一个包含列名称交集的数组。接下来我使用函数insert into .... select,但是当我按名称将数组变量传递给函数时,我收到错误。像这样:

select column_name into name1 from information_schema.columns where table_name = 'person1';
select column_name into name2 from information_schema.columns where table_name = 'person2';
select * into cols from ( select * from name1 intersect select * from name2) as tmp;
-- Create array with name of columns 
select array (select column_name::text from cols) into cols2;

CREATE OR REPLACE FUNCTION f_insert_these_columns(VARIADIC _cols text[])
  RETURNS void AS
$func$
BEGIN
   EXECUTE (
      SELECT 'INSERT INTO person1 SELECT '
          || string_agg(quote_ident(col), ', ')
          || ' FROM person2'
      FROM   unnest(_cols) col
      );
END
$func$  LANGUAGE plpgsql;


select * from cols2;

  array    
------------
 {name,age}
(1 row)

SELECT f_insert_these_columns(VARIADIC cols2);
ERROR:  column "cols2" does not exist

这里有什么问题?

1 个答案:

答案 0 :(得分:2)

答案

您似乎假设SQL中的SELECT INTO会分配变量。但事实并非如此。

它创建了一个新的,并且在Postgres中不鼓励使用它。请改用上级CREATE TABLE AS。尤其是,因为plpgsql中SELECT INTO的含义是不同

我之前已经提出了关于SQL变量的相关问题:

因此你不能这样调用这个函数:

SELECT f_insert_these_columns(VARIADIC cols2);

这样可行:

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

关于短TABLE语法:

更好的解决方案

要复制两个表之间具有相同名称的列的所有行:

CREATE OR REPLACE FUNCTION f_copy_rows_with_shared_cols(
    IN  _tbl1 regclass
  , IN  _tbl2 regclass
  , OUT rows int
  , OUT columns text) 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$  LANGUAGE plpgsql;

呼叫:

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

结果:

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

重点