Newsgroup: it.comp.www.php
Titolo: loop while

dopo un po' che "smanetto" con il php, inizio ad imparare qualcosa...
anche se con qualche difficoltà.. ad esempio:
sto cercando di creare una funzione che cerca in un database un valore.
se il valore non esiste (e quindi c'è un "buco"), inserisce in quella
posizione dei determinati valori, altrimenti continua a cercare un
posto libero nella tabella e li inserice appena trova uno spazio.
il codice che ho scritto è questo:
<?
include("config.php"); //file con i dati di accesso al db
$x = 1;
$result = mysql_query("SELECT * FROM table WHERE
position=\"$x\"",$connect);// da quel che ho capito se non trova il
valore restituisce 0, quindi ->
if ($result == 0) {
        insert(); //funzione che inserisce i dati nella posizione 1
}
else {
	$x = 2;
	while ($result != 0) {
		$result = mysql_query("SELECT * FROM table WHERE
position=\"$x\"",$connect);
		if ($result == 0) {
			insert(); // inserisce
			return; //esce dal loop
		} else {
		$x ++; // aumenta x di 1
		}
	}
	echo "finito";
}
?>
quando carico la pagina, il browser non la carica (anche se ci prova) e
dopo 30 sec mi dice che l'attesa è durata troppo e ferma il
caricamento.
non riesco a capire dov'è che sbaglio... qualcuno sa aiutarmi?
grazie

 dopo 30 sec mi dice che l'attesa è durata troppo e ferma il caricamento.
A occhio e croce direi che sta lavorando come un forsennato e dopo 30
secondi interviene il timeout di PHP a bloccare il lavoro.
Fossi in te cercherei un modo diverso di risolvere il problema.
Quando hai 100.000 valori, in questo modo rischi di fare 100.001 query.
Bye.

 Bye.
convengo che non sia il modo piu pratico, ma non me ne sono venuti in
mente altri... 
ogni suggerimento è bene accetto!
ciao

ogni suggerimento è bene accetto!
Continuo a pensare anch'io che sia una cosa brutta :-D
Comunque, se proprio devi farla pensa questo: se c'e' un buco da qualche
parte fra le posizioni x=1 e x=1000, allora il numero di righe con x fra
1 e 1000 (compresi) non sara' 1001, ma inferiore.
Quindi selezioni MAX(position) dalla tabella, e parti con a=1, b=MAX.
Dopodiche' fai una bella bisezione di quelle ignoranti:
	while($b != $a))
	{
		$c	  = (int)(($a + $b - 1) / 2);
		$expected = $c - $a + 1;
		$n = COUNT(*) ... WHERE position >= $a AND position <= $c
		print "Fra $a e $c ci sono $n/$expected righe<br />\n";
		if ($n < $expected)
		{
			print "Quindi proseguo fra $a e $c.<br />\n";
			// Fra c e a ci sono ($expected - $n) buchi.
			$b = $c;
			continue;
		}
		// Non ci sono buchi fra $a e $c. Tentiamo fra $c+1 e $b.
		$c++;
		$expected = $b - $c + 1;
		$n = COUNT(*) ... WHERE position >= $c AND position <= $b
		print "Fra $c e $b ci sono $n/$expected righe<br />\n";
		if ($n < $expected)
		{
			print "Quindi proseguo fra $c e $b.<br />\n";
			// Fra c e b ci sono ($expected - $n) buchi.
			$a = $b;
			continue;
		}
		print "Non ci sono buchi fra $a e $b;<br />\n";
		// Non ci sono buchi fra $a e $b. Ma siccome almeno
		// un buco deve esistere:
		break;
	}
	$next = $b + 1;
	print "Il primo buco e' alla posizione $next<br />\n";
Dovrebbe andare un tantino piu' veloce, ammesso che funzioni. Comunque
riguardati la sintassi di mysql_query, mysql_fetch_result,  mysql_free
e tutto il resto.
Per debug, bisogna vedere cosa succede se manca proprio il primo ID, o
se manca l'ID 6 quando hai 9, 10, 11, o 12 elementi, in tutto (provali
tutti, dammi retta: non fidarti del fatto che funzioni il primo).
Se piu' tardi ho un po' di tempo e accesso a MySQL (dubito), lo provo.
Leonardo
-- 
Dawn is near, the time is short (Human aircraft! Hard to port!)
We eight reindeers of the night work as one in silent flight.
Let not one damn house pass by (Who taught that guy how to fly?)
Each year sees more girls and boys: how many of them can we bring toys?

 o in basso andando a modificare il valore
perchè complicarti la vita?
poniamo che il campo si chiami posizione:
posizionecorrente = SELECT posizione FROM ... WHERE (pk=..record-attuale..)
UPDATE .. SET posizione=posizione+2 WHERE (pk !=..record-attuale..) AND (posizione)>=posizionecorrente
UPDATE .. SET posizione=posizione+1 WHERE (pk=..record-attuale..)
insomma il "buco" lo crei tu ...

insomma il "buco" lo crei tu ...
Attenzione che non e' detto che funzioni, in presenza di unique index;
varia da versione a versione. Su alcuni DB si puo' aggirare, su altri,
no.
mysql> CREATE TABLE pippo ( pos integer );
mysql> CREATE UNIQUE INDEX pippo_ndx ON pippo (pos);
mysql> INSERT INTO pippo VALUES ( 1 );
mysql> INSERT INTO pippo VALUES ( 2 );
mysql> INSERT INTO pippo VALUES ( 3 );
mysql> DELETE FROM pippo WHERE pos = 2;
mysql> INSERT INTO pippo VALUES ( 2 );
mysql> UPDATE pippo SET pos = pos + 1 WHERE pos >= 2;
ERROR 1062: Duplicate entry '3' for key 1
mysql> UPDATE pippo SET pos = pos + 1 WHERE pos >= 2 ORDER BY pos DESC;
Query OK, 2 rows affected (0.00 sec)
Leonardo (su un MySQL "vintage" 4.0.18-Max)


 o in basso andando a modificare il valore
Credo che in questo caso si tratti della gestione di una lista, quindi io
piuttosto metterei uno o due campi in piu` nel DB: "seguente" ed
eventualmente "precedente" in base alle necessita` di scorrimento che
avrai in seguito.
Cerca riferimenti sulle linked list o sulle doubly linked list.
E` un po' piu` complicato tenere aggiornato il DB (devi fare tre query
(+1) per ogni spostamento, inserimento o cancellazione) ma e` sicuramente
piu` leggero che scorrere tutto il DB alla ricerca di un buco.
Bye.

 Bye.
decisamente piu leggero


provo, ma penso che sia comunque molto pesante... grazie

provo, ma penso che sia comunque molto pesante...
Sulla mia macchina, una tabella con un milione di record viene esaminata
in circa due secondi... in realta' ce ne vorrebbero sei, ma ho impiegato
un piccolo trucco: quando l'intervallo e' abbastanza piccolo, calcolo il
trapezio per via diretta :-)
A scanso di equivoci ri-sottolineo che tutta la faccenda e' una tremenda
masturbazione mentale, perche' non e' cosi' che si fanno le cose; che se
hai un solo buco nella tabella, se la tabella e' abbastanza piccola (del
tipo, meno di mille elementi) puoi calcolare il trapezio subito, con UNA
sola query; e il metodo migliore e' quello che ti ha detto Alessandro.
<?php
        mysql_connect('localhost', 'guest', 'passuord');
        mysql_select_db('test');
        mysql_query('DROP TABLE IF EXISTS holey;');
        mysql_query('CREATE TABLE holey (position integer);');
        $time = time();
        for ($i = 0; $i < 10000; $i++)
        {
                $a = array();
                for ($j = 0; $j < 100; $j++)
                        $a[] = '(' . (100*$i + $j). ')';
                $k = implode(',', $a);
                mysql_query("INSERT INTO holey VALUES $k;");
        }
        $exec = mysql_query("SELECT MIN(position), MAX(position), COUNT(*)
FROM holey;");
        $val  = mysql_fetch_array($exec);
        mysql_free_result($exec);
        mysql_query("DELETE FROM holey WHERE position = 123456;");
        mysql_query("DELETE FROM holey WHERE position = 234567;");
        mysql_query("DELETE FROM holey WHERE position = 345678;");
        mysql_query("DELETE FROM holey WHERE position = 456789;");
        mysql_query("DELETE FROM holey WHERE position = 567890;");
        print "Creazione terminata in " . (time() - $time) . " secondi
($val[2] records)<br />";
        // Calcolo
        $exec   = mysql_query("SELECT MAX(position) FROM holey;");
        $varr   = mysql_fetch_array($exec);
        mysql_free_result($exec);
        $b      = $varr[0];
        $a      = 1;
        function esamina($a, $b)
        {
                $t = ($b*$b - $a*$a + $b + $a)/2;
                if ($t < 100000000)
                        $z = ", SUM(position) AS x";
                else
                {
                        $z = ", 0 AS x";
                        $t = 0;
                }
                $exec   = mysql_query("SELECT COUNT(*) $z FROM holey WHERE
position >= $a AND position <= $b;");
                $varr   = mysql_fetch_array($exec);
                mysql_free_result($exec);
                $buchi  = $b - $a - $varr[0] + 1;
                print "Fra $a e $b ci sono $buchi buchi<br />\n";
                return array($buchi, $t - $varr[1]);
        }
        while($b != $a)
        {
                $c        = (int)(($a + $b - 1) / 2);
                $expected = $c - $a + 1;
                list($buchi, $trapezio) = esamina($a, $c);
                if ($buchi)
                {
                        if ((1 == $buchi) && ($trapezio != 0))
                        {
                                $b = $trapezio;
                                break;
                        }
                        print "Quindi proseguo a sx fra $a e $c.<br />\n";
                        $b = $c;
                        continue;
                }
                $c++;
                list($buchi, $trapezio) = esamina($c, $b);
                if ($buchi)
                {
                        if ((1 == $buchi) && ($trapezio != 0))
                        {
                                $b = $trapezio;
                                break;
                        }
                        print "Quindi proseguo a dx fra $c e $b.<br />\n";
                        $a = $c;
                        continue;
                }
                print "Non ci sono buchi fra $a e $b;<br />\n";
                // Non ci sono buchi fra $a e $b. Ma siccome almeno
                // un buco deve esistere:
                $b++;
                break;
        }
        print "Il primo buco e' alla posizione $b<br />\n";
?>


E farlo impostando un cookye?? ;)
Oppure un campo nella tabella dei dati dell'utente...serializzi un array
che contiene l'ordine scelto e lo deserializzi in lettura...
snello, semplice e modulare...
;)
Alessandro Pellizzari , in data 15/11/2006 11:31 :


 snello, semplice e modulare...
Credo che ti stia pensando in piccolo. :)
Finche` hai 30-40 news puo` anche andare bene.
Quando inizi ad averne 10.000 nel DB, passi qualcosa come 100 KB di dati
ad ogni pagina aperta, se metti tutto in un cookie (che tra l'altro e`
limitato in grandezza proprio per questi motivi).
Serializzare un array (su DB o su file) e` piu` fattibile, ma ricadi nello
stesso problema: l'ordinamento dell'array.
Bye.

 


 recuperarti un esempio va...
Se devi salvare la posizione di UNA news non c'e` problema.
Il problema del poster e` di ordinare un intero database di news con un
ordine specifico, non basato sulla data di inserimento della news.
E in questo caso devi avere un array ordinato con gli id delle news.
Bye.

E io che ho detto??
:o
Alessandro Pellizzari , in data 20/11/2006 12:25 :
