是否可以使用单个 UPDATE SQL 语句执行多个更新?

Posted

技术标签:

【中文标题】是否可以使用单个 UPDATE SQL 语句执行多个更新?【英文标题】:Is it possible to perform multiple updates with a single UPDATE SQL statement? 【发布时间】:2010-09-29 13:15:23 【问题描述】:

假设我有一个表 tbl,其中包含列 idtitle。 我需要更改标题列的所有值:

    从“a-1”到“a1”, 从“a.1”到“a1”, 从“b-1”到“b1”, 从“b.1”到“b1”。

现在,我正在执行两个 UPDATE 语句:

UPDATE tbl SET title='a1' WHERE title IN ('a-1', 'a.1')
UPDATE tbl SET title='b1' WHERE title IN ('b-1', 'b.1')

这根本不是问题,如果表很小,并且单个语句在不到一秒的时间内完成,您只需要执行几条语句。

您可能是客串了它 - 我有一个巨大的表要处理(一个语句在大约 90 秒内完成),并且我有大量的更新要执行。

那么,是否可以合并更新,使其只扫描表一次?或者,在这种情况下有更好的处理方法。

编辑:请注意,我正在使用的真实数据以及我必须执行的数据更改并不是那么简单 - 字符串更长并且它们不遵循任何模式(它是用户数据,所以不能做任何假设 - 它可以是任何东西)。

【问题讨论】:

因此,从您的 EDIT 注释推断,字符串本身可能不同,但您尝试的更新是否遵循模式?如果是这样,它是什么?如果没有任何东西有模式,那么就没有解决方案,您必须单独编码每个特殊更新 我有一个 正确 值列表,并且我有一个明确指定的 错误 值列表(并且必须将哪个错误值更改为哪个正确的值)。所以是的 - 更新确实有一个模式。简而言之 - 每次更新都会更改一个值,但前提是旧值在指定的值列表中。 【参考方案1】:

您可以使用一个语句和多个 case 语句

update tbl
  set title = 
    case
      when title in ('a-1', 'a.1') then 'a1'
      when title in ('b-1', 'b.1') then 'b1'
      else title
    end

当然,这会导致对每条记录的写入,并且对于索引,这可能是一个问题,因此您可以只过滤掉您想要更改的行:

update tbl
  set title = 
    case
      when title in ('a-1', 'a.1') then 'a1'
      when title in ('b-1', 'b.1') then 'b1'
      else title
    end
where
  title in ('a.1', 'b.1', 'a-1', 'b-1')

这将减少对表的写入次数。

【讨论】:

【参考方案2】:

在更一般的情况下,每个新值可能有数百个映射,您将创建一个包含旧值和新值的单独表,然后在 UPDATE 语句中使用它。在 SQL 的一种方言中:

CREATE TEMP TABLE mapper (old_val CHAR(5) NOT NULL, new_val CHAR(5) NOT NULL);
...multiple inserts into mapper...
INSERT INTO mapper(old_val, new_val) VALUES('a.1', 'a1');
INSERT INTO mapper(old_val, new_val) VALUES('a-1', 'a1');
INSERT INTO mapper(old_val, new_val) VALUES('b.1', 'b1');
INSERT INTO mapper(old_val, new_val) VALUES('b-1', 'b1');
...etcetera...

UPDATE tbl
   SET title = (SELECT new_val FROM mapper WHERE old_val = tbl.title)
   WHERE title IN (SELECT old_val FROM mapper);

两个选择语句都至关重要。第一个是相关子查询(不一定快,但如果映射表有数千行,则比大多数替代查询要快),它将新值从对应于旧值的映射表中拉出。第二个确保仅修改映射表中具有值的那些行;这很重要,否则,对于那些没有映射条目的行,标题将设置为 null(这些是在您开始之前正常的记录)。

对于一些替代方案,CASE 操作是可以的。但是,如果您要执行成百上千或数百万个映射,那么您很可能会超出 DBMS 中 SQL 语句长度的限制。

【讨论】:

这非常非常有趣。我什至从来没有想过这一点。插入映射器仍然很快,更新只会扫描我的表一次,我不需要构造大量查询。 但出于性能原因,最好使用连接而不是相关子查询。 @HLGEM:是的,如果您的 DBMS 支持该表示法。您愿意为您知道的一些 DBMS 提供一种有效的语法吗?如果是这样,请编辑我的答案-我相信您有足够的代表来做到这一点。或者通过电子邮件告诉我 - 查看我的个人资料页面。【参考方案3】:

根据乔纳森的回答。

UPDATE tbl
   SET title = new_val
FROM mapper
WHERE title IN (SELECT old_val FROM mapper)
     AND mapper.old_val = tbl.title;

他的初始版本需要对映射表进行大量读取。

【讨论】:

@MrDenny:我可以将您的材料复制到我的答案中吗?当然要给予信用? 我使用了他的查询,它就像一个魅力!我真的很惊讶——它真的比我想象的要快。非常好。【参考方案4】:

如果转换和您的示例一样简单,您可以通过一些字符串操作来进行更新:

UPDATE tbl 
SET title = left(title, 1) + right(title, 1) 
WHERE title IN ('a-1', 'a.1', 'b-1', 'b.1')

这样的东西对你有用吗?

【讨论】:

不,不幸的是,我处理的真实 I 数据并不像我的示例中那样简单。这对我不起作用。不过还是谢谢。 听起来像 casperOne 使用 CASE WHEN 表达式是要走的路。【参考方案5】:

或者

   Update Table set 
     title = Replace(Replace(title, '.', ''), '-', '')
   Where title Like '[ab][.-]1'

【讨论】:

正如我在马特回答的 cmets 中提到的 - 真实数据库中的数据并不那么简单。 冒着听起来很明显的风险,那么,答案也不会那么简单。真正的问题是什么? 也许,我应该让示例中的标题有所不同,这样它们就没有那么简单了。真正的问题是真正的标题是不遵循任何模式的字符串——它们实际上是用户生成的标题,所以我不能对它们做任何假设。我也编辑了我的问题。

以上是关于是否可以使用单个 UPDATE SQL 语句执行多个更新?的主要内容,如果未能解决你的问题,请参考以下文章

如何判断一条sql(update,delete)语句是否执行成功

Java 程序中执行update返回结果

where条件放在子SQL语句中是否查询速度更快?

9个SQL运维常遇到的问题

单个查询中的多个 SQL 更新语句

SQL:with 查询