web-dev-qa-db-ja.com

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');

次に、trueと評価された場合、INSERTクエリを実行します。
ここまでは順調ですね。


ただし、並行性に問題があります。
2つの注文が正確に同じ時間で到着した場合、いずれかが注文を入力する前に両方とも手持数量を読み取る可能性があります。その後、両方とも注文し、qty_on_hand


それでは、質問の根本に戻りましょう。
INSERTステートメントを条件付きで実行して、これらの両方のクエリを1つにまとめることは可能ですか?

私は多くの場所を検索しましたが、見つけられる条件付きINSERTステートメントのタイプはON DUPLICATE KEY、これは明らかにここには適用されません。

31
Joseph Silber
INSERT INTO TABLE
SELECT value_for_column1, value_for_column2, ...
FROM wherever
WHERE your_special_condition

Selectから行が返されない場合(特別な条件がfalseであるため)、挿入は発生しません。

質問からスキーマを使用する(id列がauto_increment):

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

在庫が十分にない場合、行は挿入されません。そうでない場合、注文行が作成されます。

いいアイデアね

51
Bohemian

試してください:

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

id2に等しい製品が存在し、この製品のqty_on_hand20以上である場合、値product_id = 2およびqty = 20で挿入が発生します。それ以外の場合、挿入は行われません。

:製品IDが一意の注である場合、LIMITステートメントの最後にSELECT句を追加することができます。

18
Shef

並行性については定かではありませんが、mysqlのロックについて調べる必要がありますが、これにより、20個のアイテムが利用可能な場合、20個のアイテムのみを取得することができます。

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

次に、影響を受けた行数を確認できます。何も影響を受けなかった場合、十分な在庫がありませんでした。 1つの行が影響を受けた場合、在庫を効果的に消費しています。

2
My Other Me

あなたはおそらく問題を間違った方法で解決しているでしょう。

2つの読み取り操作が同時に発生するため、1つが古いデータを処理することを恐れている場合、解決策はロックまたはトランザクションを使用です。

クエリでこれを実行します。

  • 読み取り用のロックテーブル
  • テーブルを読む
  • テーブルを更新する
  • リリースロック
1
Konerak