2

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

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

UPDATE table SET contact_info_address_* = null;

如果没有,我知道我可以做很长的路要走,只是想在将来如果我需要为一组不同的专栏重新做这件事来帮助自己。

4

2 回答 2

2

为此,您需要动态 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);
  • 我利用列列表语法UPDATE来缩短代码并简化任务。

  • 我查询系统目录而不是信息模式,因为后者虽然是标准化的并保证可跨主要版本移植,但速度也非常慢,有时也很笨拙。有利有弊,我们在 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%');
  • 为了使函数更实用,它返回注释中描述的信息。更多关于在手册中获取 plpgsql 中的结果状态

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

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

-> SQLfiddle 演示

于 2013-05-02T23:00:17.003 回答
1

抱歉,没有方便的快捷方式。如果你必须经常做这种事情,你可以创建一个函数来动态执行 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();

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

于 2013-05-02T18:11:07.297 回答