在PostgreSQL中运行N个独立列更新的最佳方法是什么?在SQL规范中执行此操作的最佳方法是什么?

时间:2010-02-04 20:55:15

标签: sql postgresql sql-update

我正在寻找一种更有效的方法来在同一个表上运行许多列更新,如下所示:

UPDATE TABLE table
SET col = regexp_replace( col, 'foo', 'bar' )
WHERE regexp_match( col, 'foo' );

这样foobar将是40种不同的正则表达式替换的组合。我怀疑甚至25%的数据集都需要更新,但我想知道的是可以在SQL中干净地实现以下内容。

  • 单次通过更新
  • 正则表达式的单个匹配,触发单个替换
  • 运行所有可能的regexp_replaces(如果只有一个匹配
  • 如果只需要更新
  • ,则更新所有列
  • 如果没有列更改则更新行

我也很好奇,我在MySQL中知道(跟我一起)

UPDATE foo SET bar = 'baz'

具有隐式WHERE bar != 'baz'子句

然而,在PostgreSQL中,我知道这不存在:如果目标列未更新,我知道如果我知道如何跳过单行更新,我至少可以回答我的一个问题。

这样的东西
UPDATE TABLE table
SET col = *temp_var* = regexp_replace( col, 'foo', 'bar' )
WHERE col != *temp_var*

2 个答案:

答案 0 :(得分:4)

在代码中执行。打开游标,然后:抓取一行,通过40个正则表达式运行它,如果它发生了变化,请将其保存回来。重复,直到光标不再为您提供任何行。

无论你是这样做还是想出了神奇的SQL表达式,它仍然是整个表的行扫描,但代码会更简单。

实验结果

为了回应批评,我做了一个实验。我将文档文件中的10,000行插入到包含串行主键和varchar列的表中。然后我测试了两种方法来进行更新。方法1:

in a transaction:
  opened up a cursor (select for update)
  while reading 100 rows from the cursor returns any rows:
    for each row:
      for each regular expression:
        do the gsub on the text column
      update the row

使用本地连接的数据库需要1.16秒。

然后是“大替代”,一个单一的超级正则表达式更新:

  

更新foo set t =   REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE( REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(REGEXP_REPLACE(T,   E'\ bcommit \ b',E'COMMIT'),   E '\ b9acf10762b5f3d3b1b33ea07792a936a25e45010 \ B',   E'9ACF10762B5F3D3B1B33EA07792A936A25E45010' ),   E'\ bAuthor:\ b',E'AUTHOR:'),   E'\ bCarl \ b',E'CARL'),E'\ bWorth \ b',   E'WORTH'),E'\ b \ b',   E''),E'\ bDate:\ b',   E'DATE:'),E'\ bMon \ b',E'MON'),   E'\ bOct \ b',E'OCT'),E'\ b26 \ b',   E'26'),E'\ b04:53:13 \ b',E'04:53:13'),   E'\ b2009 \ b',E'2009'),E'\ b-0700 \ b',   E'-0700'),E'\ bUpdate \ b',E'UPDATE'),   E'\ bversion \ b',E'VERSION'),   E'\ bto \ b',E'TO'),E'\ b2.9.1 \ b',   E'2.9.1'),E'\ bcommit \ b',E'COMMIT'),   E '\ b61c89e56f361fa860f18985137d6bf53f48c16ac \ B',   E'61C89E56F361FA860F18985137D6BF53F48C16AC“),   E'\ bAuthor:\ b',E'AUTHOR:'),   E'\ bCarl \ b',E'CARL'),E'\ bWorth \ b',   E'WORTH'),E'\ b \ b',   E''),E'\ bDate:\ b',   E'DATE:'),E'\ bMon \ b',E'MON'),   E'\ bOct \ b',E'OCT'),E'\ b26 \ b',   E'26'),E'\ b04:51:58 \ b',E'04:51:58'),   E'\ b2009 \ b',E'2009'),E'\ b-0700 \ b',   E'-0700'),E'\ bNEWS:\ b',E'NEWS:'),   E'\ bAdd \ b',E'ADD'),E'\ bnotes \ b',   E'NOTES'),E'\ bfor \ b',E'FOR'),   E'\ bthe \ b',E'THE'),E'\ b2.9.1 \ b',   E'2.9.1'),E'\ brelease。\ b',   E'RELEASE。'),E'\ b谢谢\ b',   E'THANKS'),E'\ bto \ b',E'TO'),   E'\ beveryone \ b',E'EVERYONE'),   E'\ bfor \ b',E'FOR')

mega-regex更新需要0.94秒才能更新。

在0.94秒与1.16相比,大型正则表达式更新确实更快,在代码中执行它的时间占81%。然而,它并不快得多。那些上帝,看看那个更新声明。你想写那个,或者当Postgres抱怨你在某个地方放弃了一个括号时,你想弄清楚出了什么问题吗?

<强>代码

使用的代码是:

  def stupid_regex_replace
    sql = Select.new
    sql.select('id')
    sql.select('t')
    sql.for_update
    sql.from(TABLE_NAME)
    Cursor.new('foo', sql, {}, @db) do |cursor|
      until (rows = cursor.fetch(100)).empty?
        for row in rows
          for regex, replacement in regexes
            row['t'] = row['t'].gsub(regex, replacement)
          end
        end
        sql = Update.new(TABLE_NAME, @db)
        sql.set('t', row['t'])
        sql.where(['id = %s', row['id']])
        sql.exec
      end
    end
  end

我通过从文件中取词来动态生成正则表达式;对于每个单词“foo”,其正则表达式为“\ bfoo \ b”,其替换字符串为“FOO”(单词uppercased)。我使用文件中的单词来确保替换确实发生。我让测试程序吐出了正则表达式,所以你可以看到它们。每对都是一个正则表达式和相应的替换字符串:

[[/\bcommit\b/, "COMMIT"],
 [/\b9acf10762b5f3d3b1b33ea07792a936a25e45010\b/,
  "9ACF10762B5F3D3B1B33EA07792A936A25E45010"],
 [/\bAuthor:\b/, "AUTHOR:"],
 [/\bCarl\b/, "CARL"],
 [/\bWorth\b/, "WORTH"],
 [/\b<cworth@cworth.org>\b/, "<CWORTH@CWORTH.ORG>"],
 [/\bDate:\b/, "DATE:"],
 [/\bMon\b/, "MON"],
 [/\bOct\b/, "OCT"],
 [/\b26\b/, "26"],
 [/\b04:53:13\b/, "04:53:13"],
 [/\b2009\b/, "2009"],
 [/\b-0700\b/, "-0700"],
 [/\bUpdate\b/, "UPDATE"],
 [/\bversion\b/, "VERSION"],
 [/\bto\b/, "TO"],
 [/\b2.9.1\b/, "2.9.1"],
 [/\bcommit\b/, "COMMIT"],
 [/\b61c89e56f361fa860f18985137d6bf53f48c16ac\b/,
  "61C89E56F361FA860F18985137D6BF53F48C16AC"],
 [/\bAuthor:\b/, "AUTHOR:"],
 [/\bCarl\b/, "CARL"],
 [/\bWorth\b/, "WORTH"],
 [/\b<cworth@cworth.org>\b/, "<CWORTH@CWORTH.ORG>"],
 [/\bDate:\b/, "DATE:"],
 [/\bMon\b/, "MON"],
 [/\bOct\b/, "OCT"],
 [/\b26\b/, "26"],
 [/\b04:51:58\b/, "04:51:58"],
 [/\b2009\b/, "2009"],
 [/\b-0700\b/, "-0700"],
 [/\bNEWS:\b/, "NEWS:"],
 [/\bAdd\b/, "ADD"],
 [/\bnotes\b/, "NOTES"],
 [/\bfor\b/, "FOR"],
 [/\bthe\b/, "THE"],
 [/\b2.9.1\b/, "2.9.1"],
 [/\brelease.\b/, "RELEASE."],
 [/\bThanks\b/, "THANKS"],
 [/\bto\b/, "TO"],
 [/\beveryone\b/, "EVERYONE"],
 [/\bfor\b/, "FOR"]]

如果这是一个手工生成的正则表达式列表,而不是自动生成的,我的问题仍然适用:您宁愿创建或维护哪个?

答案 1 :(得分:2)

有关跳过更新,请查看suppress_redundant_updates - 请参阅http://www.postgresql.org/docs/8.4/static/functions-trigger.html

这不一定是胜利 - 但很可能在你的情况下。

或许您可以将隐式检查添加为显式检查?