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:
DROP PROCEDURE IF EXISTS select_market_offers; delimiter // CREATE PROCEDURE select_market_offers (OUT res INT(10), IN in_quant INT(10), IN in_name VARCHAR(100)) BEGIN SET @all_cost = 0; SET @tmp_sum = 0; SET @tmp_cost = 0; SET @tmp_id = 0; SET @tmp_last_id = 0; SET @counter = 0; wloop:WHILE in_quant > 0 DO 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'); PREPARE STMT FROM @q; EXECUTE STMT; IF @tmp_last_id = @tmp_id THEN LEAVE wloop; END IF; IF @tmp_sum > in_quant THEN SET @tmp_sum = in_quant; END IF; SET @tmp_last_id = @tmp_id; SET @counter = @counter + 1; SET @all_cost = @all_cost + (@tmp_sum * @tmp_cost); SET in_quant = in_quant - @tmp_sum; END WHILE; SET res = @all_cost; SELECT res; END; // delimiter ;
Przykład użycia:
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 :-)