2011-07-28 16 views
21

pierwsze, tutaj jest zwięzłe podsumowanie pytanie:MySQL INSERT IF (zwyczaj if)

Czy to możliwe, aby uruchomić oświadczenie INSERT warunkowo? Coś podobnego do tego:

IF(expression) INSERT... 

Teraz wiem, że mogę to zrobić z procedury przechowywanej. Moje pytanie brzmi: czy mogę to zrobić w moim zapytaniu?


Dlaczego miałbym to zrobić?

Załóżmy mamy następujące 2 stoliki.

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

Teraz, powiedzmy, że zamówienie na 20 Voodoo Dolls (identyfikator produktu 2) jest w
Najpierw sprawdź, czy jest wystarczająco ilość towaru:

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 wartość jest prawidłowa, uruchamiamy kwerendę INSERT.
Jak dotąd tak dobrze.


Istnieje jednak problem z współbieżnością.
Jeśli 2 zamówienia przychodzą na dokładnie w tym samym czasie, mogą one czytać ilość w kasie zanim którykolwiek z nich podał zamówienie. Następnie złożą zamówienie, tym samym przekraczając qty_on_hand.


Wróćmy do głównego pytania:
Czy to możliwe, aby uruchomić oświadczenie INSERT warunkowo, tak, że możemy połączyć obie te kwerendy w jednym?

Szukałem wiele, a jedynym typem warunkowego oświadczenia INSERT, które mogłem znaleźć, było ON DUPLICATE KEY, co oczywiście nie ma tutaj zastosowania.

Odpowiedz

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

Jeśli z selekcji nie zostaną zwrócone żadne wiersze (ponieważ Twój warunek specjalny to fałsz), nie ma wkładki.

Używanie schematu z pytaniem (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 będzie wstawić żadnych wierszy, jeśli nie ma wystarczająco dużo akcji na dłoni, inaczej będzie tworzyć rząd zamówienia.

Niezły pomysł!

+0

@Bohemian: Nie ma potrzeby stosowania dwóch instrukcji "SELECT". Wystarczy jeden. Spójrz na moją odpowiedź. :) – Shef

+0

To prawda, ale próbowałem dopasować mój * ogólny * wzór. Podoba mi się twoja odpowiedź ... +1 – Bohemian

+0

@Joseph Silber: Aby zrobić co ??? Gdzie jest to odejmowanie? Co odejmowanie ma wspólnego z normalizacją? : D Czy rozumiesz, że powyższe zapytanie składa się z dwóch instrukcji 'SELECT' w porównaniu do mojego, który działa z jednym? (Nic wbrew twojej odpowiedzi, albo ty, czeski). – Shef

2

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

Jeśli obawiasz się dwóch operacji odczytu w tym samym czasie, a tym samym będzie działać na nieaktualnych danych, rozwiązaniem jest użycie zamków lub transakcji.

Czy zapytanie to zrobić:

  • tabeli blokady dla odczytu
  • stół odczytu
  • tabela zmiana
  • zwolnienia blokady
+0

Nie jestem pewien, czy blokowanie jest najlepszym rozwiązaniem. Może to spowodować poważne problemy z wydajnością. –

2

Nie jestem pewien współbieżności, będziesz musiał aby przeczytać o blokowaniu w mysql, ale to pozwoli ci mieć pewność, że weźmiesz tylko 20 przedmiotów, jeśli 20 pozycji będzie dostępnych:

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

Następnie można sprawdzić liczbę wierszy, których dotyczy. Jeśli nic nie wpłynęło, nie miałeś wystarczającej ilości zapasów. Jeśli dotyczy to 1 rzędu, to faktycznie zużyłeś zapasy.

16

Spróbuj:

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

Jeśli produkt o id równej 2 istnieje i qty_on_hand jest większa lub równa 20 tego produktu, a następnie wkładka wystąpią z wartościami product_id = 2 i qty = 20. W przeciwnym razie nie pojawi się wkładka.

Uwaga: Jeśli identyfikatory produktów są uwaga wyjątkowy, warto dodać LIMIT klauzulę na końcu zestawienia SELECT.

+0

Jak byś to negował? – user2340939

+0

Jednym ze sposobów jest dodanie na końcu: HAVING COUNT (*) = 0. – user2340939