如何以原子方式更新多行并选择它们?

时间:2017-02-17 21:50:31

标签: mysql sql

我想更新多行:

UPDATE tbl SET x = x + 5 WHERE x < 7;

然后,我想选择与结果集相同的行:

SELECT * FROM tbl WHERE (row fulfilled previous condition);

我该怎么做?

4 个答案:

答案 0 :(得分:0)

这个答案适用于mySQL,但它应该足够普遍适用于其他DBMS。它避免了额外的行,限制或丑陋的黑客攻击,易于扩展,并且在不需要的情况下不会失去性能。

START TRANSACTION;
DROP TEMPORARY TABLE IF EXISTS temp;
CREATE TEMPORARY TABLE temp AS (
    SELECT * FROM tbl WHERE x < 7
);
UPDATE temp SET x = x + 5;
REPLACE INTO tbl SELECT * FROM temp;
SELECT * FROM temp;
COMMIT;

如果在PHP / PDO中使用它,则可能需要先选择正确的结果集。错误HY000表示错误。

$stmt = db -> prepare ($sql);
$stmt->execute ();
for ($i=0; $i<5; $i++)
    $stmt->nextRowset ();
$result = $stmt->fetchAll ();

答案 1 :(得分:0)

postgres版本

UPDATE tbl t1
SET    x = t1.x + 5
FROM  (SELECT * FROM tbl WHERE x < 7 FOR UPDATE) t2
WHERE  t1.x = t2.x
RETURNING t2.*

答案 2 :(得分:0)

在MySQL中如果要在更新行之前读取行,可以在事务中执行锁定读取:

START TRANSACTION;

SELECT ... FROM tbl WHERE x < 7 FOR UPDATE;

这会获取匹配行的锁定,就像您已完成更新一样。因此,在您的交易完成之前,没有其他人可以锁定这些行。

然后,当您解决这些行时,您可以更新这些行 - 但仍然在同一个事务中。

UPDATE tbl SET x = x + 5 WHERE x < 7;

COMMIT;

如果您想先更新行然后阅读它们,您可以按其他顺序执行:

START TRANSACTION;

UPDATE tbl SET x = x + 5 WHERE x < 7;

SELECT ... FROM tbl WHERE x < 7;

COMMIT;

但当然可以选择一组不同的行,因为x已经改变了。在UPDATE之前x> = 2或更大的任何行在UDPATE之后将具有x> = 7,因此不会在后续SELECT的结果中。

所有这些语法都是标准的SQL-99,所以它应该适用于任何兼容的SQL实现。但并非所有数据库都以相同的方式实施标准,因此请参阅您使用的品牌文档。

E.g。 https://dev.mysql.com/doc/refman/5.7/en/innodb-locking-reads.html

据我所知,RETURNINGOUTPUT等其他语法不是标准SQL。

答案 3 :(得分:-1)

对于MS SQL Server,您需要使用OUTPUT命令MSDN

UPDATE tbl SET x = x + 5 
OUTPUT DELETED.*
WHERE x < 7

这将列出所有以前的值