2012-08-10 43 views
4

条件我需要完成以下任务:锁SQL表,则执行交易,用于插入上选择值

我需要两行插入到相同的表,并且或者两个被插入,或两者都不是(原子插入两排)。

我做这在一个InnoDB表使用事务,像这样:

$db->beginTransaction(); 

# Using PDO prepared statments, execute the following queries: 
INSERT INTO t1 set uid=42, foo=1 
INSERT INTO t1 set uid=42, foo=2 

$db->commit(); 

不过我也只希望如果有在列具有值“42”表中没有任何行插入这些行。

所以我做的:

$stmt = $db->prepare("SELECT id FROM t1 WHERE uid != ?"); 
$stmt->execute(array(42)); 
if($stmt->fetchColumn() < 0){ 
    # There is no row with uid=42, so 
    # perform insertions here as per above. 
    INSERT INTO t1 set uid=42, foo=1 
    INSERT INTO t1 set uid=42, foo=2 
} 

但是这里有一个竞争条件,因为与UID = 42检查该行之后,并插入新行权前可以插入一排。

我应该如何解决这个问题?

我可以锁定表,然后在表锁中执行InnoDB事务吗?

我可以在事务内部做选择以检查uid = 42的现有行吗?那种族没有条件吗?

+0

看看REPLACE INTO ..而不是INSERT。但是,如果记录已经存在,它将执行更新。 – Waygood 2012-08-10 16:30:44

+2

看到你的表格模式会很有趣。这听起来像你想要做的是强制一个唯一的索引,而没有一个唯一的索引字段本身。虽然我可以看到你试图用同一个uid插入多行,这对我来说并不合适。看起来你的问题可能通过表格标准化和使用唯一索引来解决。 – 2012-08-10 16:31:39

+0

我的想法是重复检查42 - 在你提交之前和之后。 – 2012-08-10 16:32:32

回答

4

您可以使用信号量类型的体系结构。例如,创建一个名为“信号灯”的表格或任何你想调用它的表格。假设表中只有一个字段,并且该字段是唯一的,称为“sem”。现在,运行“INSERT INTO semaphore SET sem = 42”;

好吧,那个INSERT语句是原子的,并且意味着在其他人尝试插入42之后的那一刻,他们会得到一个错误,指出重复键。

然后,在原始表格中执行插入操作。在交易中完成所有这些。在SQL它应该是这样的:

BEGIN TRANSACTION; 
INSERT INTO semaphore SET sem = 42; 
INSERT INTO t1 set uid=42, foo=1; 
INSERT INTO t1 set uid=42, foo=2; 
COMMIT; 
DELETE FROM semaphore WHERE sem = 42; 

你在删除的原因后来有两方面:

  1. 我猜你并不需要将其删除,但让我们去清理数据应我们;)
  2. 您在COMMIT之后删除的原因是您希望在事务完成之前保留对其的锁定锁定。

旁注:信号量通常用于自动递增字段不会执行的情况。然后使用只有一条记录的信号量表来序列化插入并阻止主键。

希望它有帮助:)

+0

感谢您的回答。我愿意这样做,尽管你会认为SQL/InnoDB有一个功能来处理这个问题,而不需要一个新的信号量表。 – Olhovsky 2012-08-10 17:55:18

+0

如果您需要将所有交易数据保存在一张表中,肯定是一个可行的解决方案。 – 2012-08-10 18:03:18

+2

您可以使用应用程序级别锁而不是信号量表 - 请参阅[GET_LOCK](http://dev.mysql.com/doc/refman/5.0/zh-cn/miscellaneous-functions.html#function_getlock) – 2012-08-13 16:18:07