Newsgroup: it.comp.lang.visual-basic
Titolo: Ottimizzare numerose select di seguito....

Salve a tutti,
devo fare una routine di elaborazione che scansiona una tabella, e va ad
aggiornare dei progressivi in una seconda tabella.
In questa seconda tabella a seconda dei casi devo fare una insert o una
update.
TAB1
  CONTATORE
  CODICECLI
TAB2
  CODICECLI
  PROGRESSIVO
Quindi nella mia routine dovrei fare (un po' più complesso ma rende l'idea)
una sequenza di molte select.
Rs.source="SELECT * FROM TAB1"
rs.open
Do until Rs.eof
  Rs2="SELECT CODICECLI FROM TAB2 WHERE CODICECLI=" rs("CODICECLI")
  rs2.open
  if rs.eof then
    'insert
  else
    'update
  end if
  rs2.close
  rs.movenext
loop
In questi casi cosa è meglio fare? Assegnare Rec2 all'inizio come SELECT *
FROM TAB2 e poi usare la .Filter (e fare un requery ad ogni insert), oppure
fare ogni volta una select mirata?
Se non fosse stato necessario fare un requery immagino che la soluzione con
la .filter sarebbe migliore (magari con cursore aduseclient per spostare
tutto il recset in ram)
Grazie a tutti
-- 
DBMS Firebird
VB6 + Ado

Andrea scriveva il 13/06/2006 :

Tutto dipende se le informazioni che vanno in TAB2 sono calcolate man 
mano, o se è possibile definire tutto preliminarmente.
Se, ad esempio, il concetto fosse "Inserisco in TAB2 i record mancanti 
e aggiorno gli esistenti con i dati presi da TAB1", allora tutto si 
farebbe con statement SQL, con performance elevatissime e a prova di 
casino in caso di interruzione dell'attività a metà (e senza dover 
aprire una transazione dal client).
Se invece la cosa è "Inserisco o aggiorno TAB2, in funzione di ciò che 
ho fatto prima, ossia con dati elaborati dal client man mano", ci sono 
poche soluzioni rispetto a quello che tu hai già fatto.
Se ci dai altre informazioni, vediamo.
Ciao
Alberto
-- 
A questo punto hanno trovato un muro,
un muro duro, molto molto duro,
che siamo noi.

 Se ci dai altre informazioni, vediamo.
In questo caso mi è venuto in mente anche un secondo metodo, se riesco a
gestire i duplicati.
Mi bastarebbe fare select tab1.*,tab2.codclifor FROM TAB1 LEFT JOIN TAB2 on
TAB1.codcli=TAB2.CODCLI
In questo caso quando ho Null su tab2 sono da inserire.
Devo però vedere casi in cui in tab1 ho
ID=1 CODICECLI=1'inserito
ID=2 CODICECLI=2'inserito
ID=3 CODICECLI=1'modificato e non inserito
Ma forse riesco a prendermi tutto con un SUM() ed ottenere un rigo per ogni
cliente.
Grazie


 Stored procedure quindi? Ancora non ho fatto il grande passo :)
Volendo, anche stored procedure, certo. Ma io mi riferivo a 
normalissimi statement SQL. Cioè, tu fai un Insert quando un record non 
esiste, giusto?
Beh, allora la cosa sarebbe (è un esempio):
INSERT TAB2 (....<colonne>...)
SELECT ...<valori>...
FROM (SELECT ID_CLIENTE, MAX(VALORE) AS VALORE, ....
     FROM TAB2
     GROUP BY ID_CLIENTE)
     AS TAB2_TMP
LEFT OUTER JOIN TAB2
ON TAB1.ID_CLIENTE = TAB2_TMP.ID_CLIENTE
WHERE TAB2.ID_CLIENTE IS NULL
UPDATE TAB2
SET CAMPO1=TAB2_TMP.VALORE
FROM (SELECT ID_CLIENTE, MAX(VALORE) AS VALORE, ....
     FROM TAB2
     GROUP BY ID_CLIENTE)
     AS TAB2_TMP
LEFT OUTER JOIN TAB2
ON TAB1.ID_CLIENTE = TAB2_TMP.ID_CLIENTE
WHERE TAB2.ID_CLIENTE IS NOT NULL
Lo metti tutto in un normale Adodb.Command.CommandText e lo esegui.


Nel suo scritto precedente, Albe V° ha sostenuto :

Ho invertito un paio di volte TAB1 con TAB2, ma il concetto è quello.


 Lo metti tutto in un normale Adodb.Command.CommandText e lo esegui.
Grazie ora me lo studio, anche se temo che Firebird non lo digerirà molto
bene ^_^
Per ora ho scritto la routine, dato che sono riuscito a tirarmi fuori un
record per ogni cliente, ho usato la filter e le prestazioni sono buone
(3/4secondi)


 (3/4secondi)
3/4 secondi sono un tempo immenso.
Ma veramente immenso.
Ovviamente dipenderà dal server, ma un tempo del genere lo concepisco 
per far girare una cosa come quella da me postata, per tabelle attorno 
ai 200.000 record su un pc ordinario.
Se ne hai meno, c'è qualcosa che non va. O negli indici (mi sembra che 
anche in passato in questo NG ci fossero stati problemi di performance 
con Firebird per indicizzazioni fatte senza particolare cura), o negli 
statement utilizzati.
Oppure hai un server a carbonella.


 Oppure hai un server a carbonella.
Bhe diciamo che ho i seguenti rallentamenti:
1) Diverse tabelle non indicizzate per alcun campo
2) PC: P3 500 anche se ora sono in locale e non in rete.
Comunque ora provo ad adattare la tua stringa :)


 e funziona :)
Allora la insert completa mi funziona.
La Update no, non riconosce il FROM dopo
UPDATE TAB2
SET CAMPO1=TAB2_TMP.VALORE
*FROM (SELECT ID_CLIENTE, MAX(VALORE) AS VALORE, ....
Ho fatto diverse prove ma nada.
Comunque per ora almeno nella insert la utilizzo.
Tnx
PS: OpenStatic e ForwardOnly sembra abbiano le stesse prestazioni, immagino
che entrambe spostino in ram il recset.


 *FROM (SELECT ID_CLIENTE, MAX(VALORE) AS VALORE, ....
Potrebbe avere una sintassi simile a quella di MySQL:
UPDATE Tab1 [LEFT|RIGHT|INNER] JOIN Tab2
ON Tab1.CampoX = Tab2.CampoX
SET  Tab2.CampoY = ...
Comunque potresti anche dare un'okkiata a qualche manuale su internet,
riguardo la sintassi di firebird.
E un suggerimento: EVITA per quanto possibile cicli di cicli di cicli
di aperture di recordset per aggiornare tabelle di tabelle.
Il linguaggio SQL è nato proprio per inviare al database un comando
unico (una bella query più o meno grossa) che dica al database cosa
fare con i dati, come aggiornarli e/o eliminarli.
E' il database che deve lavorare con cicli di cicli di cicli, e
sicuramente lo farà meglio dei tuoi while/for/loop annidati, dato che
è *progettato* per fare quei loop. La tua applicazione, per quanto
ottimizzata, non potrà mai fare di meglio.
Il ciclo enorme che tu fai girare in 3/4 secondi il database lo mastica
in millesimi di secondo...

 in millesimi di secondo...
Ho visto infatti che quella insert è istantanea praticamente, la update
gestita a mano impiega quel paio di secondi, ma soprattutto manda n comandi
di insert/update ecc...


 di insert/update ecc...
Non solo.
Quella istruzione SQL è transazionale, mentre il tuo loop va 
assolutamente reso transazionale a manina, e, con quei tempi, una 
transazione aperta è un disastro.


 transazione aperta č un disastro.
Questo č vero.
Fortunatamente il 90% dei casi useranno la insert, inoltre č una tabella
non dico temporanea, ma gestita quasi in quel modo.
Tnx


 dati che stanno più o meno tutti sul DB.
Allora si può fare in Sql, garantito.


 Allora si può fare in Sql, garantito.
Ma allora ai client non rimane nulla da fare :)
In futuro vedrò di orientarmi maggiornemente in maniera SQL


 SET  Tab2.CampoY = ...
Niente non vuole proprio il comando join dopo Update Tab1.
Vabbč, grazie lo stesso :)


Allora... la documentazione un po' la trovi a questo indirizzo:
http://www.ibphoenix.com/downloads/60LangRef.zip
e, leggendola, ho notato che la UPDATE non supporta alcun tipo di join:
UPDATE {table | view}
SET col = <val> [, col = <val> ...]
[WHERE <search_condition>
e quindi nisba.
La SELECT invece supporta i JOIN:
SELECT [TRANSACTION transaction]
[DISTINCT | ALL]
{* | <val> [, <val> ...]}
[INTO :var [, :var ...]]
FROM <tableref> [, <tableref> ...]
[WHERE <search_condition>]
[GROUP BY col [COLLATE collation] [, col [COLLATE collation] ...]
dove
<tableref> = <joined_table> | table | view | procedure
[(<val> [, <val> ...])] [alias]
<joined_table> = <tableref> <join_type> JOIN <tableref>
ON <search_condition> | (<joined_table>)

 e quindi nisba.
Ti ringrazio, la insert infatti mi funge.
Comunque mi sono scaricato quel pdf, pare ottimo.
Peccato sia aggiornata ad interbase, Firebird ha introdotto nuove parole
chiave. Comunque è una buona partenza
Tnx
