Alleggerire estrazione dati da tabella

Salve lista.
Ritorno su un’applicazione di cui avevo già parlato tempo fa per
chiedere aiuto su altri aspetti.

Tanto per capire un po’ cos’è 'sta roba: ho un elenco di circa 20000
elementi gestiti da degli operatori (una decina); per semplicità le
caratteristiche che l’operatore modifica sono salvate nella tabella
dell’elemento, quindi la tabella degli elementi è una cosa del genere:
tabella objects (quella degli elementi in questione):

|id|user_id|nomeoggetto|caratteristica1|caratt2|caratt3|…|carattn|

con legame 1 a molti tra la tabella users e objects.

Devo visualizzare delle statistiche di lavoro e in una delle quali devo
mostrare quanti oggetti ha gestito ogni operatore e divisi oper
caratteristica, quindi ho fatto una tabella del genere:

|--------------| oggetti |
|nome_operatore|caratt1|caratt2|caratt3|…|carattn|totale|
|operatore1 | 10 | 23 | 131 |…| 3422 | 5335 |
|operatore2 | 345 | 76 | 678 |…| 4321 | 6742 |

La costruisco facendo nella vista una cosa del genere:

<% for user in @user %>

<%= user.login %> <% car1 = Objects.count(:conditions => "caratt1= '1'and user.id = '#{user.id}'") %> <% car2 = Objects.count(:conditions => "caratt2= '1'and user.id = '#{user.id}'") %> ecc.

(sì, i campi sono quasi tutti booleani, o se non lo sono comunque mi
serve la quantità degli oggetti con quella caratteristica)

Prima al posto del count direttamente sulla tabella facevo così:

<% car1 = @objects.map{|c| c if c.caratt1 == true c.user.id ==
user.id}.compact.length %>
ecc.

in cui @objects era definito nel controller così: @objects =
Object.find(:all)

Tra i due sistemi il primo mi genera la pagina in circa 8 secondi,
mentre i secondo dai 18 ai 20.

C’è un sistema più veloce e leggero per fare questa estrazione? E’ un
po’ una seccatura trovarsi con la macchina che fa da serverino che
rallenta di brutto ogni volta che il capogruppo va a vedersi le
statistiche…

Grazie e a presto :slight_smile:

Il 05 ottobre 2009 14.13, Daneel O. [email protected] ha
scritto:

<% for user in @user %>

<%= user.login %> <% car1 = Objects.count(:conditions => "caratt1= '1'and user.id = '#{user.id}'") %> <% car2 = Objects.count(:conditions => "caratt2= '1'and user.id = '#{user.id}'") %>

il problema più grosso, qui, è che stai facendo una query alla volta
(e ogni volta reinizi la connessione col db, mandi la query, aspetti
il risultato e lo converti).

dovresti cercare di fare meno query. se stai usando mysql potresti fare
così:
@user = User.find_by_sql [“select , (select count() from objects
where objects.user_id = users.id and caratt1=?) as car1, (select
count(*) from objects where objects.user_id = users.id and caratt2=?)
as car2) from users”, ‘1’, ‘1’]

poi puoi scrivere:

<% for user in @user %>
<%= user.login %>
<%= user[‘car1’] %>
<%= user[‘car2’] %>
<% end %>

in questa maniera ti colleghi al db una volta sola.

NOTA: ho provato qui da me ma con nomi diversi; riportando i nomi che
hai usato tu potrei aver fatto errori di battitura e comunque non sono
sicuro di aver capito cos’è “1” in “caratt1=‘1’”…

poi i dialetti sql cambiano, ma una cosa simile dovresti poterla fare
per ogni dbms (anche se ti tocca fare query specifiche).

Pietro G. wrote:

Il 05 ottobre 2009 14.13, Daneel O. [email protected] ha
scritto:

<% for user in @user %>
�<%= user.login %>
�<% car1 = Objects.count(:conditions => “caratt1= '1’and user.id =
‘#{user.id}’”) %>
�<% car2 = Objects.count(:conditions => “caratt2= '1’and user.id =
‘#{user.id}’”) %>

il problema pi� grosso, qui, � che stai facendo una query alla volta
(e ogni volta reinizi la connessione col db, mandi la query, aspetti
il risultato e lo converti).

E comunque questo sistema è molto più veloce che caricare tutta la
tabella in un’array e poi analizzarla, come dicevo prima.

dovresti cercare di fare meno query. se stai usando mysql potresti fare
cos�:
@user = User.find_by_sql [“select , (select count() from objects
where objects.user_id = users.id and caratt1=?) as car1, (select
count(*) from objects where objects.user_id = users.id and caratt2=?)
as car2) from users”, ‘1’, ‘1’]

Cioè fare un’unica query in linguaggio sql in cui conto tutto ciò che mi
serve… wow! si vede che sono alle prime armi, non c’ero arrivato :slight_smile:

poi puoi scrivere:

<% for user in @user %>
<%= user.login %>
<%= user[‘car1’] %>
<%= user[‘car2’] %>
<% end %>

in questa maniera ti colleghi al db una volta sola.

Proverò.

NOTA: ho provato qui da me ma con nomi diversi; riportando i nomi che
hai usato tu potrei aver fatto errori di battitura e comunque non sono
sicuro di aver capito cos’� “1” in “caratt1=‘1’”…

Beh, ho semplificato brutalmente il codice, quindi sicuramente qualche
boiata ci sarà .

Per quanto riguarda quel “caratt1=‘1’”, sta ad indicare che per
quell’oggetto la caratteristica booleana “caratt1” ha valore “vero”,
solo che se faccio una elaborazione in ruby posso definirlo
semplicemente con “==true”, mentre se gli passo il comando sql ho visto
che devo proprio dirgli “=‘1’”, altrimenti non funziona… almeno stando
alla mia ben poca esperienza.

Grazie.

Il 07 ottobre 2009 12.39, Daneel O. [email protected] ha
scritto:

Ah, c’è un errore dopo “car2” (che poi sarebbe lafine dell’ultimo
conteggio e passaggio alla variabile) non ci va la parentesi :slight_smile:

mannaggia! :stuck_out_tongue:

stringa… è normale?
Sì, è normale: il metodo find_by_sql è definito
così:
def find_by_sql(sql)
connection.select_all(sanitize_sql(sql), “#{name} Load”).collect! {
|record| instantiate(record) }
end

e tutti i metodi find usano find_by_sql.

Ad esempio, in un mio progetto c’è un modello Amount, un cui elemento
(trovato con Amount.first) è questo:
#<Amount id: 1, attachment_id: 174, tax_base: 139.58, vat_rate: 20.0,
vat: 27.92, created_at: “2008-11-07 15:58:56”, updated_at: “2008-11-07
15:58:56”>

dalla connection però arriva questo:

{“vat_rate”=>“20”, “updated_at”=>“2008-11-07 15:58:56”,
“attachment_id”=>“174”, “id”=>“1”, “vat”=>“27.92”,
“tax_base”=>“139.58”, “created_at”=>“2008-11-07 15:58:56”}

È poi il metodo instantiate che fa le conversioni necessarie,
basandosi sull’attributo columns, che contiene la definizione delle
varie colonne.

pietro

Pietro G. wrote:

dovresti cercare di fare meno query. se stai usando mysql potresti fare
cos�:
@user = User.find_by_sql [“select , (select count() from objects
where objects.user_id = users.id and caratt1=?) as car1, (select
count(*) from objects where objects.user_id = users.id and caratt2=?)
as car2) from users”, ‘1’, ‘1’]

Provato e funziona: sono sui 9 secondi per generare la pagina.
Ah, c’è un errore dopo “car2” (che poi sarebbe lafine dell’ultimo
conteggio e passaggio alla variabile) non ci va la parentesi :slight_smile:

poi puoi scrivere:

<% for user in @user %>
<%= user.login %>
<%= user[‘car1’] %>
<%= user[‘car2’] %>
<% end %>

Curiosità : dovendo anche generare i totali delle varie caratteristiche,
io ho una cosa del genere:

<% for user in @user %>
<%= user.login %>
<%= user[‘car1’] %>
<% car1tot += user[‘car1’].to_i %>
<%= user[‘car2’] %>
<% car2tot += user[‘car2’].to_i %>
<% end %>

Ho dovuto aggiungere il “to_i” perché il dato così com’è è visto come
stringa… è normale?