Newsgroup: it.comp.software.database
Titolo: [Firebird] Ricreare tutti gli indici

Salve a tutti,
mi è capitato per la prima volta che un indice di una tabella, avesse
"perso efficacia".
In pratica da una query da 8sec impiegava diversi minuti.
L'ho ripristinato facendo:
ALTER INDEX "NOME" SET INACTIVE;
ALTER INDEX "NOME" SET ACTIVE;
Per fare il rebuild di tutti gli indici in un colpo solo, mi domandavo se
potevo sfruttare le tabelle di sistema con questi due comandi:
(escludo indici delle PK e delle FK)
UPDATE RDB$INDICES SET RDB$INDEX_INACTIVE=1
WHERE COALESCE(RDB$UNIQUE_FLAG,0)<>1 AND RDB$FOREIGN_KEY IS NULL AND
RDB$SYSTEM_FLAG IS NULL;
UPDATE RDB$INDICES SET RDB$INDEX_INACTIVE=NULL
WHERE COALESCE(RDB$UNIQUE_FLAG,0)<>1 AND RDB$FOREIGN_KEY IS NULL AND
RDB$SYSTEM_FLAG IS NULL;
Ho testato e funziona (la query è tornata "rapida"), ma mi domandavo se
agire su tali tabelle potesse dare problemi.
Grazie

 Grazie
In genere non è buono agire sulle tabelle di sistema.
Andrebbe specificato che database utilizzi.
In sql server dovresti vedere DBCC DBREINDEX
e DBCC INDEXDEFRAG
Lucazeo

 e DBCC INDEXDEFRAG
L'avevo scritto in intestazione :)
Il db č Firebird.
Comunque userņ tale tabella per avere i nomi degli indici, e poi ciclo
sulla lista e faccio INACTIVE / ACTIVE per ognuno singolarmente.

 sulla lista e faccio INACTIVE / ACTIVE per ognuno singolarmente.
La cosa che dovresti controllare e` se puoi farlo via stored procedure,
altrimenti devi usare uno script come questo:
#########################
import kinterbasdb
# Parametri di connessione (da impostare a discrezione dell'utente)
db_name=""
hostname="localhost"
username="sysdba"
pwd="masterkey"
def reindex():
    query_index=("SELECT RDB$INDEX_NAME FROM RDB$INDICES " +
                "WHERE COALESCE(RDB$UNIQUE_FLAG,0)<>1 " +
                "AND RDB$FOREIGN_KEY IS NULL " +
                "AND RDB$SYSTEM_FLAG<>1")
    con=kinterbasdb.connect(host=hostname, database=db_name,
                            user=username, password=pwd)
    cur=con.cursor()
    cur.execute(query_index)
    all_index=cur.fetchall()
    for index_name in all_index:
        print "Reindex " + index_name[0].strip()
        #cur.execute("ALTER INDEX " + index_name[0].strip() + " INACTIVE")
        #cur.execute("ALTER INDEX " + index_name[0].strip() + " ACTIVE")
        cur.execute("SET STATISTIC INDEX " + index_name[0].strip())
    cur.close()
    con.close()
if __name__ == "__main__":
    reindex()
#########################
Come puoi notare, le righe che definiscono l'ALTER INDEX sono
commentate, in quanto molto probabilmente il tuo problema e` dovuto alle
statistiche degli indici che sono sballate (una situazione del genere e`
dovuta ad una intensa modifica del numero di record presenti nella
tabella). Lo script e` in Python e utilizza il modulo kinterbasdb per
connettersi al database
Enrico

 connettersi al database
Capito, praticamente è quello che volevo fare in VB6, unica cosa non avevo
pensato alle statistic.
Volendo posso fargli far tutto, e farglielo lanciare al cliente un paio di
volte l'anno. Trall'altro è capitato ad il cliente che forse ha più dati
nel DB, prima non era mai successo.
PS: ti sai spiegare come mai la seconda query è molto più veloce? Premetto
che i campi sono indicizzati. A logica con la Join invece del SELECT IN
dovrebbe andare molto meglio no?
Questa è lenta (8secondi):
UPDATE FCLIFOR TA SET TA.TOTDARE=TA.TOTDARE +
(SELECT SUM(C.IMPORTO)
  FROM FCONTABRCONT C 
  WHERE C.DARECLIFOR=TA.CODCLIFOR AND
        C.CODCONTAB IN
        (SELECT CONT.IDCONTAB 
         FROM FCONTAB CONT 
         WHERE CONT.IDCONTAB BETWEEN '1' AND '9976' AND CONT.DATAREG 
               BETWEEN '01-01-2007' AND '06-30-2007')
);
Questa è rapida (0.05 secondi):
UPDATE FCLIFOR TA SET TA.TOTDARE=TA.TOTDARE +
(SELECT SUM(C.IMPORTO)
  FROM FCONTABRCONT C 
  WHERE C.DARECLIFOR=TA.CODCLIFOR AND
        C.CODCONTAB IN
        (SELECT CONT.IDCONTAB 
         FROM FCONTAB CONT 
         WHERE CONT.IDCONTAB BETWEEN '1' AND '9976' AND CONT.DATAREG 
               BETWEEN '01-01-2007' AND '06-30-2007')
);
Grazie.

 Questa è rapida (0.05 secondi)
UPDATE FCLIFOR TA SET TA.TOTDARE=TA.TOTDARE +
(SELECT SUM(C.IMPORTO)
  FROM FCONTABRCONT C 
  WHERE C.DARECLIFOR=TA.CODCLIFOR AND
        C.CODCONTAB IN
        (SELECT CONT.IDCONTAB 
         FROM FCONTAB CONT 
         WHERE CONT.IDCONTAB BETWEEN '1' AND '9976' AND CONT.DATAREG 
               BETWEEN '01-01-2007' AND '06-30-2007')
);
Tnx

 Ho incollato due volte la stessa :)
Me n'ero accorto :)
Controllando le due query, l'unica cosa che mi viene in mente da dirti
e` che nell'inner join esplicita (la prima query) il planner non riesce
ad accedere correttamente agli indici, mentre nella seconda si
(probabilmente la causa e` da imputare ai BETWEEN). DA questo punto di
vista, ricordo perfettamente che Flamerobin permette un explain grafico
della query, che a questo punto ti consiglio di utilizzare
Enrico

 della query, che a questo punto ti consiglio di utilizzare
Controllerň la plan, in ogni caso io penso sia anche qualcosa del tipo:
Facendo la INNER JOIN deve tirar su molti piů record (testa*righi) e poi
applica i filtri, invece nella seconda filtra direttamente la testa con
molti meno record, e poi applicando la IN lo fa molto velocemente.

 Controllerň la plan
Ho isolato la parte select.
Quella lenta č questa:
PLAN JOIN (CONT INDEX (IDX_FCONTAB_1),C INDEX (IDX_FCONTABRCONT_1))
Quella veloce č questa:
PLAN (CONT INDEX (IDX_FCONTAB_1))
PLAN (C NATURAL)
Da notare che eseguendo solo la parte select (quindi senza  WHERE
C.DARECLIFOR=TA.CODCLIFOR) sono entrambe istantanee.
Vabbč, per ora ho risolto, ed anche 8 sec. mi andrebbero bene (comuqnue ho
adottato quella piů rapida)

 Ho isolato la parte select.
Ho fatto una prova con fb 2.1.0.15999
La lenta impiega 1 sec. invece di 8... :)