Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [PHP] Usuwanie zduplikowanych wierszy z tablicy
Forum PHP.pl > Forum > Przedszkole
Mesajah
Witam,

Mam taki kodzik:

  1. $query = $_GET['query']." ".$_GET['field_name']." LIKE '".mysql_real_escape_string($input)."%'";
  2.  
  3. if($limit > 0) $query .= " LIMIT $limit";
  4.  
  5. $result = mysql_query($query, $link);
  6.  
  7. while($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
  8. $row2=$row[$_GET['field_name']];
  9. $row2 = str_replace('-', '', $row2);
  10. $aResults[] = array( "id"=>0,"value"=>strtolower($row2));
  11. }



W $aResults[] (po translacji strlower) pojawiają mi się zdublowane rekordy.
Chciałbym pozbyć się wszystkich powtórzeń i zostawić tylko unikalne elementy.
Jak to zrobić najlepiej ?

BTW A czy można w zapytaniu użyć distincta który jest case insensitive ?
Noidea
Sprawdź jaki masz metodę porównywania tekstu ustawioną dla tej kolumny. Jeśli kończy się na "_ci" (np. utf8_general_ci), to jest case insensitive. Jeśli kończy się na "_cs" albo "_bin" (np. latin1_general_cs, utf8_bin) to jest case sensitive.

Metodę porównywania napisów kolumny możesz zmienić przez ALTER TABLE, albo zdefiniować bezpośrednio w zapytaniu:
  1. SELECT DISTINCT tekst COLLATE utf8_general_ci FROM tabela WHERE tekst LIKE "%aaa%"


Ale pamiętaj, że DISTINCT odnosi się do całego wiersza, więc jeśli przy okazji pobierasz jakieś ID, czy inne kolumny, to może nie działać jakbyś się tego spodziewał.


PS. Zabezpieczenie w postaci mysql_real_escape_string( $input ), podczas gdy większa część zapytania pochodzi bezpośrednio z $_GET wygląda tak:
http://3.bp.blogspot.com/_6B8tPuW7TwQ/TQim...0/peclogate.jpg
Valdi_B
Po odczycie z bazy danych usuwasz z treści minusy i (jak sądzę) dopiero wtedy powstają powtórzenia.
Dlatego słowo DISTINCT w SQL'u to nie to czego trzeba.

Usuwać powtórzenia trzeba dopiero gdy one powstaną, czyli po modyfikacji wczytanych danych.
Załatwia to funkcja array_unique().
Mesajah
Próbowałem z tym array_unique() ale coś mi nie idzie.
W którym miejscu to wstawić ?

Zrobiłem tak:

  1. while($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
  2. $row2=$row[$_GET['field_name']];
  3. $row2 = str_replace('-', '', $row2);
  4. $aResults[] = array( "id"=>0,"value"=>strtolower($row2));
  5. $aResults[] = array_unique($aResults[]);
  6. }


Ale nie działa ...

Gdy zamienię ostatnią linijkę na:

  1. $aResults[] = array_unique($aResults); to jest niby lepiej (skrypt jakoś dziala)

ale dalej mam powtórzenia.

Jak to poprawić ?
Valdi_B
Kolejność jest taka:
1. Najpierw w pętli gromadzisz dane w tablicy (usuwając minusy).
2. Dopiero po pętli robisz:
$aResults = array_unique($aResults);
Mesajah
Zrobiłem tak jak mówisz i po pętli dałem array_unique:


  1. while($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
  2. $row2=$row[$_GET['field_name']];
  3. $row2 = str_replace('-', '', $row2);
  4. $aResults[] = array( "id"=>0,"value"=>strtolower($row2));
  5. //$aResults[] = array_unique($aResults);
  6. }
  7. $aResults = array_unique($aResults);


jednak wciąż nie działa :/
Valdi_B
Przeoczyłem, że elementami $aResults nie są "zwykłe" stringi, ale tablice.

Proponuję taką kolejność:
1. W pętli dodawaj do innej tablicy (np. $rob) same stringi:
  1. $rob[] = strtolower($row2);

2. Po pętli usuń powtórzenia:
  1. $rob = array_unique($rob);

3. Dopiero wtedy pobieraj z $rob kolejne stringi i zmontuj tablicę - wynik:
  1. foreach ($rob as $tt) {
  2. $aResults[] = array( "id"=>0,"value"=>$tt);
  3. }
Mesajah
Zrobiłem tak jak mówiłeś:

  1. while($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
  2. $row2=$row[$_GET['field_name']];
  3. $row2 = str_replace('-', '', $row2);
  4. $rob[] = strtolower($row2);
  5. }
  6.  
  7. $rob = array_unique($rob);
  8. foreach ($rob as $tt) {
  9. $aResults[] = array( "id"=>0,"value"=>$tt);
  10. }


Jednak wciąż mam powtórzenia (dokłdnie takie jak wcześńiej).
Czy coś robię nie tam ?
Valdi_B
Jestem zaskoczony. Sprawdziłem Twój kod na swoich przykładowych danych i (w mnie) powtórzenia się eliminują.
Podejrzenie: A może gdzieś na początku pola masz spację?
(końcowe spacje nie powinny mieć znaczenia, bo baza danych je "obcina").

Dla sprawdzenia zmień pętlę na:
  1. foreach ($rob as $tt) {
  2. $aResults[] = array( "id"=>0, "value"=>$tt, "dlug"=>strlen($tt));
  3. }
  4. print_r($aResults);
i dla powtarzających się tekstów porównaj "dlug".

Edit:
Zrobiłem jeszcze eksperyment ze wstawieniem do tekstów polskich liter i widzę, że:
1. Domyślnym kodowaniem po mysql_connect(...) jest latin1, natomiast ja mam i bazę i stronę w UTF-8.
Po otwarciu b.d. należy dać: mysql_set_charset('utf8');
2. Przekodowanie na małe litery trzeba robić przez mb_strtolower($row2, 'UTF-8');
Zwykła funkcja strtolower(...) wstawia za polskie znaki "?".

Może już sam do tego doszedłeś, ale na wszelki wypadek - wspominam o tym.
Mesajah
1. Pomijam kwestie ogonków bo to już inna sprawa (baze mam w utf, ale akurat tą tabele w latin2 a strona znowu utf;)), ale
nawet przy typowo angielskich nazwach są powtórzenia.

2. Nie mogę wyświetlić danych za pomocą print_r, gdyż skrypt działa jakby ajaxowa podpowiedź na stronie,
i gdy go wywołuje z parametrem to dostaje 1000 błędów, a bezpośrednio nie widzę co zwraca print_r.

3. Może wpływ na wszystko ma dalsza część kodu, której nie podałem:

  1. $link = mysql_connect($host, $user, $pass);
  2. mysql_select_db($name, $link);
  3.  
  4. if ($len)
  5. {
  6. //$query = $_GET['query']." MATCH(".$_GET['field_name'].") AGAINST ('".mysql_real_escape_string($input)."*' IN BOOLEAN MODE)";
  7. //if(!empty($_GET['orderby'])) $query .= " ORDER BY ".$_GET['orderby'];
  8. $query = $_GET['query']." ".$_GET['field_name']." LIKE '".mysql_real_escape_string($input)."%'";
  9.  
  10. if($limit > 0) $query .= " LIMIT $limit";
  11.  
  12. $result = mysql_query($query, $link);
  13.  
  14. while($row = mysql_fetch_array($result, MYSQL_ASSOC)) {
  15. $row2=$row[$_GET['field_name']];
  16. $row2 = str_replace('-', '', $row2);
  17. $rob[] = strtolower($row2);
  18. }
  19.  
  20. $rob = array_unique($rob);
  21. foreach ($rob as $tt) {
  22. $aResults[] = array( "id"=>0,"value"=>$tt);
  23. }
  24.  
  25. }
  26.  
  27. header ("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); // Date in the past
  28. header ("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); // always modified
  29. header ("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
  30. header ("Pragma: no-cache"); // HTTP/1.0
  31. header("Content-Type: application/json");
  32.  
  33. echo "{\"results\": [";
  34. $arr = array();
  35. for ($i=0;$i<count($aResults);$i++) {
  36. $arr[] = "{\"id\": \"".$aResults[$i]['id']."\", \"value\": \"".$aResults[$i]['value']."\", \"info\": \"\"}";
  37. }
  38. echo implode(", ", $arr);
  39. echo "]}";


Czy ta dalsza część może coś zmieniać ?

BTW
Cytat
Zabezpieczenie w postaci mysql_real_escape_string( $input ), podczas gdy większa część zapytania pochodzi bezpośrednio z $_GET wygląda tak:
http://3.bp.blogspot.com/_6B8tPuW7TwQ/TQim...0/peclogate.jpg


Dobre ;]
Valdi_B
Rada1: Jak sprawdzić, czy w polu "x" są pocz. spacje:

SELECT * FROM <tabela>
WHERE substr(x, 1, 1) = " "

Uruchom taką kwerendę (np. spod phpMyAdmin'a).

Rada 2:
Zmień $row2=$row[$_GET['field_name']];
na $row2 = 'x' . $row[$_GET['field_name']];
Wtedy ew. pocz. spacja w treści będzie widoczna.
Alternatywa:
zamień na $row2=trim($row[$_GET['field_name']]);
to obetniesz ew. pocz. spacje.

Rada 3: Zamiast "zwykłego" print_r($rob); zrób $xx = print_r($rob, true);
Zmienną $xx zapisz do jakiegoś pliku.
Potem podejrzyj ten plik.

Rada 4: Zrób z tego fragmentu kodu oddzielny plik i uruchom jako "normalną" stronę (a nie spod Ajax'a).
Wtedy będziesz mógł wstawić dowolne print_r.
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.