何を達成しようとしているかによって異なります。
製品の利用可能な数量を表す単純な数値が必要な場合は、それをフィールドとして製品テーブルに保存します。単一のステートメントで更新することはアトミックです。
UPDATE PRODUCT
SET AMOUNT = AMOUNT + <change>
WHERE PRODUCT_ID = <whatever>
または、次のようなこともできます。
START TRANSACTION;
SELECT AMOUNT
FROM PRODUCT
WHERE PRODUCT_ID = <whatever>
FOR UPDATE;
(Calculate new amount.)
UPDATE PRODUCT
SET AMOUNT = <new amount>
WHERE PRODUCT_ID = <whatever>;
COMMIT;
COMMIT まで行をロックし、それ以外の場合に並行環境で発生する可能性がある異常を防ぐ FOR UPDATE に注意してください。
FOR UPDATE がなければ、通常の ACID 保証だけでは十分ではありません。例えば:
- トランザクションAは現在の AMOUNT を選択します。たとえば、5 とします。
- トランザクションBは現在の AMOUNT を選択しますが、他のトランザクションがまだ変更をコミットしていないため、これはまだ 5 です。
- トランザクションAは金額に 2 を加算し、結果は 7 になります。
- トランザクションBは金額に 3 を追加し、結果は 8 になります。
- トランザクションAは行を 7 に UPDATE し、コミットします。
- トランザクションBは行を 8 に更新し、コミットします。
突然、5 + 2 + 3 = 10 になるはずだったテーブルの AMOUNT が 5 + 3 = 8 になりました。変更の 1 つが失われ、最後にコミットした人が勝ちます。
製品の金額の履歴が必要な場合、または製品の個々の「インスタンス」を追跡する必要がある場合は、はい、1:N の関係で別のテーブルが必要になります。これには、並行環境で独自のロックの問題がある可能性があります。
在庫量は非負の値です。
残念ながら、MySQL は CHECK 制約を強制しませんが、この特定のケースでは、単純にUNSIGNEDデータ型の 1 つを使用できます。