我只是组成一个复杂的更新查询,看起来或多或少像这样:

update table join
    (select y, min(x) as MinX
     from table
     group by y) as t1
    using (y)
set x = x - MinX

这意味着变量x是基于子查询进行更新的,它也处理变量x-但是不能通过运行更新命令修改此x吗?这不是问题吗?我的意思是,在正常编程中,您通常必须明确地处理此问题,即将新值从旧值存储到其他位置,并在作业完成后,用新值替换旧值...但是,SQL数据库将如何执行此操作?

我对单个观察或实验不感兴趣。我想从docs或sql标准获取一个片段,该片段将说明在这种情况下定义的行为是什么。我使用的是MySQL,但对于其他PostgresQL,Oracle等也有效的答案,尤其是对于的SQL标准,尤其是,我们非常感谢。谢谢!

最佳答案

** 已编辑 **

从目标表中选择

13.2.9.8. Subqueries in the FROM Clause:



因此,可以,您可以执行上述查询。

问题

这里确实有两个问题。这是并发的,或者确保没有其他人从我们的脚下更改数据。这是通过锁定来处理的。使用派生表处理新值与旧值的实际修改。

锁定

对于上面的查询,MySQL使用InnoDB首先执行SELECT,然后分别对表中的每一行获取读取(共享)锁。如果在SELECT语句中有WHERE子句,则只有您选择的记录将被锁定,而范围也会导致任何间隙也被锁定。

read lock防止任何其他查询获取写锁定,因此在读取记录时,无法从其他地方更新记录。

然后,MySQL分别获取表中每个记录的写(独占)锁。如果UPDATE语句中有WHERE子句,则仅特定记录将被写锁定,并且,如果WHERE子句选择了一个范围,那么您将具有范围锁定。

具有先前SELECT的读取锁定的任何记录将自动升级为写入锁定。

write lock阻止其他查询获得读或写锁定。

您可以使用Innotop在锁定模式下运行它,启动事务,执行查询(但不要提交查询)来查看此内容,然后您将在Innotop中看到这些锁。另外,您可以使用SHOW ENGINE INNODB STATUS在不使用Innotop的情况下查看详细信息。

死锁

如果同时运行两个实例,则查询很容易出现死锁。如果查询A具有读取锁,则查询B具有读取锁,则查询A必须等待查询B的读取锁释放,然后才能获取写入锁。但是,查询B直到完成后才会释放读锁定,除非它可以获取写锁定,否则它不会完成。查询A和查询B处于僵局,因此陷入僵局。

因此,您可能希望执行显式表锁定,既避免大量的记录锁定(使用内存并影响性能),又避免出现死锁。

另一种方法是对内部SELECT使用SELECT ... FOR UPDATE。首先从对所有行的写锁开始,而不是从读取和升级行开始。

派生表

对于内部SELECT,MySQL创建一个derived temporary table。派生表是存在于由MySQL自动创建的临时表中的数据的实际非索引副本(与您显式创建并可以向其添加索引的临时表相反)。

由于MySQL使用派生表,因此这是您在问题中引用的临时旧值。换句话说,这里没有魔术。 MySQL就像在其他任何地方一样使用临时值来执行此操作。

您可以通过对UPDATE语句执行EXPLAIN来查看派生表(在MySQL 5.6+中受支持)。

08-18 07:20
查看更多