【问题标题】:Update only dynamically chosen columns仅更新动态选择的列
【发布时间】:2015-12-13 17:46:14
【问题描述】:

我创建了一个函数copy_rows_from_table(_tbl regclass),它复制表中的记录并为它们提供一个新的主键值。它返回一个包含成对 old_id => new_id 的 hstore。例如,对于表 books,我的函数将创建两个额外的记录并返回一个 hstore。

books 最初:

  id | title         | price | author_id | publisher_id 
 ----+---------------+-------+-----------+--------------
   1 | The Cyberiad  | 15.00 |    23     |      46
   2 |   The Trial   | 10.00 |    12     |      67

books 评估后copy_rows_from_table('books')

  id | title         | price | author_id | publisher_id 
 ----+---------------+-------+-----------+--------------
   1 | The Cyberiad  | 15.00 |    23     |      46
   2 |   The Trial   | 10.00 |    12     |      67
   3 | The Cyberiad  | 15.00 |    23     |      46
   4 |   The Trial   | 10.00 |    12     |      67

返回的 hstore: "1"=>"3", "2"=>"4"

它工作正常。现在我想创建一个函数,它从几个表中复制记录(以数组形式传递),然后使用返回的 hstore 更新所有外键。例如,在复制 booksauthors 之后,我希望在 books 表中更新 author_id 列。在booksauthorspublishers 上使用我的函数后,如果我有一个包含"1"=>"3", "2"=>"4","23"=>"167","12"=>"98","46"=>"87","67"=>"102" 的hstore,我的函数应该以这种方式更新books 表:

  id | title         | price | author_id | publisher_id 
 ----+---------------+-------+-----------+--------------
   1 | The Cyberiad  | 15.00 |    23     |      46
   2 |   The Trial   | 10.00 |    12     |      67
   3 | The Cyberiad  | 15.00 |    167    |      87
   4 |   The Trial   | 10.00 |    98     |      102

我想出了这样的东西:

CREATE OR REPLACE FUNCTION copy_tables(_tbls regclass[])
RETURNS void AS
$func$
DECLARE
  _tbl regclass;
  _id_pairs hstore;
  _table_id_pairs hstore;
  _row record;
BEGIN
  FOR _tbl IN SELECT _tbls
  LOOP
    EXECUTE format('SELECT copy_rows_from_table(''%1$s'')', _tbl)
    INTO _table_id_pairs;
    SELECT COALESCE(_id_pairs, hstore('')) || COALESCE(_table_id_pairs, hstore('')) INTO _id_pairs;
  END LOOP;

  FOR _tbl IN SELECT _tbls
  LOOP
    FOR _row IN EXECUTE format('SELECT * FROM %1$s WHERE id = ANY(''%2$s''::uuid[])', _tbl, avals(_id_pairs))
    LOOP
      EXECUTE (
        SELECT format('UPDATE %1$s SET (%2$s) = (%3$s) WHERE id = %4$s'
                    , _tbl, string_agg(quote_ident(attname), ', '),
                    string_agg(COALESCE(_id_pairs -> ('_row.' || quote_ident(attname)), '_row.' || quote_ident(attname)), ', '), _row.id)
        FROM   pg_attribute
        WHERE  attrelid = _tbl
        AND    NOT attisdropped
        AND    attnum > 0
        AND    attname LIKE '%_id'
      );
    END LOOP;
  END LOOP;
END
$func$
LANGUAGE plpgsql;

但它并不完全有效。有没有可能按照我解释的方式更新记录?

【问题讨论】:

    标签: sql database postgresql plpgsql


    【解决方案1】:

    我终于在 PLpgSQL 中找到了一种方法。我只是遍历每条记录的每一列。这是我的工作功能:

    CREATE OR REPLACE FUNCTION copy_tables(_tbls regclass[])
    RETURNS void AS
    $func$
    DECLARE
      _id_pairs hstore;
      _table_id_pairs hstore;
      _row record;
      _hs_row record;
    BEGIN
      FOR I IN array_lower(_tbls, 1)..array_upper(_tbls, 1)
      LOOP
        EXECUTE format('SELECT copy_rows_from_table(''%1$s'')', _tbls[I])
        INTO _table_id_pairs;
        SELECT COALESCE(_id_pairs, hstore('')) || COALESCE(_table_id_pairs, hstore('')) INTO _id_pairs;
      END LOOP;
    
      FOR I IN array_lower(_tbls, 1)..array_upper(_tbls, 1)
      LOOP
        FOR _row IN EXECUTE format('SELECT * FROM %1$s WHERE id = ANY(''%2$s''::uuid[])', _tbls[I], avals(_id_pairs))
        LOOP
          FOR _hs_row IN SELECT kv."key", kv."value" FROM each(hstore(_row)) kv
          LOOP
            IF _hs_row."value" = ANY(akeys(_id_pairs)) THEN
              EXECUTE format('UPDATE %1$s SET %2$s = ''%3$s'' WHERE id = ''%4$s''',
                              _tbls[I], _hs_row."key", _id_pairs -> _hs_row."value", _row.id);
            END IF;
          END LOOP;
        END LOOP;
      END LOOP;
    END
    $func$
    LANGUAGE plpgsql;
    

    【讨论】:

      【解决方案2】:

      PLpgSQL 不是动态更新的好工具。可能还有一些其他的可能性,但没有一个是微不足道的。其他方式:

      1. 使用一些更动态的 PL 语言 - PLPythonu、PLPerl
      2. 使用扩展名https://github.com/okbob/pltoolbox - 有record_get_fieldsrecord_set_fields函数

      【讨论】:

        猜你喜欢
        • 2013-01-25
        • 2015-04-11
        • 1970-01-01
        • 2021-08-01
        • 2021-01-25
        • 1970-01-01
        • 1970-01-01
        • 2021-09-28
        • 1970-01-01
        相关资源
        最近更新 更多