2011-07-28 119 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