Newsgroup: it.comp.www.php
Titolo: Query ricorsive o singola query con elaborazione PHP?

Oggetto un po 'stupido'.
Supponiamo di avere una tabella con milioni e milioni di record.
+------------------------+
| id | servizio | prezzo |
+------------------------+
Ciascun ID identifica un utente.
Ogni utente può avere un parent (la relazione è in una tabella esterna)
Ho bisogno di prendere, per un dato utente, ad esempio ID = 345345, il
prezzo di uno o più servizi. Fin qui tutto ok, una normale select.
Supponiamo che l'utente non abbia inserito un tariffario per quel dato
prodotto. In questo caso, devo 'ereditare' quello del suo parent.
Se anche il parent non avesse un tariffario, devo ereditare quello del
parent del parent e così via, fino ad arrivare ad un tariffario fisso
da me impostato.
Ora, conviene prendere *tutti* i record della tabella (che potrebbero
essere milioni) e poi fare un brutale foreach in php sovrascrivendo il
tariffario man mano che ci sono dei dati, oppure conviene fare magari
150 query nel caso il tariffario richiesto fosse quello di un utente
al 150esimo livello gerarchico? (nel senso, prendo il suo tariffario,
non lo trovo, faccio la query per il parent, non lo trovo, faccio
query per il parent del parent e così via)
Che dite?
-- 
Non tocca a noi dominare tutte le maree del mondo,
il nostro compito è di fare il possibile per la
salvezza degli anni nei quali viviamo,
sradicando il male dai campi che conosciamo.


non scrivi 150 query ma al max un paio.
io farei una funzione ricorsiva, che prenda in caso di mancato 
tariffario l'id del parent e richiama se stessa.
prendi questa function come esempio (sarà scritta alla pene di segugio :) )
function tariffario($id)
{
    $select="select servizio , prezzo from tariffario where id = " .$id;
    $result=mysql_query($select);
    //se il risultato della query è null (non ci sono tariffe)
    // allora prendo il parent
    if $result is null
       $select="select parent from tabella_parent where id_figlio =".$id;
       $result=mysql_query($select);
       //richiamo la funzione stessa passando come id il nuovo parent.
       tariffario($result[parent]);
    else
	// sel esiste il tariffario ritorno il prezzo ed esco.
	return prezzo;
    end if;
}
Ciao


Volendo la ricorsione si può incapsulare dentro una stored procedure.
La sintassi può diventare complicata però e anche debug e manutenzione. 
Credo sia cmq abbastanza più performante rispetto a un ciclo for ricorsivo
Magari si potrebbe pensare a dei prepared statement così almeno si 
limita il payload superfluo.
Io personalmente userei un ciclo come bramante.
Marco

 tariffario l'id del parent e richiama se stessa.
Ok, ma se al 150esimo livello nessuno ha un tariffario impostato
andando a ritroso eseguo 150 query finchè non arrivo al  root.


 tariffario l'id del parent e richiama se stessa.
Credo di avere qualche problema.
E' tutto ieri che provo.
Ho un oggetto di nome Model_Price che estende ORM (di Kohana)
dentro ho fatto una funziona di nome "_getByUser($user_id, $arrUtenti)"
che richiede lo userid di cui prendere il listino, e un array con tutta
la mappatura userid<>parentid
prendo il listino prezzi per userid.
verifico che sian stati ritornati dei dati (banalmente verifico che lo
userid ritornato dalla query sia lo stesso che ho richiesto io, se
manca, vuol dire che la query non ha ritornato record)
se son presenti dati, faccio un bel return (e funziona)
se mancano i dati richiamo la funzione usando come id quello del parent:
$this->_getByUser($parent_id, $arrUsers);
ma mi ritrovo con una pagina vuota, nessun errore PHP o altro.
Semplicemente non funziona.


Che dite?
Che non si può rispondere senza una stima quantitativa di quanti record
hanno un tariffario.
Supponiamo che ce l'abbiano tutti: una query ricorsiva è vantaggiosa in
quanto non fai mai 150 query, ne fai una sola.
Supponiamo che solo l'ID padre ce l'abbia: la query ricorsiva t'ammazza
le prestazioni perche' ogni volta ne fai 150.
Nei casi intermedi ti *potrebbe* convenire magari fare una join ridotta
e ogni volta prendere l'oggetto e il suo padre (e forse il nonno). Così
la probabilità di trovare la tariffa aumenta; bisogna vedere se aumenta
più o meno dell'aumento di costo causato dalla query più complessa, che
a sua volta dipende anche da come sono messi i dati sul disco.
Inoltre puoi mettere in join solo "N" tabelle, con N pari al numero dei
livelli completi partendo dal padre (altrimenti, rischi di generare una
query per un livello anteriore all'ID radice).
Credo che l'unica sia sperimentare.
Leonardo
-- 
Enough, enough!  But, stranger, ere we part,
Glancing farewell to each nefarious bier,
This warning would I beg you take to heart:
There is an end to e'en the worst career!

 le prestazioni perche' ogni volta ne fai 150.
Ho dimenticato di dire, che devo partire obbligatoriamente dal root
e poi discendere, non posso andare a ritroso.
Perchč?
Semplice: un listino č composto da piů record. Non č detto che il figlio
abbia impostato tutti i record del suo listino, potrebbe ad esempio
averne impostati solo 3 su 5.
i 2 mancanti, li devo ereditare dal padre.
Quindi pensavo di prendere di default il listino root, poi scendere
andando via via a sovrascrivere eventuali listini non null con quelli
degli utenti elaborati in quel momento.
Cosě facendo, quando arrivo alla fine della catena (ovvero all'utente
che a me interessa), avrň tutti i listini ereditati da quello subito
sopra.
-- 
Non tocca a noi dominare tutte le maree del mondo,
il nostro compito č di fare il possibile per la


 
posta il codice, se come penso hai creato una funzione ricorsiva
c'è sicuramente un intoppo da qualche parte, controlla anche la
classe padre (ORM) che estendi e controllo che magari non faccia
qualcosa alla sua inizializzazione/chiama.
Ciao
-- 
Postato da Virgilio Newsgroup: lo usi da web ma con le funzioni del newsreader 
http://newsgroup.virgilio.it
Gerarchie it, italia, it-alt, tin, it.binari. Unico!

 qualcosa alla sua inizializzazione/chiama.
Non posso postare il codice, ormai ho modificato un po tutto
ed ora sembra funzionare.
Per quanto riguarda il constructor, non penso possa essere stato
il problema. il constructor viene chiamato alla prima esecuzione,
che nel mio caso va a buon fine. I richiami interni non impattano
sul constructor ed io non sto reinstanziando l'oggetto tutte le
volte, ma semplicemente richiamo un suo metodo ricorsivamente.


 
non penso che ci siano altre soluzioni, anche se fai il
contrario, cioè parti da root ed estendi i figli, se è il 150
figlio comunque esegui 150 query.
se la query è banale eseguirne 150/200 e non c'è concorrenza nel
dbms non è così pesante, discorso diverso se la query è
complessa, non utilizzi indici/chiavi primarie, c'è parecchia
concorrenza nel dbms, allora rischi di far sedere il tutto.
altre soluzioni sono quelle di cachare il tutto se e solo se tale
accesso avviene più volte per gli stessi dati (velocizze agli
accessi successivi).
o creare un tabella che "gia contiene" questi dati e che viene
popolata in fasi di meno stress (notturni o a orari fuori
utilizzo del dbms), questo se i dati hanno una variazione non
immediata.
(non è che le tariffe cambiano ogni istante della giornata).
potresti anche utilizzare i trigger del dbms, in modo tale che
quando si crea una nuova tariffa automaticamente venga popolata
la "tabella contenitore" con le varie parti (tariffe $id ed
eventuali tariffe $parent).
di soluzioni c'è ne sono diverse a secondo del contesto, se i
dati cambiano repentinamente (insert/update/delete) più volte al
giorno e gli accessi sono sempre per parametri diversi, forse le
soluzioni sopracitate sono da escludere, si dovrebbe ragionare
sull'architettura dei dati e cercare di normalizzare il tutto.


 contrario, cioè parti da root ed estendi i figli, se è il 150
Non ne vengo a capo senza fare cagate pazzesche.
La situazione è questa:
tabella "standardprices":
servizio, prezzo
tabella "users":
id, parent_id, username
tabella "prices":
userid, servizio, prezzo
Quello che vorrei fare è:
per ogni utente, ad esempio "userid = 1234" prendere il suo
listino prezzi per intero. Mi sta bene prenderlo tutto ogni
volta e poi filtrare l'array risultate a livello di PHP. Non
è fondamentale mettere un WHERE sulla query, ricavare 20
record composti da un indice ed un FLOAT o solo 1 prestazionalmente
non fa molta differenza.
Se l'utente non ha un listino prezzi, lo devo ereditare dal
suo parentid (visibile in tabella "users")
Se anche il parentid non ha un listino, devo ereditare quello
del parent del parent (ovvero il nonno).
Così via, fino ad arrivare al primo utente che ha parentid = 0
o parentid = id (ovvero: un utente di primissimo livello).
Se anche questo non ha un listino, devo ereditare quello standard
presente in "standardprices".
Si tratta quindi di fare una pazzesca query ricorsiva all'indietro
per un numero indefinito di passaggi.
Il problema, però, nasce da un fatto:
supponiamo di avere 20 servizi. In questa catena, un utente potrebbe
aver impostato solo 19 prezzi, e non averne volutamente impostato uno.
In tal caso, con una query ricorsiva tradizionale, mi ritroverei
un listino "bucato" ovvero con 19 servizi ed il ventesimo non ereditato.
Come posso fare? Non nascondo una cosa: non ne ho la più pallida idea.
Non son capace a fare una struttura del genere.


 Non son capace a fare una struttura del genere.
nessuno mi aiuta? Non riesco a venirne a capo.
-- 
Non tocca a noi dominare tutte le maree del mondo,
il nostro compito ï¿œ di fare il possibile per la
