2011-07-28 86 views
21

首先,这里的问题的简明摘要:MySQL的INSERT IF(自定义if语句)

是否可以有条件地运行INSERT声明? 一种近乎此:

IF(expression) INSERT... 

现在,我知道我可以用一个存储过程做到这一点。 我的问题是:我可以在我的查询中做到这一点?


现在,我为什么要这么做?

假设我们有以下2个表:

products: id, qty_on_hand 
orders: id, product_id, qty 

现在,让我们说20个巫毒娃娃(产品ID 2)的订单进来
我们首先检查是否有足够数量的手:

SELECT IF(
    (SELECT SUM(qty) FROM orders WHERE product_id = 2 ) + 20 
    <= 
    (SELECT qty_on_hand FROM products WHERE id = 2) 
, 'true', 'false'); 

然后,如果计算结果为真,我们运行一个INSERT查询。
到目前为止这么好。


但是,并发性存在问题。
如果两个订单在完全相同的时间进来,它们可能都会在其中任何一个订单进入订单之前读取数量。 然后他们会下订单,因此超过了qty_on_hand


所以,回到问题的根源:
是否可以有条件地运行INSERT声明,这样我们就可以这两个查询合并成一个?

我搜索了很多,而且我能找到的条件INSERT声明的唯一类型是ON DUPLICATE KEY,这在这里显然不适用。

回答

38
INSERT INTO TABLE 
SELECT value_for_column1, value_for_column2, ... 
FROM wherever 
WHERE your_special_condition 

如果从select中返回没有行(因为您的特殊条件为false),则不会发生插入操作。

使用您的架构从问题(假设你的idauto_increment):

insert into orders (product_id, qty) 
select 2, 20 
where (SELECT qty_on_hand FROM products WHERE id = 2) > 20; 

这将插入任何行,如果有手头没有足够的库存,否则会造成该命令行。

好想法btw!

+0

@Bohemian:不需要两个'SELECT'语句。一个就足够了。看看我的答案。 :) – Shef

+0

是的,但我试图匹配我的* general *模式。我喜欢你的答案,但... +1 – Bohemian

+0

@约瑟夫西尔伯:做什么?那里的减法在哪里?减法与标准化有什么关系? :D你是否明白上面的查询是由两个'SELECT'语句组成的,与我的相比,它运行一个? (没有任何反对你的答案,或者你,波西米亚人)。 – Shef

2

您可能是以错误的方式解决问题。

如果您担心两个读取操作会同时发生,因此一个会使用陈旧的数据,解决方法是使用锁或的事务处理

有查询做到这一点:

  • 锁表中读取
  • 读出表
  • 更新表
  • 释放锁
+0

我不太确定锁定是最好的解决方案。它可能会导致严重的性能问题。 –

2

不确定并发,你需要阅读关于在MySQL锁定,但这将让你确信,只有20个项目,如果有20个项目可用:

update products 
set qty_on_hand = qty_on_hand - 20 
where qty_on_hand >= 20 
and id=2 

然后,您可以检查有多少行受到影响。如果没有影响,你没有足够的股票。如果1行受到影响,您已经有效地消耗了股票。

16

尝试:

INSERT INTO orders(product_id, qty) 
SELECT 2, 20 FROM products WHERE id = 2 AND qty_on_hand >= 20 

如果具有id等于2产物存在,并且是qty_on_hand大于或等于20该产品,然后插入将与值product_id = 2,和qty = 20发生。否则,不会发生插入。

:如果您的产品ID是唯一的音符,你可能想在SELECT声明的末尾添加一个LIMIT条款。

+0

你会如何否定这一点? – user2340939

+0

一种方法是在结尾添加以下内容:HAVING COUNT(*)= 0。 – user2340939