Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Obliczanie średniej i odchylenia standardowego
Forum PHP.pl > Forum > PHP
damianprz

SCREEN

Na screenie widać prosty schemat tabeli w bazie danych
Na dole struktura a na górze rekordy
Mamy 8 dni i 4 produkty dla których trzeba policzyć Popyt średni i Odchylenie Standardowe

Kolega proponuje tak obliczać Pśr i OS
Cytat("Norbert")
$pytanko="SELECT popyt_klej FROM popyt";
$rezultat = mysql_query($pytanko)
or die("Błąd zapytania");


$tablica = mysql_fetch_array($rezultat);
$n=count($tablica);
$srednia=0;

for($i=0;$i<=$n;$i++)
{
$srednia=$srednia+$tablica[$i];
}

$srednia=$srednia/$n;

for($i=0;$i<=$n;$i++)
{
$wyraz_srednia=$tablica[$i]-$srednia; // tutaj do zmiennej $wyraz_srednia przypisujesz roznice danej liczby i sredniej wszystkich liczb

$tablica2[$i]= $wyraz_srednia * $wyraz_srednia;
}

for($i=0;$i<=$n;$i++)
{
$wynik=$wynik+$tablica2[$i]; //dostajesz sume tych wszystkich kwadratow roznicy wyrazu i sredniej;
}
$wynik=sqrt($wynik);

echo "<h1>Srednia: ".$srednia."</h1><P>";
echo "<h1>Odchylenie: ".$wynik."</h1><P>";


Kod daje takie wyniki dla popyt_klej i popyt_srubki
Srednia: 10
Odchylenie: 17.320508075689

Kod daje takie wyniki dla popyt_farba i popyt_drewno
Srednia: 5
Odchylenie: 8.6602540378444

Powinna wyjść średnia dla popyt_klej:
160/8 czyli 20

Mam takie pytanie: Czy da radę napisać tak kod aby później była możliwość dodawania kolejnych wierszy Id=9, data, popyty dla poszczególnych towarów itd... oraz możliwość dodawania towarów, czyli chodzi mi o taki kod który by liczył Pśr i OS nawet gdy tabela będzie się rozrastać w 2 wymiarach

Chyba nie ma sensu dla każdego towaru robić osobną tabelę w bazie danych. Takie rozwiązanie jakie prezentuje screen, że w jednej tabeli są wszystkie towary i wszystkie wartości popytu jest chyba najlepszy. Tylko problem z tym ciężkim do wymyślenia kodem.

Jakby ktoś był w stanie coś wymyśleć to proszę o jakieś pomysły lub kontakt
GG 3257134
outsider
przy obliczaniu sredniej zmien znak <= na < :

  1. for($i=0;$i<$n;$i++)
  2. {
  3. $srednia=$srednia+$tablica[$i];
  4. }


damianprz
I co to da?
Próbowałem i nic

Powyższy kod bierze tylko wartości z Id=1 i dzieli przez n=2
i dlatego wychodzą wyniki odpowiednio 5 10 10 5 bo w tym wierszu wartości wynoszą 10 20 20 10

Id Data popyt_drewno popyt_srubki popyt_klej popyt_farba
1 2009-07-01 10 20 20 10

Nawet nie wiem jak policzyć średnią z tych ośmiu wartości dla danego towaru a co dopiero mówić o kodzie który będzie liczył wszystko nawet pod dodaniu wierszy czy kolumn

Pomocy!

$tablica = mysql_fetch_array($rezultat);

tu jest problem bo to polecenie wczytuje tylko 1 wiersz
Czy jest polecenie aby wczytać do $tablica całą tablicę

Id Data popyt_drewno popyt_srubki popyt_klej popyt_farba
1 2009-07-01 10 20 20 10
2 2009-07-02 15 25 15 20
3 2009-07-03 5 15 25 5
4 2009-07-06 10 5 15 35
5 2009-07-07 5 15 25 15
6 2009-07-08 10 10 10 10
7 2009-07-09 15 15 25 25
8 2009-07-10 15 15 25 25

thek
Wartość średnia to prosta sprawa, robisz bowiem sumowanie wszystkich wartości w kolumnie i dzielisz przez liczbę wierszy.Najlepiej chyba będzie wykonać 2 zapytania. Pierwsze zwróci sumy każdej kolumny. Drugie zwróć liczbę wierszy w tabeli. To da nam średnią smile.gif
Z odchyleniem standardowym gorzej... To pierwiastek z sumy kwadratów wartości rekordów podzielonych przez liczbę wierszy od których odjęto kwadrat średniej arytmetycznej. Najszybciej więc będzie zrobić coś podobnego do poprzedniego. Ale teraz nie tylko będziemy sumowali wartości rekordów, tylko wpierw je podniesiemy do kwadratu. Zwróconą sumę podzielimy przez liczbę rekordów i odejmiemy od tego kwadrat wyliczonej średniej. Całość zaś spierwiastkujemy smile.gif
Za chwilkę zrobię tabelkę i sprawdzę swoje rozumowanie winksmiley.jpg
damianprz
Tylko jak zrobić
sumowanie wszystkich wartości w kolumnie

W ogóle to taki kod

$pytanko="SELECT * FROM popyt";
$rezultat = mysql_query($pytanko)
or die("Błąd zapytania");
$tablica = mysql_fetch_array($rezultat);
$n=count($tablica);
$wiersze = mysql_num_rows($rezultat);
var_dump($tablica) ;


daje następujące wyniki:

array(12) { [0]=> string(1) "1" ["Id"]=> string(1) "1" [1]=> string(10) "2009-07-01" ["Data"]=> string(10) "2009-07-01" [2]=> string(2) "10" ["popyt_drewno"]=> string(2) "10" [3]=> string(2) "20" ["popyt_srubki"]=> string(2) "20" [4]=> string(2) "20" ["popyt_klej"]=> string(2) "20" [5]=> string(2) "10" ["popyt_farba"]=> string(2) "10" }
var dump pokazuje że tablica jest jednowymiarowa, wczytało tylko pierwszy wiersz ten z Id=1

wiersze: 8
z kolei funkcja mysql_num_rows mówi że w tabeli jest 8 wierszów, jak dla mnie var dump zaprzecza mysql_num_rows
n: 12
zliczone elementy tablicy, dlaczego tylko 12? tyle ile pokazuje var dump

Natomiast taki kod:
echo "<h1>TAb0: ".$tablica[0]."</h1><P>";
echo "<h1>TAb1: ".$tablica[1]."</h1><P>";
echo "<h1>TAb2: ".$tablica[2]."</h1><P>";
echo "<h1>TAb3: ".$tablica[3]."</h1><P>";
echo "<h1>TAb 0 0: ".$tablica[0][0]."</h1><P>";
echo "<h1>TAb 0 1: ".$tablica[0][1]."</h1><P>";
echo "<h1>TAb 1 0: ".$tablica[1][0]."</h1><P>";
echo "<h1>TAb 1 1: ".$tablica[1][1]."</h1><P>";
echo "<h1>TAb 0 2: ".$tablica[0][2]."</h1><P>";
echo "<h1>TAb 2 0: ".$tablica[2][0]."</h1><P>";
echo "<h1>TAb 2 1: ".$tablica[2][1]."</h1><P>";
echo "<h1>TAb 1 2: ".$tablica[1][2]."</h1><P>";
echo "<h1>TAb 2 2: ".$tablica[2][2]."</h1><P>";
echo "<h1>TAb 3 0: ".$tablica[3][0]."</h1><P>";
echo "<h1>TAb 3 1: ".$tablica[3][1]."</h1><P>";
echo "<h1>TAb 3 2: ".$tablica[3][2]."</h1><P>";
echo "<h1>TAb 3 3: ".$tablica[3][3]."</h1><P>";
echo "<h1>TAb 0 3: ".$tablica[0][3]."</h1><P>";
echo "<h1>TAb 1 3: ".$tablica[1][3]."</h1><P>";
echo "<h1>TAb 2 3: ".$tablica[2][3]."</h1><P>";


daje wyniki:

TAb0: 1

TAb1: 2009-07-01

TAb2: 10

TAb3: 20


czyli tutaj mamy kolejne komórki 1. wiersza (Id=1)

TAb 0 0: 1

TAb 0 1:

TAb 1 0: 2

TAb 1 1: 0

TAb 0 2:

TAb 2 0: 1

TAb 2 1: 0

TAb 1 2: 0

TAb 2 2:

TAb 3 0: 2

TAb 3 1: 0

TAb 3 2:

TAb 3 3:

TAb 0 3:

TAb 1 3: 9

TAb 2 3:


Nie wiem o co tu chodzi, komórka [0][0] ma wartość 1, niektóre komórki nie mają wartości, komórka [1][3] ma wartość 9 (questionmark.gif)
thek
Sumowanie wszystkiego w kolumnie:
  1. SELECT sum(popyt_drewno) AS suma_dr, sum(popyt_srubki) AS suma_sr, sum(popyt_klej) AS suma_kl, sum(popyt_farba) AS suma_fa FROM tablica_danych

Sumowanie kwadratów w kolumnie:
  1. SELECT sum(pow(popyt_drewno, 2)) AS kw_dr, sum(pow(popyt_srubki, 2)) AS kw_sr, sum(pow(popyt_klej, 2)) AS kw_kl, sum(pow(popyt_farba, 2)) AS kw_fa FROM tablica_danych

Brakuje Ci jedynie liczby wierszy winksmiley.jpg
  1. SELECT count(id) AS ile FROM tablica_danych

A więc ostatecznie:
  1. $sumy = mysql_fetch_row( mysql_query( "SELECT sum(popyt_drewno) AS suma_dr, sum(popyt_srubki) AS suma_sr, sum(popyt_klej) AS suma_kl, sum(popyt_farba) AS suma_fa FROM tablica_danych" ) );
  2. $kwadraty = mysql_fetch_row( mysql_query( "SELECT sum(pow(popyt_drewno, 2)) AS kw_dr, sum(pow(popyt_srubki, 2)) AS kw_sr, sum(pow(popyt_klej, 2)) AS kw_kl, sum(pow(popyt_farba, 2)) AS kw_fa FROM tablica_danych" ) );
  3. $temp = mysql_fetch_array( mysql_query( "SELECT count(id) AS ile FROM tablica_danych" ) );
  4. $liczba = $temp['ile'];
  5. for($i = 0; $i < 4; $i++) {
  6. $srednia = $sumy[$i]/$liczba;
  7. $odchylenie = sqrt( ($kwadraty[$i]/$liczba ) - pow( $srednia, 2 ) );
  8. echo 'Średnia wynosi: '.$srednia.', a odchylenie: '.$odchylenie
  9. }

Nie dałem jedynie sprawdzenia czy $result zwraca wynik z bazy czy nie. To i tak chyba tylko na localhoscie będzie odpalane.

EDIT: Użyłem FOR bo jest wygodniej. Nie robiłem sprawdzania nazwy bo po co? Wyniki są liczone dla kolejnych kolumn i stąd po kolei będzie to wyliczone dla: drewna, śrubek, kleju i farby. Stąd też właśnie pętla do 4 jedzie smile.gif Ja użyłem wzoru na odchylenie:

odchylenie = pierwiastek ( ( suma_kwadratów_wszystkich_wartości / liczba_wartości ) - kwadrat_średniej )
Nie wiem czy tego akurat używasz, ale jest ten wzór równoważny z:
odchylenie = pierwiastek ( ( ( do_kwadratu(wartość - średnia) ) / liczba_wartości ) )

EDIT2: Doczytałem Twoje dodatkowe pytania... Podane rozwiązanie z minimalnymi modyfikacjami może służyć jak chciałeś. Na ten moment działa jeśli będziesz zwiększał liczbę rekordów dla kolejnych dni. Założyłem, że wszystkie rekordy są pełne, a więc zawierają dane. jeśli by tak nie było to musisz sobie poprawić zapytanie by sprawdzało i liczyło tylko dla tych wierszy gdzie w danej kolumnie SĄ jakieś wartości i liczyło ile ich jest.
Rozwiązanie jest skalowalne także o kolejne produkty, ale do SELECT w zapytaniach wtedy musisz dodawać dodatkowe kolumny wynikowe oraz przy samym obliczaniu zmienić w pętli FOR liczbę iteracji na taką, by pasowała do liczby produktów. Teraz są 4 ale może być ich więcej. Pamiętaj, że ja założyłem, iż wszystkie pola w tabeli są wypełnione. Jeśli tak nie jest to musisz sobie zapytania i kod dopasować do własnych potrzeb.
damianprz
  1. Średnia wynosi: 10.625, a odchylenie: 3.903123748999
  2. Średnia wynosi: 15, a odchylenie: 5.5901699437495
  3. Średnia wynosi: 20, a odchylenie: 5.5901699437495
  4. Średnia wynosi: 18.125, a odchylenie: 9.3332403269175


Pomysł bardzo fajny
Dziękuje za pomoc

Po dodaniu nowych rekordów średnia będzie nadal liczona poprawnie.
Tylko, że jest problem taki, że gdy dodam do tabeli nowy produkt to raz że trzeba zmieniać dopisując
  1. sum(popyt_nowyprodukt) AS suma_no FROM popyt

i tu zmieniając 4 na 5
  1. for($i = 0; $i < 5; $i++)

Dodatkowo gdy nowy produkt zacznie mieć wpisywane wartości od id=9 to 8 pierwszych pozycji będzie pustych a skoro są na NOT NULL to będą brane chyba zera do średniej

Problem polega na tym, że marzy mi się kod który będzie stały, którego nie trzeba będzie zmieniać po każdym dodaniu produktu
Tylko że SELECTA chyba w pętle wziąźć nie można żeby zrobić coś w stylu
  1. $n=liczba kolumn zawierająca nazwę popyt_%
  2. % - zastępuje dowolny ciąg znaków
  3. SELECT popyt_% FROM popyt


Kolega z innego forum proponuje coś w tym stylu:

nowy projekt bazy danych:

przykładowa tabela:


Proponuje taki kod:
  1. SELECT AVG(popyt),STD(popyt) FROM popyt WHERE towary_id=1;


Tylko że też jest problem bo policzy to dla Id=1 i jak damy to dla 2 3 i 4 ale jak zrobić żeby dodać 5 produkt i żeby nie dodawać linijki
  1. SELECT AVG(popyt),STD(popyt) FROM popyt WHERE towary_id=5;

thek
Problemy które poruszasz w poście zaznaczyłem po obliczeniach. Stąd właśnie pisałem by wtedy zmodyfikować to do potrzeb. Kompletnie jednak zapomniałem, że przecież MySQL ma własne funkcje statystyczne smile.gif Wtedy liczenie niemal kompletnie Ci odpada. Jeśli masz wszystkie rekordy zapełnione to robisz tylko
  1. SELECT AVG(popyt_drewno), AVG(popyt_srubki), AVG(popyt_klej), AVG(popyt_farba) FROM tabela

  1. SELECT STD(popyt_drewno), STD(popyt_srubki), STD(popyt_klej), STD(popyt_farba) FROM tabela

A w pętli for tylko byś te dane wyświetlał i nic więcej.

Gdy mowa o wyeliminowaniu pustych, lepiej "obrócić zapytania" i wywoływać dla konkretnych kategorii popytu, wyrzucając z zapytania wszystkie będące NULLami
Trzymając się Twojego układu tabel dla kolumny drewno jest to:
  1. SELECT AVG(popyt_drewno) AS srednia, STD(popyt_drewno) AS odchylenie FROM tabela WHERE popyt_drewno IS NOT NULL

W przypadku jednak bardzo dużej elastyczności rozwiązania warto pomyśleć o przebudowie tabeli i pomysł zaproponowany na tamtejszym forum jest jak najbardziej poprawny.
Jeśli przyjmiemy więc, że baza wygląda jak z innego forum to możemy zrobić automatykę smile.gif
  1. SELECT towary_id, avg(popyt) AS srednia, std(popyt) AS odchylenie FROM tabela GROUP BY towary_id

Grupujesz po towarach i dla tych zgrupowanych liczysz średnią oraz odchylenie.

Ja się dostosowywałem do Twojej bazy danych, ale jak widzisz odpowiednie zaprojektowanie tabeli potrafi przynieść lepsze efekty. Jako przyspieszacz ustawiłbym index na towary_id, a samo id można w zasadzie wyrzucić. Nazwy towarów możesz dorzucić jako osobną tabelę (z polami nazwa i id), które łączysz z towary_id by uzyskać nazwę towaru. Datę mimo wszystko bym zostawił wewnątrz dopóki jest mało produktów. Gdy będzie ich wiele dorzuciłbym pole wskazujące na datę z tabeli z datami smile.gif

Czyli w maksymalnie rozwiniętej wersji mamy:
Tabela Data:
id(int), data(date)

Tabela Towar:
id(int), nazwa(varchar)

Tabela Produkty:
id_daty(int), id_towaru(int), popyt(double)

Teraz wyświetlanie wyników to:
  1. SELECT t.nazwa, p.towary_id, avg(p.popyt) AS srednia, std(p.popyt) AS odchylenie FROM Produkty p LEFT JOIN Towar t GROUP BY p.towary_id
damianprz


Na screenie widać 3 tabelki które zrobiłem wg twojej instrukcji
W napisanych zapytaniach są błędy

  1. SELECT towary_id, avg(popyt) AS srednia, std(popyt) AS odchylenie FROM tabela GROUP BY towary_id

a ma być tak jak na screenie czyli
  1. SELECT id_towaru, avg(popyt) AS srednia, std(popyt) AS odchylenie FROM Produkty GROUP BY id_towaru


Jak widać na screenie zostały policzone wartości średnich i odchyleń
I są to poprawne wartości bo dane jaki zapisałem w tabeli (te 32 rekordy) są takie same jak w poprzedniej tabeli i wyniki są identyczne

Nie kapuje tego wyświetlania wyników
  1. SELECT t.nazwa, p.towary_id, avg(p.popyt) AS srednia, std(p.popyt) AS odchylenie FROM Produkty p LEFT JOIN Towar t GROUP BY p.towary_id

O co chodzi z tym t.nazwa Co to jest to t. czy p.

Gdy wpisałem ten kod w myPhPAdmin to zwraca mi błąd

  1. MySQL zwrócił komunikat:
  2. #1064 - Something is wrong in your syntax obok 'GROUP BY p.towary_id LIMIT 0, 30' w linii 1


Gdy poprawie na

  1. SELECT t.nazwa, p.id_towaru, avg(p.popyt) AS srednia, std(p.popyt) AS odchylenie FROM Produkty p LEFT JOIN Towar t GROUP BY p.id_towaru

to wyskakuje
  1. MySQL zwrócił komunikat:
  2. #1064 - Something is wrong in your syntax obok 'GROUP BY p.id_towaru LIMIT 0, 30' w linii 1


Jak wyświetlić te wyniki?
thek
Bo z rozpędu nie dopisałem warunku łączenia winksmiley.jpg
  1. SELECT t.nazwa, p.id_towaru, avg(p.popyt) AS srednia, std(p.popyt) AS odchylenie FROM Produkty p LEFT JOIN Towar t ON p.id_towaru = t.id GROUP BY p.id_towaru

Przy złączeniach jeśli do wyniku przekazuję kolumnę z jakiejś tabeli to muszę wskazać jaką i z której. Używa się do tego składni nazwa_tabeli.nazwa_kolumny, ale jeśli sobie ustawię alias to mogę użyć aliasu... Stąd gdy zadeklarowałem Towar t, mogę się posługiwać t jak pseudonimem a składnia t.nazwa_kolumny jest równoważna Towar.nazwa_kolumny i to samo w przypadku Produktu winksmiley.jpg Aliasy są często stosowane bo skracają zapytania mające długie nazwy tabel i w przypadku łączenia tabeli z samą sobą (a często się tak robi).
Równie dobre i być może szybsze będzie zapytanie
  1. SELECT * FROM (SELECT id_towaru, avg(popyt) AS srednia, STD(popyt) AS odchylenie FROM Produkty GROUP BY id_towaru) p LEFT JOIN Towar t ON p.id_towaru = t.id
To teraz najpierw zrobi wszystko i zwróci 4 rekordy, do których dopiszesz nazwy towarów. W pierwszym przypadku (w którym zapomniałem dopisać warunku łączenia) najpierw przypisało wszystkim rekordom nazwy produktów, a potem liczyło.
damianprz
Możesz mi sprawdzić czy to jest OK?
Do wyświetlania tego co się policzyło smile.gif
Chyba działa bo otrzymałem taką tabelkę jako wynik:

nazwa id srednia odchylenie
drewno 1 10.625 3.903123748999
srubki 2 15 5.5901699437495
klej 3 20 5.5901699437495
farba 4 18.125 9.3332403269175


  1. $zapytanko2="SELECT t.nazwa, p.id_towaru, avg( p.popyt ) AS srednia, std( p.popyt ) AS odchylenie
  2. FROM Produkty p
  3. LEFT JOIN Towar t ON p.id_towaru = t.id
  4. GROUP BY p.id_towaru
  5. ";
  6. $rezultat2 = mysql_query($zapytanko2)
  7. or die("Błąd zapytania");
  8.  
  9. $wiersze = mysql_num_rows($rezultat2);
  10.  
  11. echo '<table>';
  12. echo '<tr><td>nazwa</td><td>id</td><td>srednia</td><td>odchylenie</td></tr>';
  13.  
  14. for ($i=0; $i <$wiersze; $i++)
  15. {
  16. $wiersz = mysql_fetch_array($rezultat2);
  17. echo '<tr>';
  18. echo '<td>'.$wiersz['nazwa'].'</td>';
  19. echo '<td>'.$wiersz['id_towaru'].'</td>';
  20. echo '<td>'.$wiersz['srednia'].'</td>';
  21. echo '<td>'.$wiersz['odchylenie'].'</td>';
  22. echo '</tr>';
  23. }
  24. echo '</table>';
thek
Kod wygląda poprawnie. W sumie chyba jedynie nie musisz wyświetlać informacji o id_towaru, bo to głównie informacja dla Ciebie i nie jest wymagana do wizualizacji winksmiley.jpg Jeśli chcesz to możesz nawet ją usunąć z zapytania (usunięcie z SELECT kolumny: p.id_towaru) i pętli FOR.

PS: I chyba jako jeden z niewielu tutaj piszących nowych użytkowników zastosowałeś tabelki z sensem - do wyświetlania danych tabelarycznych, a nie układania layoutu strony "bo tak robią inni i tak pisze w książkach" winksmiley.jpg

A jeśli byś bardzo chciał się standardów trzymać to zrobiłbyś jeszcze th i tbody tongue.gif A by już super wszystko wyglądało to idealne dotrzymanie się standardów tabel z tfoot, thead itp oraz rozłączenie tagów HTML od kodu PHP... Ale to już dla purystów i zatwardziałych miłośników poprawności kodu od każdej możliwej strony winksmiley.jpg Oby dalszy Twój forumowy byt i tematy tutaj były równie owocne. Przynajmniej robisz ze swojej strony co potrafisz i nie czekasz tylko na wyniki, tylko grzebiesz gdzie się da, pytasz, szukasz i słuchasz co się do Ciebie pisze. Nie każdemu tutaj się chcę smile.gif Myślę, że na ten moment pozostaje tylko problem Ci już z wizualizacją wyników na stronie czy gdzie chcesz i ewentualne "panel administracyjny" do wprowadzania danych do bazy lub usuwania ich z niej czy też edycji smile.gif Ale to już temat albo do samodzielnego opracowania, albo do podszkolenia swoich umiejętności, bo sedno tematu już zostało wyjaśnione i kodem poprawnym poparte. A obecny uważam jako minimalista za w pełni wystarczający. Teraz już tylko można go upiększać od strony wizualnej i ewentualnie starać pisać poprawny kod HTML, walidujący się i będący zgodny ze wszelkimi standardami smile.gif Życzę w tym powodzenia smile.gif
damianprz
Dzięki stary za pomoc
Powyższe obliczenia stosuje do pracy magisterskiej
Mam za zadanie zrobić w PHP prostą aplikację do zarządzania zapasami
Użytkownik aplikacji ma za zadanie wpisywać dane popytu poszczególnych towarów oraz ustalić pewne wartości typu
-oczekiwany poziom obsługi POP (np. 95%, czyli że brak zapasu może wystąpić 5 razy na 100 przypadków)
-czas uzupełnienia zapasu T np 2 dni
-czas przeglądu zapasu T0 np 6 dni
I co 6 dni roboczych czyli co tydzień (przykładowo) aplikacja będzie liczyła wielkość zamówienia poszczególnych towarów
Do obliczenia tego potrzebne będą te stałe wartości POP T T0 oraz właśnie wyliczone przez nas wspólnie (głównie przez Ciebie smile.gif ) średnie popyty i odchylenia standardowe
Będzie trzeba zrobić kolejną tabelę w której znajdą się pewne wielkości dla każdego towaru takie jak obecny stan zapasu czy właśnie popyt średni i odchylenie które muszą być aktualizowane za każdym razem gdy je wyliczymy
Otrzymujemy wyniki:
nazwa id srednia odchylenie
drewno 1 10.625 3.903123748999
srubki 2 15 5.5901699437495
klej 3 20 5.5901699437495
farba 4 18.125 9.3332403269175
i te pogrubione dane musimy wprowadzić (zaktualizować) do tej innej tabeli
Hmm tylko jest znowu mały problem bo te dane mogą dotyczyć 4 towarów lub więcej
I teraz opcja "dodanie nowego towaru" powinna zwiększyć liczbę towarów w tabeli Towar (tam gdzie jest Id i Nazwa)
ale także powinna zwiększyć liczbę towarów w tabeli gdzie są przechowywane dane (obecny stan zapasu, popyt średni i odchylenie)
I jeśli policzymy średnie i odchylenia to muszą one zostać dodane do tej tabeli w każdej ilości, czy mamy 4 pary średnia-odchylenie czy więcej
Da się to zrobić?

thek
No jasne. Zauważ, że grupujesz dane po id_towaru. Nieważne więc ile ich jest. Zawsze policzy te dane dla tylu, ile jest różnych id_towaru. Nieważne czy będzie to 3, 33 czy 3333 smile.gif Oczywiście im więcej danych tym dłuższy czas obliczeń. Ale nie sądzę by nagromadziło się ich tyle by znacząco trzeba było czekać na wyniki winksmiley.jpg

W tym momencie masz dorobić, jeśli dobrze zrozumiałem, tabelę przechowującą informację o danych statystycznych dla tego towaru. Czyli tabela o kolumnach:
id_towaru, średnia, odchylenie, stan

Jeśli tak, to wyliczone za każdym razem dane od razu można użyć jako UPDATE w tej nowej tabeli. Na pewno masz gdzieś dane dotyczące obecnego stanu danego produktu. Zapytanie więc pobierało by tę daną pomniejszoną o popyt z danego dnia, no chyba, że to już gdzieś automatycznie by odejmowało i stan byłby "na bieżąco".
Załóżmy, że jest tabela przechowująca stan każdego z produktów i mająca inne dane (jakaś o nazwie hmmm... Stan ? winksmiley.jpg ) oraz możemy to połączyć z już obliczonymi wynikami średnimi i odchyleniami bo w końcu mamy łączącą je kolumnę id_towaru jako klucz.
  1. SELECT c.*, s.aktualny_stan FROM ( SELECT t.nazwa, p.id_towaru, avg( p.popyt ) AS srednia, std( p.popyt ) AS odchylenie FROM Produkty p LEFT JOIN Towar t ON p.id_towaru = t.id GROUP BY p.id_towaru) c LEFT JOIN Stan s ON c.id_towaru = s.id_towaru
Świetnie. Właśnie połączyliśmy wyniki ze stanem... Pozostaje jedynie zapisać je w tabeli nadzorującej co się dzieje. Niech to będzie Raport o polach jakie podałem na początku.
  1. UPDATE Raport r LEFT JOIN (tutaj walnij cale powyższe zapytanie) w ON r.id_towaru = w.id_towaru SET r.srednia = w.srednia, r.odchylenie = w.odchylenie, r.stan = w.aktualny_stan

Zauważ, że można część widoczna pod aliasem w optymalizować poprzez usuwanie z podzapytań kolumn jakie nie są nam potrzebne. A więc tak naprawdę możemy wyrzucić całkowicie łączenie z tabelą Towary (bo nie potrzebujemy w tym wypadku nazwy smile.gif ) i skrócić zapytanie do:
  1. UPDATE Raport r LEFT JOIN ( SELECT p.*, s.aktualny_stan FROM ( SELECT id_towaru, avg( popyt ) AS srednia, STD( popyt ) AS odchylenie FROM Produkty GROUP BY id_towaru) p LEFT JOIN Stan s ON p.id_towaru = s.id_towaru ) w ON r.id_towaru = w.id_towaru SET r.srednia = w.srednia, r.odchylenie = w.odchylenie, r.stan = w.aktualny_stan
Przypatrz się temu zapytaniu, bo z reguły takie podejście do UPDATE wielu rekordów jednocześnie poprzez użycie LEFT JOIN by połączyć tabelę z danymi starymi i nowymi a potem nadpisanie odpowiednich kolumn wartościami innej kolumny jest czymś rzadko stosowanym, choć przydatnym, gdyż nie trzeba odwoływać się za każdym razem do jakiegoś identyfikatora w warunku WHERE, co jest najczęściej stosowana formą rozpoznawania jaki wiersz mamy uaktualniać smile.gif Ja tak sobie parę dni temu zautomatyzowałem aktualizację jakichś 400-500 rekordów w bazie winksmiley.jpg
Zakładam, że w nowej tabeli są już jakieś domyślne rekordy dla produktów o tym id, choćby puste. Jeśli nie to zrób INSERT i tyle smile.gif
Powinniśmy po tym działaniu w tej tabeli mieć zawsze tyle rekordów ile jest produktów. Jeśli teraz będziesz chciał wyświetlić te dane to robisz:
  1. SELECT t.id, r.* FROM Raporty r LEFT JOIN Towary t ON r.id_towaru = t.id
Możesz jednak rozwiązać to nieco inaczej. Możesz dodać jeszcze dodatkową kolumnę z datą, dzięki temu zapytanie uruchomione po zakończeniu wprowadzania popytów z danego dnia stworzy rekordy w tej bazie zamiast je uaktualniać. Dzięki temu będziesz miał dane o średniej, odchyleniu i stanie dla każdego produktu z dowolnego dnia. Oczywiście jeśli już przechowujesz informację o stanie produktu na każdy dzień w innej tabeli, to nie ma sensu jej dublować także tu. Od tego posłuży ewentualny JOIN z tamtą tabelą i wyciagnięcie stanu na interesujący nas smile.gif Na upartego to odpowiednio zaplanowawszy bazę można dzięki temu tworzyć raporty obejmujące określone okresy czasu. Osobiście jednak uważam, że od tego praca magisterska jest, aby samemu coś zrobić. Możesz nie wierzyć, ale ja swoją zrobiłem od A do Z samodzielnie, bo temat wybrałem sobie taki, że nie miałem szans na znalezienie gotowca w necie ("Analiza wydajnościowa technik animacji komputerowej przedstawicieli systemów operacyjnych rodziny Windows i Linux w środowisku Maya"). W przerwach zaś jednemu kolesiowi pisałem bardzo prosty CRM do jego magisterki. Nie płacił wiele to i wiele funkcji nie dostał winksmiley.jpg Zresztą napisanie samemu pracy sprawi, że na obronie z Twojej pracy Cię nie zagną nawet najbardziej wrednymi pytaniami. Dla mnie najtrudniejszym było: "Jak Pan sobie wyobraża wykorzystanie tej pracy. Do czego może się ona przydać? Jak ja rozwinąć by można". A na to odpowiedź znałem od momentu gdy poznałem wyniki swoich pomiarów winksmiley.jpg
damianprz
Oj kolego thek, co ja bym bez Ciebie zrobił smile.gif
Dzięki za pomoc. Poświęciłeś dużo czasu zeby mi pomóc.
Szacunek dla Ciebie, bo takich osób dużo nie ma.
Spróbuje zastosować twoje ostatnie podpowiedzi i jak zadziała to dam znać.
thek
A ja życzę powodzenia w kodzie php/html i analizie oraz tworzeniu własnych zapytań smile.gif A później także z obroną smile.gif Wrzesień już niemal, a jak sądzę w połowie września będzie obrona, czyli za pasem winksmiley.jpg
damianprz
Ponoć ma być raczej pod koniec września tak żeby jeszcze do połowy września był czas na analizy z promotorem
Trzeba się sprężyć

Przeczytałem twoje ostatnie podpowiedzi i sie zastanawiam na d ilością tabel
bo proponujesz tabele

Stan (Załóżmy, że jest tabela przechowująca stan każdego z produktów i mająca inne dane (jakaś o nazwie hmmm... Stan ?)
i
Raport (id_towaru, średnia, odchylenie, stan)

I tu jest stan i tu jest stan i troszkę nie bardzo rozumiem po co
Moja tabela w której są obecne wyniki zapasów (stan zapasu, srednia, odchylenie) wygląda tak

zapasy
id_towaru ......... int(10) .......... Nie 0
Nazwa ..........varchar(30) ....... Nie
stan .............int(10) ........... ..Nie 0
srednia .......... int(10) ......... .Nie 0
odchylenie ........... int(10) ...... Nie 0

i pasuje te wyniki które policzyliśmy dla każdego towaru (średnia odchylenie)
tutaj zapakować

gdzieś bedzie opcja żeby wyskoczyło okno do wpisania popytów z danego dnia i pracownik codziennie bedzie wpisywał te popyty a kod bedzie uaktualniał tabele Podukty
Co jakiś okres czasu (zależnie od ustawienia T0 - czas przeglądu zapasu, np 6 dni czyli co tydzień) bedzie wyskakiwał monit żeby zamówić dostawy towarów
i wtedy pracownik kliknie na Oblicz średnią i odchylenie, te dane sie zaktualizują, obecny stan zapasu bedzie zaktualizowany na bieżąco (codziennie) i jakiś kod wygeneruje ile każdego zapasu trzeba zamówić. I to mniej wiecej tyle

Tworzenie tabeli Stan i tabeli Raport nie jest konieczne prawda? Można po prostu dane wpakować do 1 tabeli co nie? do tej mojej Zapasy
Jeśli chodzi o daty to za bardzo nie chce sie z nimi bawić. Z resztą sam widzisz, że mistrzem php czy mysql to nie jestem sad.gif
Chyba tylko data bedzie przy wstawianiu dziennego popytu i daty beda wyświetlane gdy ktos bedzie chciał zobaczyć tabele z dziennym popytem

EDIT:
  1. UPDATE zapasy LEFT JOIN
  2. ( SELECT id_towaru, avg(popyt) AS srednia, STD(popyt) AS odchylenie FROM produkty GROUP BY id_towaru)
  3. ON zapasy.id_towaru = produkty.id_towaru
  4. SET zapasy.srednia = produkty.srednia, zapasy.odchylenie = produkty.odchylenie

Coś nie chce chodzić. Wywala błąd:
  1. MySQL zwrócił komunikat:
  2.  
  3. #1064 - Something is wrong in your syntax obok
  4. 'LEFT JOIN ( SELECT id_towaru, avg(popyt) AS srednia, STD(popyt)'
  5. w linii 1


.

11
thek
Zapytanie sypie Ci błąd bo nie nadałeś aliasu dla podzapytania... Zauważ, że po nawiasach nie masz nazwy tego, choć sądząc z całości powinno tam być napisane produkty. Tworzenie tabeli nie jest konieczne. Ważne byś miał gdzieś możliwość przechowania informacji o aktualnych danych jakie wyliczasz i tych jakie potrzebujesz (jak aktualny stan produktu). Może to być wspomniana Zapasy czy jakakolwiek inna, która jest związana bezpośrednio z danym produktem (drewnem, śrubkami) poprzez id_towaru. Opisana przez Ciebie dobrze pasuje, z jednym małym zastrzeżeniem. Średnia i odchylenie muszą być wartościami zmiennoprzecinkowymi, czyli FLOAT lub DOUBLE, bo inaczej przytnie Ci wartości za przecinkiem, gdyż pole to masz na int ustawione. Wygodniej będzie double bo ma większą precyzję.
damianprz
Już wcześniej ustawiłem wartości na double z obawy że UPDATE mógł wywalać jakiś błąd próbując wprowadzić do rekordu INT wartość niecałkowitą

Ten UPDATE nadal nie działa
thek
Nie dublujmy może tematów i skupmy się w jednym winksmiley.jpg UPDATE na 100% działa, ale to jest UPDATE a nie INSERT, więc w bazie Zapasy muszą już istnieć rekordy z id_towaru równym produktom jakie w zapytaniu obrabiasz. Zapytanie działa i jest ono w osobnym temacie, który dlań założyłeś. Ale by nie skakać to tu masz je powtórzone:
  1. UPDATE zapasy z LEFT JOIN (SELECT id_towaru, avg( popyt ) AS srednia, STD( popyt ) AS odchylenie FROM produkty GROUP BY id_towaru) p ON z.id_towaru = p.id_towaru SET z.srednia = p.srednia, z.odchylenie = p.odchylenie
Innych kolumn i tabel nie ruszam bo to zbędne. I jeszcze raz zaznaczam. UPDATE mozna zrobić tylko na tych rekordach, które istnieją. A będą one istniały w wersji finalnej, gdyż powstaną zapewne jako puste i wyzerowane w momencie dodania nowego produktu do bazy. No ale to już Twoja działka jako tworzącego stronę, by o tym pamiętać winksmiley.jpg
damianprz
  1. UPDATE zapasy z LEFT JOIN (SELECT id_towaru, avg( popyt ) AS srednia, STD( popyt ) AS odchylenie FROM produkty GROUP BY id_towaru) p
  2. ON z.id_towaru = p.id_towaru SET z.srednia = p.srednia, z.odchylenie = p.odchylenie


Nie wiem dlaczego to nie działa u mnie
W tabeli zapasy mam 4 pozycje od id 1 do 4
W tabeli produkty mam dane dla 4 towarów w 36 rekordach (9 wartości popytu z różnymi datami dla 1 towaru)
  1. SELECT id_towaru, avg( popyt ) AS srednia, STD( popyt ) AS odchylenie FROM produkty GROUP BY id_towaru

daje taki wynik:

id_towaru ..........srednia ........................odchylenie
1 ......................10.555555555556 ..........3.6851386559504
2 ......................15 ................................5.2704627669473
3 .......................20 ...............................5.2704627669473
4 .......................18.111111111111 ..........8.7995510547659

czyli niby wszystko jest OK
Może mam źle ustawione klucze, indeksy...questionmark.gif

Mam takie tabele:

zapasy

id_towaru int(10) Nie 0
Nazwa varchar(30) Nie
ZM int(10) Nie 0
ZZ int(10) Nie 0
ZR int(10) Nie 0
stan double Nie 0
srednia double Nie 0
odchylenie double Nie 0

produkty

id_daty int(11) Nie 0
id_towaru int(11) Nie 0
popyt double Nie 0






A tak jeszcze zapytam skąd wiesz że u Ciebie działa ok?
Zrobiłeś na szybcika podobną bazę danych?
thek
Owszem smile.gif Mam na laptopie swoim wszystko co potrzebne do pracy programiście, webmasterowi smile.gif Stworzenie tabeli w bazie i wypełnienie jej to chwilka.
MySQL 5.1.30
PHP 5.2.8
Apache: 2.2.11
Pokaż dokładnie jaki komunikat błędu wysypuje baza danych, by można było usterkę i jej przyczynę zlokalizować. Może gdzieś jest tylko literówka smile.gif
damianprz
Już wszystko działa!
Instalka Vertrigo pomogła

guitar.gif
thek
Widzę, ze sugestia o fatalnym wpływie MySQL 3.X na zapytanie przyniosła efekt. Pamiętaj by używać stabilnych nowych wersji oprogramowania jeśli chcesz testować cokolwiek. W necie jest bowiem wiele poradników, ale część z nich tyczy tylko albo bardzo nowych funkcji, które nie zawsze działają, albo tyczą się bardzo starych standardów. Ja też kiedyś mialem problem wlaśnie z MySQL 3 i stąd wiem, że lepiej zamienić ją na coś o wiele nowszego winksmiley.jpg Nowsze bazy z reguły bowiem są o wiele bardziej zgodne ze standardami języka SQL.
MMaro
Witam

odkopuje temat - ponieważ mam problem z obliczeniem odchylenia standardowego... to co piszecie owszem działa ale co w sytuacji gdy mamy obliczyć odchylenie dla liczb, które zawierają liczby ujemne?? Skrypt sypie się w momencie obliczania kwadratów... ma ktoś pomysł jak to obejść?

  1. $sumy = mysql_fetch_row( mysql_query( "SELECT sum(popyt_drewno) AS suma_dr, sum(popyt_srubki) AS suma_sr, sum(popyt_klej) AS suma_kl, sum(popyt_farba) AS suma_fa FROM tablica_danych" ) );
  2. $kwadraty = mysql_fetch_row( mysql_query( "SELECT sum(pow(popyt_drewno, 2)) AS kw_dr, sum(pow(popyt_srubki, 2)) AS kw_sr, sum(pow(popyt_klej, 2)) AS kw_kl, sum(pow(popyt_farba, 2)) AS kw_fa FROM tablica_danych" ) );
  3. $temp = mysql_fetch_array( mysql_query( "SELECT count(id) AS ile FROM tablica_danych" ) );
  4. $liczba = $temp['ile'];
  5. for($i = 0; $i < 4; $i++) {
  6. $srednia = $sumy[$i]/$liczba;
  7. $odchylenie = sqrt( ($kwadraty[$i]/$liczba ) - pow( $srednia, 2 ) );
  8. echo 'Średnia wynosi: '.$srednia.', a odchylenie: '.$odchylenie
  9. }

i właśnie tutaj pojawia się problem...

  1. $kwadraty = mysql_fetch_row( mysql_query( "SELECT sum(pow(popyt_drewno, 2)) AS kw_dr, sum(pow(popyt_srubki, 2)) AS kw_sr, sum(pow(popyt_klej, 2)) AS kw_kl, sum(pow(popyt_farba, 2)) AS kw_fa FROM tablica_danych" ) );

thek
Pytanie... Po co obliczać odchylenie samemu, skoro MySQL posiada funkcję do tego? -> STDDEV_POP()
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.