高效更新 SQL,一条 SQL 语句更新多行,避免循环

Posted

技术标签:

【中文标题】高效更新 SQL,一条 SQL 语句更新多行,避免循环【英文标题】:Efficient Update SQL, updating multiple rows with one SQL statement, Avoiding Loops 【发布时间】:2012-01-09 10:32:54 【问题描述】:

我正在尝试避免在我正在处理的项目中循环访问数据库。不太擅长使用 SQL,我不确定解决此问题的最佳方法。

我正在更新具有多个库存位置/挑选位置的销售过程中的库存级别数据库。

因此,这就是我正在做的。

遍历产品 ID,然后遍历每个产品的拣货地点并随时更新数量,例如:

For Each wProductId In calculatedProds.Keys '' loop through products requested passing values of pick locations
        For i = 0 To locationCount ' split the location value from the string as per above
            Dim thisLocation As Integer = locationID
            Dim thisQty As Integer = qtyPicked                
            Dim sql As String = "UPDATE `stockLevels` SET `stockLevel`=`stockLevel` - '" & thisQty & "' WHERE `stockLocation`='" & thisLocation & "' AND `id`='" & wProductId & "'"
            ' DO DATA ACCESS WITH SQL ABOVE       
        Next
Next

这当然可行,但它会为每个库存位置、每个项目打开一个新的数据库连接。

那么我如何将它整合到一个更新语句中呢?

http://www.karlrixon.co.uk/writing/update-multiple-rows-with-different-values-and-a-single-sql-query/

我认为,该链接使我非常接近我所追求的,但我不能 100% 确定如何动态构建该 SQL 语句以及如何向 CASE 添加两个条件。

我需要构建一个类似的 SQL 语句:

UPDATE stockLevels
SET stockLevel= CASE id
    WHEN '"& wProductId  &"' AND stockLocation='"& thisLocation &"' THEN `stockLevel` - '" & thisQty & "'
    WHEN '"& NEXTwProductId  &"' AND stockLocation='"& NEXTthisLocation &"' THEN `stockLevel` - '" & NEXTthisQty & "'
END

但是我在 CASE 中添加第二个参数的地方是不正确的!

像往常一样,我正在使用 mysql 和 VB.NET,非常感谢任何帮助。

【问题讨论】:

【参考方案1】:

在您的示例中的 set 子句中使用 case 语句并不理想,原因有很多。

没有帮助数据库高效执行查询的 where 子句 大量更新的查询大小变得过大(考虑像这样更新 1000 行) 您正在手动实现联接 - 数据库几乎可以肯定地比您更有效地执行此操作。 调试这样的查询也很困难。

相反,您应该首先衡量一次更新的性能,看看您是否真的需要进行改进。

如果需要提高性能,那么我会建议一种方法,首先将更新批量插入到临时表中。一个合适的表应该有以下列:

wProductID, stockLocation, newStockLevel

可以使用以下 MySQL 语法批量插入更新:

INSERT INTO temp_stock_updates
    (wProductID, stockLocation, newStockLevel)
VALUES
    (?,?,?), (?,?,?), (?,?,?), ...

然后运行一次更新来更新主表。这个查询看起来像这样:

UPDATE stockLevels s
    JOIN temp_stock_updates u USING (wProductID, stockLocation)
SET
    s.stockLevel = u.newStockLevel

【讨论】:

好的,谢谢您的建议。在这种情况下,我不能 100% 确定一次一个方法是不好的,但我之前肯定遇到过类似情况的问题。在这个应用程序中,我们不可能一次处理超过 10-20 行,最多 3/4 个位置,因此可能有 80 个循环......您能否扩展您建议的临时表方法?我不确定那里发生了什么? @JamieHartnoll,我为临时表方法添加了一些额外的查询。 a'r 我认为您可能在这里找到了最好的解决方案,但我接受的解决方案直接回答了这个问题,并且我已经能够立即实施。但是,从长远来看,您的解决方案确实看起来更好。我以前从未使用过临时表,甚至不知道如何制作一个,所以我现在坚持我所知道的!还是谢谢! @a'r 一个问题。临时表是什么意思?普通表创建一次,然后仅用于更新目的或在每次更新时创建临时表,如下所示:www.tutorialspoint.com/mysql/mysql-temporary-tables.htm MySQL 官方文档对临时表有更好的描述。见:dev.mysql.com/doc/refman/5.1/en/create-table.html【参考方案2】:

您的 CASE 表达式在语法上完全不正确。

CASE 表达式有两种,彼此几乎相同,但语法略有不同。

一个有以下形式

CASE expr
  WHEN value1 THEN result1
  WHEN value2 THEN result2
  ...
  ELSE result_else
END

另一个是这样的:

CASE
  WHEN condition1 THEN result1
  WHEN condition2 THEN result2
  ...
  ELSE result_else
END

而您实际上是在尝试混合使用这两种CASE

您可能只需要使用第二个(也称为 search CASE,如果我没记错的话):

...
CASE
  WHEN id = '"& wProductId  &"' AND stockLocation='"& thisLocation &"' THEN ...
  WHEN id = '"& NEXTwProductId  &"' AND stockLocation='"& NEXTthisLocation &"' THEN ...
...

请注意,如果没有匹配项并且CASE 没有ELSE 部分,则结果将为NULL,因此请确保您已涵盖所有情况,否则请使用ELSE 部分,如下所示:

ELSE `stocklevel`

CASE 将评估为正在更新的列的原始值,最终不会对其进行更新。

【讨论】:

哦,我明白了,所以这实际上只是在 case 语句的开头没有指定列名的情况。那么我可以用两个参数运行case 吗?这很简单!我知道我的语法是错误的,但不知道如何改正!谢谢! a'r 答案中的注释是有效的,尽管 SQL 语句可能会变得很长。就我而言,我们可能不会处理超过 20 条产品线,在大多数情况下可能少于 10 条,您认为使用 case 声明是解决这种情况的最佳方式吗? 在不超过 20 行的情况下,CASE 解决方案对我来说似乎足够可行。不过,总的来说,我更喜欢@a'r 的想法。您似乎不太确定将来是否会处理更多产品线,因此,至少,您可能希望牢记a'r 的建议(或者考虑在此时实施) . 谢谢。我会接受这个答案,因为这是我最初想要的,但我认为@a'r 有更好的长期解决方案。不过我从来没有使用过临时表,所以我现在要回避它! @JamieHartnoll:好吧,我是谁来反对你手的坚定和思想的坚定?这是您的决定,我希望您在我面前如此友好地提出的原因将帮助我及时接受我的回答足以被接受以被实际接受的事实。 :) 只是开玩笑,感谢您的反馈和解释。不过,我想知道,如果与您对 a'r 的回答的称赞一致,您是否无法通过支持上述回答来实现您的称赞? :) @JamieHartnoll:(澄清一下:我与作者没有任何关系,只是想如果我们都称赞他们的答案是更好的答案,为什么不让它更明显以利于作者和公众的?)

以上是关于高效更新 SQL,一条 SQL 语句更新多行,避免循环的主要内容,如果未能解决你的问题,请参考以下文章

php - 用多行更新 sql 语句

SQL如何用一条SQL语句更新表行

SQL:如果存在则更新,否则插入...但对于具有不同值的多行

日志系统:一条SQL更新语句是如何执行的

日志系统:一条SQL更新语句是如何执行的

MySQL45讲-2-一条SQL更新语句是如何执行的?