Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Pierwsza procedura MySQL
Forum PHP.pl > Forum > Bazy danych
Skie
Witam,
chciałbym byście ocenili sposób, w który podchodzę do rozwiązania pewnego problemu. Jednak najpierw może go przedstawię:

Stworzyłem dla użytkowników witryny możliwość zamieszczania ogłoszeń kupna różnych przedmiotów (Np. chcę kupić 2x Przedmiot1 płacąc 10 za sztukę).
Tabelę od tego nazwałem market_offers, która zawiera takie pola jak (id, name, quantity, price), oraz kilka innych, które nie są teraz niezbędne.

name -> nazwa przedmiotu (tutaj należy zauważyć że nazwa danego przedmiotu jest w bazie dla niego unikalna w innej tabeli, w której przechowuję dane przedmiotów).
quantity -> ilość takich przedmiotów, które chcemy kupić.
price -> cena za sztukę.

Problem pojawia się natomiast przy sprzedaży przedmiotu. Załóżmy, że użytkownik posiada 40 egzemplarzy Przedmiotu2. Przy próbie ich sprzedaży, skrypt powinien wybrać z bazy danych 40 najwyższych ofert, i wyliczyć ile wspomniany user zarobi na sprzedaży. I tutaj pojawia się prawdziwy ćwiek - jak pobrać z bazy tyle rekordów by suma ich "quantity" była dokładnie 40 lub wyższa, szukając oczywiście najwyższych ofert wg. ceny.

Można byłoby to zrobić dość prosto w PHP + MySQL, pobierając po jednym najwyższym rekordzie (coś a la "[...] LIMIT $actual_row, 1"). Jednakże uważam, że takie coś byłoby prawdziwym mordercą dla bazy danych, zwłaszcza, gdy oferty kupna będą oferowały niskie ilości przedmiotów, a dany użytkownik będzie chciał ich sprzedać naprawdę dużo.

Pomyślałem więc -> napisze do tego procedurę w MySQL (i tutaj pojawia się pierwsze pytanie - czy dobrze podszedłem do tej sprawy odwołując się do procedur?)

Po 4 godz pracy (tak, wiem, że czas śmieszny, ale jest to pierwsza procedura jaką napisałem w życiu), wyskrobałem wreszcie działający fragment kodu:
  1. DROP PROCEDURE IF EXISTS select_market_offers;
  2.  
  3. delimiter //
  4.  
  5. CREATE PROCEDURE select_market_offers (OUT res INT(10), IN in_quant INT(10), IN in_name VARCHAR(100))
  6. BEGIN
  7. SET @all_cost = 0;
  8. SET @tmp_sum = 0;
  9. SET @tmp_cost = 0;
  10. SET @tmp_id = 0;
  11. SET @tmp_last_id = 0;
  12. SET @counter = 0;
  13.  
  14. wloop:WHILE in_quant > 0 DO
  15. SET @q = CONCAT('SELECT id, quantity, price INTO @tmp_id, @tmp_sum, @tmp_cost FROM market_offers WHERE name="', in_name, '" ORDER BY price DESC LIMIT ', @counter, ',1');
  16. PREPARE STMT FROM @q;
  17. EXECUTE STMT;
  18. IF @tmp_last_id = @tmp_id THEN LEAVE wloop; END IF;
  19. IF @tmp_sum > in_quant THEN SET @tmp_sum = in_quant; END IF;
  20. SET @tmp_last_id = @tmp_id;
  21. SET @counter = @counter + 1;
  22. SET @all_cost = @all_cost + (@tmp_sum * @tmp_cost);
  23. SET in_quant = in_quant - @tmp_sum;
  24. END WHILE;
  25. SET res = @all_cost;
  26. SELECT res;
  27. END; //
  28.  
  29. delimiter ;


Przykład użycia:
  1. CALL select_market_offers(@out, "Przedmiot1", 30);


Takie użycie powinno zwrócić w zmiennej @out (i tak rzeczywiście robi) ilość pieniędzy jakie dany użytkownik może otrzymać przy sprzedaży tych 30 przedmiotów.

Teraz kilka pytań:
Czy powyższa procedura jest poprawnie napisana? Czy może są w niej jakieś błędy powodujące niepotrzebnie zbytne obciążenie bazy?
Czy wybrane przeze mnie podejście do tego problemu jest dobre?

Z góry dziękuję za wszystkie odpowiedzi :-)
cojack
Jeżeli tak wygląda pl/sql w mysql, to ciesze się że nie używam tej bazy.

@EDIT
a że się spytam, po cholerę Ci tam pętla w tym zapytaniu?

Nie można zrobić tak:

SELECT "price" * "in_quant" FROM "market_offers" WHERE name="', in_name, '";

?

Zresztą nie wiem o jakim danym użytkowniku piszesz, jak nie przesyłasz nigdzie id użytkownika więc nie ma jak pobierać cen dla danego użytkownika. W takim wypadku taka cena będzie dla każdego użytkownika.

Poćwicz jeszcze trochę, nie rób nic na siłę.
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.