MySQL INSERT IF (custom if)

Po pierwsze, oto zwięzłe podsumowanie pytania:

Czy możliwe jest warunkowe uruchomienie INSERT Instrukcji? Coś podobnego do tego:

IF(expression) INSERT...
Wiem, że mogę to zrobić za pomocą procedury składowanej. Moje pytanie brzmi: Czy Mogę to zrobić w moim zapytaniu?
Dlaczego miałbym to robić?

Załóżmy, że mamy następujące 2 tabele:

products: id, qty_on_hand
orders: id, product_id, qty
Teraz, powiedzmy, że zamówienie na 20 lalek Voodoo (ID Produktu 2) przychodzi.
Najpierw sprawdzamy czy jest wystarczająca ilość Na Rękę:
SELECT IF(
    ( SELECT SUM(qty) FROM orders WHERE product_id = 2  ) + 20
    <=
    ( SELECT qty_on_hand FROM products WHERE id = 2)
, 'true', 'false');

Następnie, jeśli ewaluuje na true, uruchamiamy zapytanie INSERT.
Jak na razie dobrze.


Jest jednak problem z współbieżnością.
Jeśli 2 zamówienia przychodzą w dokładnie w tym samym czasie , mogą one zarówno odczytać ilość na rękę, zanim którykolwiek z nich wejdzie do zamówienia. Następnie obie złożą zamówienie, przekraczając tym samym qty_on_hand.


Wracając do sedna pytania:]} Czy możliwe jest uruchomienie instrukcji INSERT warunkowo, abyśmy mogli połączyć oba te zapytania w jedno?

Szukałem dużo i jedynym rodzajem warunkowego INSERT stwierdzenia, które mogłem znaleźć, było ON DUPLICATE KEY, co oczywiście nie ma zastosowania tutaj.

Author: Joseph Silber, 2011-07-28

4 answers

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

Jeśli nie zostaną zwrócone żadne wiersze z select (ponieważ Twoim specjalnym warunkiem jest false), nie dojdzie do wstawienia.

Używając schematu z pytania (zakładając, że kolumna id jest auto_increment):

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

To nie wstawi wierszy, jeśli nie ma wystarczającej ilości zapasów pod ręką, w przeciwnym razie utworzy wiersz zamówienia.

Fajny pomysł btw!
 42
Author: Bohemian,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-28 07:16:20

Spróbuj:

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

Jeśli istnieje produkt o id równy 2, a qty_on_hand jest większy lub równy 20 dla tego produktu, wtedy wystąpi insert o wartościach product_id = 2 i qty = 20. W przeciwnym razie nie wystąpi żadna wstawka.

Uwaga : jeśli identyfikatory produktów są unikalne, możesz dodać klauzulę LIMIT na końcu instrukcji SELECT.

 17
Author: Shef,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-28 07:16:13

Prawdopodobnie rozwiązujesz problem w niewłaściwy sposób.

Jeśli obawiasz się, że dwie operacje odczytu wystąpią w tym samym czasie, a tym samym jedna będzie pracować ze starymi danymi, rozwiązaniem jest użycie blokad lub transakcji.

Niech zapytanie zrobi to:

  • lock table for read
  • przeczytaj tabelę
  • tabela aktualizacji
  • release lock
 2
Author: Konerak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-28 06:52:56

Nie jesteś pewien co do współbieżności, musisz przeczytać o blokowaniu w mysql, ale to pozwoli Ci być pewnym, że bierzesz tylko 20 pozycji, jeśli dostępnych jest 20 pozycji:

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

Możesz następnie sprawdzić, ile wierszy zostało naruszonych. Jeśli żaden nie został naruszony, nie masz wystarczającej ilości zapasów. Jeśli 1 rząd został naruszony, skutecznie zużyłeś zapas.

 2
Author: My Other Me,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-07-28 06:58:59