web-dev-qa-db-de.com

MySQL INSERT IF (benutzerdefinierte if-Anweisungen)

Zunächst die knappe Zusammenfassung der Frage:

Ist es möglich, eine INSERT-Anweisung bedingt auszuführen? Etwas ähnliches:

IF(expression) INSERT...

Jetzt weiß ich, dass ich dies mit einer gespeicherten Prozedur erledigen kann .. Meine Frage ist: Kann ich das in meiner Abfrage tun?


Warum sollte ich das machen wollen?

Nehmen wir an, wir haben die folgenden zwei Tabellen:

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

Nehmen wir jetzt an, eine Bestellung für 20 Voodoo-Puppen (Produkt-ID 2) kommt.
Wir prüfen zunächst, ob genügend Mengen vorhanden sind:

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

Wenn es als wahr ausgewertet wird, führen wir eine INSERT-Abfrage aus.
So weit, ist es gut.


Es gibt jedoch ein Problem mit der Parallelität.
Wenn 2 Aufträge zum exakt dieselbe Zeiteingehen, können sie beide die verfügbare Menge lesen, bevor einer von ihnen die Bestellung eingegeben hat Reihenfolge und überschreitet somit den qty_on_hand.


Also zurück zur Wurzel der Frage:
Ist es möglich, eine INSERT-Anweisung bedingt auszuführen, damit wir beide Abfragen zu einer kombinieren können?

Ich habe viel herumgesucht und die einzige Art von bedingter INSERT-Anweisung, die ich finden konnte, war ON DUPLICATE KEY, was hier offensichtlich nicht zutrifft.

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

Wenn vom select keine Zeilen zurückgegeben werden (da Ihre spezielle Bedingung falsch ist), erfolgt keine Einfügung.

Verwenden Sie Ihr Schema aus der Frage (vorausgesetzt, Ihre id-Spalte ist auto_increment):

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

Dadurch werden keine Zeilen eingefügt, wenn nicht genügend Bestand vorhanden ist. Andernfalls wird die Bestellzeile erstellt.

Schöne Idee übrigens!

45
Bohemian

Versuchen:

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

Wenn ein Produkt mit id gleich 2 vorhanden ist und der qty_on_hand für dieses Produkt größer oder gleich 20 ist, erfolgt eine Einfügung mit den Werten product_id = 2 und qty = 20. Andernfalls erfolgt keine Einfügung. 

Hinweis : Wenn Ihre Produkt-IDs eindeutig sind, möchten Sie möglicherweise eine LIMIT-Klausel am Ende der SELECT-Anweisung hinzufügen.

17
Shef

Wenn Sie sich nicht sicher sind, was Parallelität bedeutet, müssen Sie sich über das Sperren in mysql informieren. Dies bedeutet jedoch, dass Sie nur 20 Elemente nehmen können, wenn 20 Elemente verfügbar sind:

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

Sie können dann überprüfen, wie viele Zeilen betroffen waren. Wenn keine betroffen waren, hatten Sie nicht genügend Bestand. Wenn 1 Zeile betroffen war, haben Sie den Bestand effektiv verbraucht.

2
My Other Me

Wahrscheinlich lösen Sie das Problem falsch. 

Wenn Sie befürchten, dass zwei Leseoperationen gleichzeitig ausgeführt werden und einer mit veralteten Daten arbeitet, lautet die Lösung Sperren oder Transaktionen verwenden.

Lassen Sie die Abfrage dies tun:

  • sperrtabelle zum Lesen
  • tabelle lesen
  • tabelle aktualisieren 
  • lösen Sie die Sperre
1
Konerak