【问题标题】:Update multiple columns that start with a specific string更新以特定字符串开头的多个列
【发布时间】:2013-04-26 23:40:29
【问题描述】:

我正在尝试更新数据库中的一堆列以测试某个功能。我有一个用 hibernate 构建的表,因此为嵌入式实体创建的所有列都以相同的名称开头。 IE。 contact_info_address_street1、contact_info_address_street2等

我想弄清楚是否有办法做一些事情来影响:

UPDATE table SET contact_info_address_* = null;

如果没有,我知道我可以做很长的路要走,只是寻找一种方法来帮助自己,如果我需要为一组不同的列重新做一遍。

【问题讨论】:

  • 您需要阅读元数据。检查表pg_%。

标签: sql postgresql sql-update plpgsql dynamic-sql


【解决方案1】:

为此,您需要动态 SQL。所以你必须准备好应对可能的 SQL 注入。

基本查询

生成所需 DML 命令的基本查询如下所示:

SELECT format('UPDATE tbl SET (%s) = (%s)'
               ,string_agg (quote_ident(attname), ', ')
               ,string_agg ('NULL', ', ')
             )
FROM   pg_attribute
WHERE  attrelid = 'tbl'::regclass
AND    NOT attisdropped 
AND    attnum > 0
AND    attname ~~ 'foo_%';

返回:

UPDATE tbl SET (foo_a, foo_b, foo_c) = (NULL, NULL, NULL);
  • 我使用"column-list syntax" of UPDATE 来缩短代码并简化任务。

  • 我查询system catalogs 而不是information schema,因为后者虽然是标准化的并保证可跨主要版本移植,但速度也非常慢,有时也很笨拙。有利有弊,我们在 SO 上已经讨论过好几次了。搜索关键字以获取更多信息。

  • quote_ident() 用于列名可防止 SQL 注入,并且对于 任何 非标准列名也是必需的。

  • 您忘记提及您的 Postgres 版本。聚合函数string_agg() 需要 9.0+。

使用 PL/pgSQL 函数实现完全自动化

CREATE OR REPLACE FUNCTION f_update_cols(_tbl regclass, _col_pattern text
                                        , OUT row_ct int, OUT col_ct int)
  RETURNS record AS
$func$
DECLARE
   _sql text;
BEGIN
   SELECT format('UPDATE tbl SET (%s) = (%s)'
                 ,string_agg (quote_ident(attname), ', ')
                 ,string_agg ('NULL', ', ')
                )
         ,count(*)::int
   INTO   _sql, col_ct
   FROM   pg_attribute
   WHERE  attrelid = _tbl
   AND    NOT attisdropped         -- no dropped columns
   AND    attnum > 0               -- no system columns
   AND    attname ~~ _col_pattern; -- only columns matching pattern

   -- RAISE NOTICE '%', _sql;      -- output generated SQL for debugging
   EXECUTE _sql;

   GET DIAGNOSTICS row_ct = ROW_COUNT;
END
$func$  LANGUAGE plpgsql;

COMMENT ON FUNCTION f_update_cols(regclass, text)
 IS 'Updates all columns of table _tbl ($1)
that match _col_pattern ($2) in a LIKE expression.
Returns the count of columns (col_ct) and rows (row_ct) affected.';

呼叫:

SELECT * FROM f_update_cols('myschema.tbl', 'foo%');
  • 为了使函数更实用,它返回注释中描述的信息。更多关于 obtaining the result status 在手册中的 plpgsql 中。

  • 我使用变量_sql 来保存查询字符串,因此我可以收集在同一查询中找到的列数 (col_ct)。

  • 对象标识符类型regclass 也是自动避免表名的 SQL 注入(并清理非标准名称)的最有效方法。您可以使用模式限定的表名来避免歧义。如果您的数据库中有多个模式,我建议您这样做!此相关问题的更多详细信息:
    Table name as a PostgreSQL function parameter

-> SQLfiddle demo.

【讨论】:

    【解决方案2】:

    抱歉,没有方便的快捷方式。如果你必须经常做这种事情,你可以创建一个函数来动态执行 sql 并实现你的目标。

    CREATE OR REPLACE FUNCTION reset_cols() RETURNS boolean AS $$ BEGIN 
        EXECUTE (select 'UPDATE table SET ' 
                      || array_to_string(array(
                                  select column_name::text 
                                  from information_schema.columns 
                                  where table_name = 'table' 
                                  and column_name::text like 'contact_info_address_%'
                         ),' = NULL,') 
                      || ' = NULL'); 
        RETURN true; 
     END; $$ LANGUAGE plpgsql;
    
    -- run the function
    SELECT reset_cols();
    

    虽然不是很好。一个更好的函数是接受表名和列前缀作为参数。我将把它作为练习留给读者:)

    【讨论】:

    • 我喜欢这个答案,如果没有更好的结果,我会将其标记为已接受。 (感谢您的信息=))
    • 小心,列名需要转义。这允许 SQL 注入。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-04-08
    • 2017-08-08
    • 1970-01-01
    • 2017-09-20
    • 1970-01-01
    • 2020-05-11
    • 1970-01-01
    相关资源
    最近更新 更多