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.
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!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
.
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
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.
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