【问题标题】:Copy records with dynamic column names复制具有动态列名的记录
【发布时间】:2016-06-19 12:22:20
【问题描述】:

我在 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

这里有什么问题?

【问题讨论】:

    标签: arrays postgresql variables plpgsql dynamic-sql


    【解决方案1】:

    您似乎认为 SQL 中的SELECT INTO 会分配一个变量。但事实并非如此。

    它会创建一个新的,不鼓励在 Postgres 中使用它。请改用上级CREATE TABLE AS。不是最不重要的,因为 plpgsql 中SELECT INTO 的含义是不同的

    关于 SQL 变量:

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

    <strike>SELECT f_insert_these_columns(VARIADIC cols2);</strike>

    这可行:

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

    或清洁工:

    SELECT f_insert_these_columns(VARIADIC array)  -- "array" being the unfortunate column name
    FROM   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)
      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$;
    

    呼叫:

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

    结果:

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

    要点

    【讨论】:

    • 它有效:SELECT f_insert_these_columns(VARIADIC (TABLE cols2 LIMIT 1));谢谢帮助
    • @PiotrOlkiewicz:考虑一下我添加的替代解决方案。
    • 您的功能完美运行。另外我不需要创建临时对象。这是一个很好的解决方案。感谢您的帮助
    猜你喜欢
    • 2022-01-18
    • 2017-09-21
    • 2016-08-03
    • 1970-01-01
    • 1970-01-01
    • 2015-11-26
    • 1970-01-01
    • 2019-08-08
    • 2020-08-04
    相关资源
    最近更新 更多