Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [php/mysql] - unikalne wpisy w bazie
Forum PHP.pl > Forum > Przedszkole
konrados
Witam,

Jestem trochę zaskoczony pewną sytuacją. Otóż robię mały system statystyk - tam jedyne co mam to "id", "referrer" oraz "hits" (czyli - z jakiego referrer'a ile hits)

W tym celu mam prostą funkcję, która jest wywoływana przy każdym wejściu na stronę - funkcja ta ma za zadanie zrobić co następuje:
Czy dany referrer jest już w bazie danych?
nie?: należy dodać go a "hit" ustawić na 1
tak?: należy zwiększyć jego "hits"

Niby proste, ale gdy dnia następnego zajrzałem na tę tabelę w phpmyadmin, okazało się... że w wielu przypadkach ten sam referrer się powtarza, mam więc np. 3 referrers z "jakas_strona.com".

Ta prosta funkcja wygląda mniej więcej tak:

  1. <?php
  2. function WReferrersIncrementHits($sRef)
  3. {
  4.  //(...) pominięte sprawdzanie czy to wygląda na url i różne anty-hackowe sprawy
  5. $result=mysql_query("SELECT * FROM referrers WHERE referrer='$sRef'") or die(mysql_error());
  6. $num=mysql_numrows($result);
  7.  
  8. //jak nie ma to dodajemy i jeszcze raz pobieramy:
  9. if($num==0){//a może winno być ===0?
  10. mysql_query("INSERT INTO referrers (id,referrer)"."VALUES ('NULL', '$sRef')")or die(mysql_error());
  11. //no to raz jeszcze (trochę głupie, chyba od razu mógłbym wstawić z hit=1 ale kit z tym na razie):
  12. $result=mysql_query("SELECT * FROM referrers WHERE referrer='$sRef'") or die(mysql_error());
  13. $num=mysql_numrows($result);
  14. };
  15.  
  16. if($num!=1){
  17.  //return ;//to niemożl.
  18.  //kurde a właśnie, że się zdarza!!!!!
  19.  echo "w morde jeżozwierza, więcej niż 1 hit dla $sRef !!!";
  20.  return;
  21. };
  22.  
  23. //zwiekszamy licznik:
  24. $val_inc=mysql_result($result,0,'hits');
  25. $val_inc+=1;
  26. mysql_query("UPDATE referrers SET hits='$val_inc' WHERE referrer='$sRef'") or die(mysql_error());
  27. };
  28. ?>


Kończy się tym, że dla jednego referrer jest więcej niż jeden wiersz w bazie.

Czy to możliwe, że jeden user powoduje wywołanie tej funkcji i w jej połowie (w trakcie jej wykonywania) inny user zagląda na stronę z tego samego referrer i znowu powoduje wywołanie tej funkcji, co kończy się namnożeniem tegoż referrer?
PawelC
Przed dodaniem do bazy sprawdź czy w bazie jest już zapisany dany referer, jeżeli jest to do hits dodaj 1 ale nie zapisuj referera, jeżeli niema to dodaj nowy referer. I tak unikniesz powtarzanie się referera w bazie. I wstaw ten kod w tag php będzie lepiej widać co i jak.
konrados
Dzieki, no ale przecież właśnie to robię! Spójrz na kod - sprawdzam czy jest - jak nie ma to dodaję, jak jest - zwiększam hits. Problem w tym, że i tak się dubluje
PawelC
Sprawdź
  1. <?php
  2.  
  3. function WReferrersIncrementHits($sRef)
  4. {
  5. $pobierz="SELECT `id`,`referrer`,`hits` FROM referrers WHERE referrer='$sRef'";
  6. $zrob=mysql_query($pobierz);
  7. while($link=mysql_fetch_array($zrob)){
  8. if($link[1]=='$sRef'){
  9.  
  10. $val_inc=mysql_result($pobierz,0,'hits');
  11. $val_inc+=1;
  12.  
  13. $aktualizuj="UPDATE referrers SET hits='$val_inc' WHERE referrer='$sRef'"; 
  14. mysql_query($aktualizuj) or die(mysql_error());
  15. print "zaaktualizowane pole hits";
  16. } 
  17. else 
  18. {
  19.  
  20. $add="insert into referrers (`id`,`referrer`,`hits`) values('','$sRef','1')";
  21. mysql_query($add) or die (mysql_error());
  22. print "Dodano referrera do bazy";
  23. }
  24. }
  25.  
  26. ?>
  27. </div>
  28.  
  29.  </body></html>

Poprawiłem kod bo nie było jednego } u mnie w kodzie
marcio
Dodaj pole unique na index dla danej kolumny i potem dawaj errora jak taki wpis juz jest smile.gif
konrados
Po paru dniach zajrzałem i ... znowu to samo. Na 2500 referrers 20 się dubluje, a jeden tripluje. Mimo zastosowania powyższej funkcji.

Wrócę więc do swojego pytania:
Czy to możliwe, że jeden user powoduje wywołanie tej funkcji i w jej połowie (w trakcie jej wykonywania) inny user zagląda na stronę z tego samego referrer i znowu powoduje wywołanie tej funkcji, co kończy się namnożeniem tegoż referrer?

Czy nie powinienem zastosować jakiegoś lockowania tabeli czy czegoś tam?

p.s.
Jak to właściwie jest - załóżmy, że jest długotrwale wykonująca się strona w php. Niech będzie, że czas jej wykonania to 5 sekund. W ciągu tych 5 sekund na tę stronę zagląda 2 userów:
user #1 powoduje, że zaczyna się wykonywać skrypt
user #2 również.

Pytanie A: Czy user #2 spowoduje równoległe wykonanie się skryptu, czy też będzie musiał poczekać aż żądanie #1 usera się wykona? Sądzę, że raczej równolegle, ale tak dla pewności pytam.

Pytanie B: W tym skrypcie jest coś robione na tabeli 'jakas_tabela'. Niech to właśnie trwa owe 5 sekund. Czy user #2 spowoduje równoległe wykonanie tej samej procedury na tej samej tabeli 'jakas_tabela' ?

Może rozbiję to na poszczególne kroki, oto moja teoria:)
Procedura wygląda tak (w skrócie):
1. Czy dany referrer już jest?
2a. Nie ma? : dodajmy do tabeli
2b. Jest? - zwiększamy hits dla tego referrer, ale to nie ważne w tym przykładzie

Teraz teoria:
user #1 powoduje wykonanie powyższej procedury:
user #1: Czy dany referrer już jest?
user #1: Nie ma....zaraz dodamy, ale nagle, w tym momencie:
user #2 też powoduje wykonanie tej procedury:
user #2: Czy dany referrer już jest ?
user #2: Nie ma, dodajemy. Wykonane dla usera #2 (referrer dodany)
Wracamy do wykonującej się procedury dla usera #1. Było, że nie ma danego referrera, więc:
user #1: Dodajemy

Czy powyższy scenariusz jest możliwy?
Nawet pokolorowałem, by łatwiej było zrozumieć o co mi biega:)

Bardzo bym prosił o odpowiedź, bo ta konkretna tabela przez to dublowanie psuje wszystko.

p.s.2
Cytat
Dodaj pole unique na index dla danej kolumny i potem dawaj errora jak taki wpis juz jest

Już takie pole jest, i się nie powtarza - jedyne co się powtarza to 'referrer'.
nevt
Cytat
Teraz teoria:
user #1 powoduje wykonanie powyższej procedury:
user #1: Czy dany referrer już jest?
user #1: Nie ma....zaraz dodamy, ale nagle, w tym momencie:
user #2 też powoduje wykonanie tej procedury:
user #2: Czy dany referrer już jest ?
user #2: Nie ma, dodajemy. Wykonane dla usera #2 (referrer dodany)
Wracamy do wykonującej się procedury dla usera #1. Było, że nie ma danego referrera, więc:
user #1: Dodajemy

Czy powyższy scenariusz jest możliwy?
Nawet pokolorowałem, by łatwiej było zrozumieć o co mi biega:)

Bardzo bym prosił o odpowiedź, bo ta konkretna tabela przez to dublowanie psuje wszystko.

p.s.2
CytatDodaj pole unique na index dla danej kolumny i potem dawaj errora jak taki wpis juz jest
Już takie pole jest, i się nie powtarza - jedyne co się powtarza to 'referrer'.


unique powinien być referrer! a nie jakieś id wg którego ani nie sortujesz ani nie wyszukujesz.
to rozwiąże twój problem, ponieważ to:
Cytat
Wracamy do wykonującej się procedury dla usera #1. Było, że nie ma danego referrera, więc:
user #1: Dodajemy
spowoduje BŁĄD! obsłużysz ten błąd (tzn. jeszcze raz sprawdzisz referrera) i będzie ok...
konrados
Możesz mi więcej powiedzieć? Dotąd tylko ID (int) robiłem unikalny, tzn. jako klucz podstawowy. Można też na innych polach?
Czyli w phpmyadmin (bo z niego korzystam) mam na 'referrer' postawić "klucz podstawowy"? Ale phpmyadmin robi mi ten przycisk disabled ('referrer' jest typu TEXT). Czyli się nie da chyba...?

A poza tym - czyli ogólnie moja teoria dotycząca dublowania się tych referrers jest prawidłowa? To tak się właśnie może dziać jak pokazałem? To może jednak lockowanie tables jest sensowne tutaj (skoro nie moge zrobić 'referrer' unikalnym)?

Edit:
zainteresowałem się jednak lockowaniem tabel - bo to by rozwiązało problem z jeszcze jedną moją tabelą (gdzie niby limituję ilość wierszy do 100 a teraz widzę ich tam 110 - pewnie z powodu podobnej sytuacji).
W związku z tym mam parę pytań:

1. gdy zastosuję LOCK TABLES 'tabela' a user w połowie przerwie wykonywanie skryptu (naciśnie stop w przeglądarce) - to mysql automatycznie odblokuje tę tabelę, czy na wieczność będzie ona zablokowana?

2. rozumiem składnię LOCK - trzeba podać nazwę konkretnej tabeli - ale jak patrzę na składnię UNLOCK- to tu nie ma możliwośći podania nazwy tabeli do odblokowania (jest po prostu UNLOCK TABLES) - pytanie: wszystkie tabele zostaną odblokowane czy jak? Czy tylko ostatnio zablokowana? Chcę odblokować tylko konkretną tabelę.

3. gdy już zrobię LOCK, to następne wywołanie mojej procedury (przez innego usera) wyrzuci błąd, czy może mysql poczeka aż tabela będzie odblokowana?

p.s. Google nie pomaga - znalazłem parę artykułów ale w żadnym nie ma odpowiedzi na te pytania.

Pomoże ktoś ?
dr_bonzo
Dodaj klucz unique na referera a potem masz fajne zapytanko: INSERT .. ON DUPPLICATE UPDATE ...
http://dev.mysql.com/doc/refman/5.0/en/ins...-duplicate.html
konrados
Cytat
Dodaj klucz unique na referera a potem masz fajne zapytanko: INSERT .. ON DUPPLICATE UPDATE ...
http://dev.mysql.com/doc/refman/5.0/en/ins...-duplicate.html


No dobra, to spróbuję tak. Pozostaje tylko jedna sprawa - phpmyadmin nie pozwala mi dołożyć klucza do referrer (pewnie dlatego, że jest to kolumna typu 'TEXT') ? Jeśli w phpmyadmin zamienię tę kolumnę na 'VARCHAR' - ładnie się przekonwertuje wszystko? I będę mógł wtedy dodać klucz unique?
dr_bonzo
Tak, no i jeszcze usun wszczesniej powtarzajace sie wpisy.
konrados
Dzięki, ale mam mały problem, zrobiłem takie coś:
CODE
$sQuery="INSERT INTO referrers (url,display_count) VALUES ('$sUrl',1) ON DUPLICATE KEY UPDATE display_count=display_count+1";
mysql_query($sQuery) or die(mysql_error());


($sUrl to zmienna w której jest jakiś url, display_count to kolumna typu int której wartość chcę zwiększyć dla danego url'a, albo wstawić nowy referrer z display_count równym 1)

I wyskakuje błąd:
You have an error in your SQL syntax. Check the manual that corresponds to your MySQL server version for the right syntax to use near 'ON DUPLICATE KEY UPDATE display_count=display_count+1' at line

Co robię źle?
nevt
  1. ... UPDATE display_count=VALUES(display_count)+1;
konrados
Dzięki nevt, ale nadal wyskakuje ten błąd...
W phpinfo() w sekcji MySql widzę takie coś:
Client API version: 4.0.26
Może mam za starą wersję mysql ? Czy "Client API version" oznacza właśnie wersję mysql ?

Edit: dodam jeszcze, że w tabeli jest jeszcze kolumna ID, które jest indexem podstawowym (auto-increment).
Edit2: ten skrypt też pokazuje 4.0.26
CODE

//chyba tak można sprawdzić wersję mysql:
$res=mysql_query("select version();");
echo mysql_result($res, 0);

A właśnie gdzieś znalazłem, że ten cały ON DUPLICATE KEY UPDATE jest od wersji MySQL 4.1
Czy jest jakieś alternatywne rozwiązanie dla "ON DUPLICATE KEY UPDATE" które by działało na mojej wersji mysql?

Edit3:
Ostatecznie zaimplementowałem to wg. poprzedniej sugestii - czyli "referrer" jest teraz unikalny, robię INSERT i jak mi to zwróci false to przerywam całą operację.

Dzięki jeszcze raz.
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.