web-dev-qa-db-fra.com

MySQL INSERT IF (instructions personnalisées if)

Tout d'abord, voici le résumé concis de la question:

Est-il possible d'exécuter une instruction INSERT de manière conditionnelle? Quelque chose de semblable à ceci:

IF(expression) INSERT...

Maintenant, je sais que je peux le faire avec une procédure stockée ..__ Ma question est: puis-je le faire dans ma requête?


Maintenant, pourquoi voudrais-je faire cela?

Supposons que nous avons les 2 tables suivantes:

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

Maintenant, supposons qu'une commande de 20 poupées Voodoo (numéro de produit 2) arrive.
Nous vérifions d’abord si la quantité en stock est suffisante:

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

Ensuite, s'il vaut true, nous exécutons une requête INSERT.
Jusqu'ici tout va bien.


Cependant, il existe un problème de concurrence.
Si 2 commandes arrivent au exactement au même moment , elles peuvent lire la quantité en main avant même que l'une d'elles ait passé la commande . dépassant ainsi le qty_on_hand.


Revenons donc à la racine de la question:
Est-il possible d’exécuter une instruction INSERT de manière conditionnelle, afin de pouvoir combiner ces deux requêtes en une seule?

J'ai cherché beaucoup, et le seul type de déclaration INSERT conditionnelle que j'ai pu trouver était ON DUPLICATE KEY, ce qui ne s'applique évidemment pas ici.

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

Si aucune ligne n'est renvoyée à partir de la sélection (car votre condition spéciale est false), aucune insertion ne se produit.

Utilisation de votre schéma depuis question (en supposant que votre colonne id est auto_increment):

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

Cela n'insère aucune ligne s'il n'y a pas assez de stock disponible, sinon cela créera la ligne d'ordre.

Belle idée d'ailleurs!

45
Bohemian

Essayer:

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

Si un produit avec id égal à 2 existe et que qty_on_hand est supérieur ou égal à 20 pour ce produit, une insertion aura lieu avec les valeurs product_id = 2 et qty = 20. Sinon, aucune insertion ne se produira. 

Remarque : Si vos ID de produit sont uniques, vous pouvez ajouter une clause LIMIT à la fin de l'instruction SELECT.

17
Shef

Vous n'êtes pas sûr de la concurrence, vous aurez besoin de lire sur le verrouillage dans mysql, mais cela vous permettra de ne prendre que 20 articles si 20 sont disponibles:

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

Vous pouvez ensuite vérifier le nombre de lignes affectées. Si aucun n'était affecté, vous n'aviez pas assez de stock. Si 1 ligne a été affectée, vous avez effectivement consommé le stock.

2
My Other Me

Vous résolvez probablement le problème de la mauvaise façon. 

Si vous craignez que deux opérations de lecture aient lieu en même temps et que l’on travaille donc avec des données obsolètes, la solution consiste à utiliser des verrous ou des transactions .

Demandez à la requête de faire ceci:

  • table de verrouillage pour la lecture
  • lire le tableau
  • table de mise à jour 
  • déverrouiller
1
Konerak