Codice troppo lento

di il
13 risposte

Codice troppo lento

Ciao.... piccolo (o grande) problema:
ho una tabella con dei codici fiscali (univoci) ed una query (che prende i dati da un'altra tabella) univoca per cf, anno procedura e numero procedura.
Facendo un'altra query tra le due, ottengo un elenco con cf anno procedura e numero procedura.
Capita che un cf abbia più di una procedura e le devo raggruppare su un'unica riga (nella prima tabella dei cf univoci c'è un campo apposito):
cf annoproc-numproc / annoproc-numproc / annoproc-numproc etc

Ho provato a farlo via codice ma impiega 50 minuti solo per questa operazione e ne devo fare una decina.....
Si può ovviare?
Grazie mille

13 Risposte

  • Re: Codice troppo lento

    Posta l'SQL delle query e il codice che hai scritto e magari si riesce a capire il problema. 50 minuti sembrano tanti…di quanti record stiamo parlando?

  • Re: Codice troppo lento

    29/09/2023 - silverado60 ha scritto:


    ho una tabella con dei codici fiscali (univoci) ed una query (che prende i dati da un'altra tabella) univoca per cf, anno procedura e numero procedura.

    In che senso?

    Hai una tabella che contiene solo codice fiscale?

    Una query che prende i dati da una tabella che ha anche il codice fiscale? Mi chiedo: la tabella con solo codice fiscale che ruolo ha in questa query?

    29/09/2023 - silverado60 ha scritto:


    Facendo un'altra query tra le due, ottengo un elenco con cf anno procedura e numero procedura.

    Hai già una query e ne fai un'altra prendendo la tabella con cf che è presente anche nell'altra tabella?

    Mi dici nomi e campi delle tabelle? Eventuali relazioni?

    29/09/2023 - silverado60 ha scritto:


    Capita che un cf abbia più di una procedura e le devo raggruppare su un'unica riga (nella prima tabella dei cf univoci c'è un campo apposito):

    Puoi raggruppare per cf.

    Ma che significa campo apposito?

  • Re: Codice troppo lento

    Campo apposito = campo di testo dove scrivo annoproc - numeroproc.

    Riepilogo forse con più chiarezza:

    ho un tabellone di 460000 righe, dove i cf si ripetono ed anche i campi anno procedura e numero procedura, perchè cambiano poi altri campi.

    Creo la tabella CF_UNIVOCI da questo tabellone (cfis, procedure)

    Poi Creo la query, sempre sul tabellone 

    SELECT DISTINCT  PGM.cfis, PGM.DANNFAS, PGM.NFAS, PGM.CATTAZI 
    
    FROM PGM WHERE (((PGM.CATTAZI)="142"))

    con questa query ho l'elenco univoco delle procedure di un codice fiscale (che come detto, possono essere più di una per cf)

    utilizzo questo codice per raggruppare le procedure per codice fiscale:

    Dim rss2 As Recordset 
    
    Dim rss3 As Recordset
    
    txt = ""
    
    Set rss2 = CurrentDb.OpenRecordset("Select * from CF_UNIVOCI")
    
    If rss2.RecordCount > 0 Then
    
    rss2.MoveFirst
    
    Do While Not rss2.EOF
    
    Set rss3 = CurrentDb.OpenRecordset("Select * from A_Q01 where  [Cfis] = '" & rss2.Fields("Cfis") & "'")
    
    If rss3.RecordCount > 0 Then
    
    rss3.MoveFirst
    
    Do While Not rss3.EOF
    
    txt = txt & " - " & rss3.Fields("NFAS") & "/" & rss3.Fields("DANNFAS")
    
    rss3.MoveNext
    
    Loop
    
    rss2.Edit
    
    rss2.Fields("procedure") = txt
    
    rss2.Update
    
    txt = ""
    
    End If
    
    rss2.MoveNext
    
    Loop
    
    End If

    Per eseguire questo codice l'ultima volta (pochi minuti fa) ha iniziato alle 14,33 e terminato alle 15,56 !!!!

    Chiedo se c'è un metodo di raggruppare le procedure (cioè: numero proc1 - annoproc1 / numproc2 - annoproc2 etc) direttamente sulla query evitando l'esecuzione del codice.

    Grazie

  • Re: Codice troppo lento

    Innanzitutto sarebbe meglio che mostrassi la struttura del db. Poi capire perché non usare una action query.

  • Re: Codice troppo lento

    29/09/2023 - silverado60 ha scritto:


    Per eseguire questo codice l'ultima volta (pochi minuti fa) ha iniziato alle 14,33 e terminato alle 15,56 !!!!

    Non puoi creare una query passando record dopo record da codice. È un suicidio.

    Fai come ti ha detto Antony73.

    29/09/2023 - Antony73 ha scritt

  • Re: Codice troppo lento

    29/09/2023 - silverado60 ha scritto:


    Per eseguire questo codice l'ultima volta (pochi minuti fa) ha iniziato alle 14,33 e terminato alle 15,56 !!!!

    Non puoi creare una query passando record dopo record da codice. È un suicidio.

    Fai come ti ha detto Antony73.

    29/09/2023 - Antony73 ha scritt

  • Re: Codice troppo lento

    29/09/2023 - silverado60 ha scritto:


    Campo apposito = campo di testo dove scrivo annoproc - numeroproc.

    Questo puoi ricavarlo tramite la stessa query.

  • Re: Codice troppo lento

    Sinceramente devo capire lo scopo del codice e lo scopo finale.

    Personalmente, con tutti quei cicli annidati mi è andato in pappa il cervello, anche perchè, da quello che ho capito alla conclusione di ogni ciclo, se trovi il valore giusto, riporti tutto all'inizio e ricominci a ciclare fino al successivo.
    Quindi stai eseguendo un codice che crea il fattoriale di 460.000 record. Praticamente quello che un matematico definirebbe come tendente all'infinito.
    E considerando solo una coppia di dati da confrontare, stai facendo questa quantità di cicli e confronti:

    105.570.000.000 di cicli e confronti.

    Un codice simile si scriveva prima dei database relazionali, o sui database (sono rimasti pochi) che usano file di testo per archiviare i dati.

    Quindi presubilmente, deduco che non hai una chiave primaria in nessuna tabella e nessuna relazione tra le due. Altrimenti access lo farebbe da solo in pochi secondi.

    Quindi sei costretto ad aprire due istanze openrecordset sulle due tabelle distinte e ciclare record per record, campo per campo, entrambe per verificare la corretta correlazione tra i dati presenti sulla prima e sulla seconda.

    Comunque senza conoscere la struttura del database non è facile dare un aiuto.

    Ma se si trattasse di un database normalizzato, basterebbe una query semplice con la condizione where sul campo che ti interessa ricercare, ed al massimo applicare un filter ai dati della query.

    Oppure, con la soluzione della serva, usare una query di ricerca duplicati.

  • Re: Codice troppo lento

    Non ho le competenze per mettere in discussione il tuo approccio ma credo che ci siano soluzioni più furbe

    Guardando il tuo codice intanto potresti eliminare i recordCount e i movefirst che non servono a niente se non a rallentare l'esecuzione; perchè ad ogni Set rss = CurrentDb.OpenRecordset riparti dal primo record e in caso di RecordCount=0 salti il loop.

    io ciclerei direttamente il tabellone ordinato per CF per costruirmi la stringa delle procedure e aggiornerei la tabella CFUnivoci quando pronta la stringa

    In questo modo eviti di riaprire il tabellone ad ogni cf che credo sia piuttosto lento

    qualcosa del genere (ho scritto di getto, verifica in debug eventuali errori)

    Dim rss As Recordset
    Dim CFHold As String
    	Set rss = CurrentDb.OpenRecordset("Select * from A_Q01 ORDER BY [Cfis];")
    	DoCmd.SetWarnings (False)
        Do While Not rss.EOF
            If (rss.Fields("Cfis") <> CFHold) Then
                If CFHold <> "" Then 'in teoria solo per il primo record
                    StrSQL = "UPDATE CF_UNIVOCI " & _
                             "SET [procedure] ='" & txt & "' " & _
                             "WHERE [Cfis] ='" & CFHold & "'"
                    DoCmd.RunSQL StrSQL
                End If
                txt = rss.Fields("NFAS") & "/" & rss.Fields("DANNFAS")
                CFHold = rss.Fields("Cfis")
            Else
                txt = txt & " - " & rss.Fields("NFAS") & "/" & rss.Fields("DANNFAS")
            End If
            rss.MoveNext
        Loop
        StrSQL = "UPDATE CF_UNIVOCI " & _
                 "SET [procedure] ='" & txt & "' " & _
                 "WHERE [Cfis] ='" & CFHold & "'"
    
        DoCmd.RunSQL StrSQL
        DoCmd.SetWarnings (True)
  • Re: Codice troppo lento

    29/09/2023 - silverado60 ha scritto:


    Ciao.... piccolo (o grande) problema:
    ho una tabella con dei codici fiscali (univoci) ed una query (che prende i dati da un'altra tabella) univoca per cf, anno procedura e numero procedura.
    Facendo un'altra query tra le due, ottengo un elenco con cf anno procedura e numero procedura.
    Capita che un cf abbia più di una procedura e le devo raggruppare su un'unica riga (nella prima tabella dei cf univoci c'è un campo apposito):
    cf annoproc-numproc / annoproc-numproc / annoproc-numproc etc

    Ho provato a farlo via codice ma impiega 50 minuti solo per questa operazione e ne devo fare una decina.....
    Si può ovviare?
    Grazie mille

    Ciao, 
    premettendo che concordo da quanto esposto da Fratac in quanto è difficile dare una soluzione adeguata senza info di un certo tipo…

    ti chiedo una semplice cosa con qualche osservazione che magari, se lo ritieni, potrebbe tornarti utile:

    • Perchè raggruppare da A_Q01 i valori per quel codicefiscale e scriverli nella tabella CF_UNIVOCI
      • i dati che interessano li hai già e in un database relazionale una delle prime regole è quella di non ridondare le informazioni
        • metti che i dati su A_Q01 siano errati e vengono aggiornati
        • metti che nei dati su A_Q01 venga inserita una nuova procedura con nuovo anno
        • metti che in A_Q01 deve essere eliminata una procedura perchè inserita erroneamente
        • metti il caso che non sai quali sono i record aggiornati o eliminati o implementati
          • In questi e altri casi possibili, cosa farai ?  esegui una procedura per ripulire il campo di raggruppamento in CF_UNIVOCI e rifai tutto il ciclo per ricreare nuovamente i raggruppamenti da salvare ? 
      • Pertanto l'approccio, a mio avviso, è del tutto errato al di là dello scopo che avrà tale soluzione. in altre parole … così non si fa ;-)

    • Un approccio corretto a tal fine è quello di mettere in relazione le due tabelle per codice fiscale
      • CF_UNIVOCI (FK Id di A_Q01) in relazione uno a molti con A_Q01 (PK Id) 
        • richiamando il codice fiscale dalla tabella CF_UNIVOCI con una semplice Where oppure da una query qualsiasi, puoi elencare tutte le procedure ad esso collegate
        • se non vuoi avere un elenco di procedure e leggerle nella stessa riga, sarà sufficiente concatenarle nel momento in cui si rende necessario esporre i dati…. in una Form, in un Report, etc etc etc…
      • A fronte di una Relazione tra le due tabelle, ogni modifica alle procedure in A_Q01 sarà automaticamente riflessa quando si richiama uno o più codici fiscali dalla tabella CF_UNIVOCI 
      • Insomma … in tutti questi casi non hai bisogno di ridondare i dati che esistono già in una tabella per inserirli in una seconda… non si fa! ;-)

    • Perchè usare nelle fields nomi riservati come procedure ?    ( non si fa ;-)… )
      • ti consiglio di rinominare tale nome per non avere problemi sia nelle stringhe sql che in altre parti del progetto

    • Quando si esegue un ciclo per leggere un recordset appena aperto non è necessario impostare una If per controllare se esistono records, a quello ci pensa la while con il EOF 
    • Quando si esegue un ciclo per leggere un recordset appena aperto non è necessario spostarsi sul primo record in quanto già in fase di open è posizionato sul primo record
    • Quando si aprono i Recordset in DAO o ADODB , etc…. è sempre bene indicare se la open è di sola lettura, se di lettura e aggiornamento, etc etc… anche queste cosine sono importanti da tenere in considerazione oltre a facilitare la rilettura del codice e altre cosucce se si è in condivisione multiutenza, etc etc …
    • Perchè aggiornare un record (rss2.Fields("procedure") = txt) quando non esistono record procedure ? 

    Per esempio un ciclo del genere dovrebbe essere impostato in questo modo;

    Dim rss2 As DAO.Recordset
    Dim rss3 As DAO.Recordset
    Dim strText As String
    ' open recordset
    Set rss2 = DBEngine(0)(0).OpenRecordset("SELECT * FROM CF_UNIVOCI;", dbOpenDynaset)
    ' read
    Do While Not rss2.EOF
        ' open recordset
        Set rss3 = DBEngine(0)(0).OpenRecordset("SELECT * from A_Q01 WHERE Cfis = '" & rss2.Fields("Cfis") & "';", dbReadOnly)
        ' reset variable
        strText = vbNullString
        ' read
        Do While Not rss3.EOF
            If strText <> vbNullString Then strText = strText & " - "
            strText = strText & rss3.Fields("NFAS").Value & "/" & rss3.Fields("DANNFAS").Value
            ' next record
            rss3.MoveNext
        Loop
        ' check to update record
        If strText <> vbNullString Then
            ' update record
            rss2.Edit
            rss2.Fields("CfisProcedure").Value = strText
            rss2.Update
        End If
        ' next record
        rss2.MoveNext
    Loop
    ' dispose
    Set rss2 = Nothing
    Set rss3 = Nothing


    In questi cicli si cerca di leggere, all'occorrenza, il recordset meno corposo per limitare le letture all'indispensabile… per esempio potrebbe essere una cosa di questo tipo se la tabella delle procedure contiene meno records: (ma io questo non lo)

    Dim rss3 As DAO.Recordset
    Dim strText As String
    Dim strCfis As String
    ' open recordset
    Set rss3 = DBEngine(0)(0).OpenRecordset("SELECT * from A_Q01 ORDER BY Cfis;", dbReadOnly)
    ' read
    Do While Not rss3.EOF
        ' check to update record
        If strText <> vbNullString And rss3.Fields("Cfis").Value <> strCfis Then
            ' update record
            DBEngine(0)(0).Execute "UPDATE CF_UNIVOCI SET CfisProcedure = '" & strText & "' WHERE Cfis = '" & strCfis & "';"
            ' reset variables
            strText = vbNullString
        End If
        ' procedure grouping
        strCfis = rss3.Fields("Cfis").Value
        If strText <> vbNullString Then strText = strText & " - "
        strText = strText & rss3.Fields("NFAS").Value & "/" & rss3.Fields("DANNFAS").Value
        ' next record
        rss3.MoveNext
    Loop
    ' dispose
    Set rss3 = Nothing

    Alla fine come all'inizio ;-))  devo però considerare e ribadire che a mio avviso l'approccio non è corretto e consono in un database relazionale.
    Ti consiglio, se vuoi, di cambiare strategia nel tuo progetto.

    P.s.
    Probabilmente quanto ti ho suggerito sarà del tutto errato, abbi pazienza perchè, come premesso, avendo poche informazioni sulle strutture e sullo scopo specifico dell'applicazione, ci può stare 
    ;-)

  • Re: Codice troppo lento

    30/09/2023 - VBAndre ha scritto:


    Dim rss As Recordset Dim CFHold As String Set rss = CurrentDb.OpenRecordset("Select * from A_Q01 ORDER BY [Cfis];")

    Ciao Andre,

    a parte che sono stato particolarmente ;-)) prolisso (mio vizio purtroppo) nel mio post che si è accavallato con il tuo , non ti avevo visto… 

    in generale e spesso per cicli molto onerosi, consiglierei di utilizzare  sempre (quando possibile)  il Set DBEngine(0)(0)
    risulta molto più performante sia in select che in update che per l' insert

    Set rst = DBEngine(0)(0).OpenRecordset("SELECT....
    
    DBEngine(0)(0).Execute "UPDATE...
    
    DBEngine(0)(0).Execute "INSERT INTO...

    Un salutone, e scusa per l'accavallamento, ti ho visto solo adesso

    ;-) 

  • Re: Codice troppo lento

    30/09/2023 - By65Franco ha scritto:


    Ciao Andre,

    a parte che sono stato particolarmente ;-)) prolisso (mio vizio purtroppo) nel mio post che si è accavallato con il tuo , non ti avevo visto… 

    Figurati, 

    grazie per le dritte

  • Re: Codice troppo lento

    29/09/2023 - silverado60 ha scritto:


    Ciao.... piccolo (o grande) problema:
    ho una tabella con dei codici fiscali (univoci) ed una query (che prende i dati da un'altra tabella) univoca per cf, anno procedura e numero procedura.
    Facendo un'altra query tra le due, ottengo un elenco con cf anno procedura e numero procedura.
    Capita che un cf abbia più di una procedura e le devo raggruppare su un'unica riga (nella prima tabella dei cf univoci c'è un campo apposito):
    cf annoproc-numproc / annoproc-numproc / annoproc-numproc etc

    Ho provato a farlo via codice ma impiega 50 minuti solo per questa operazione e ne devo fare una decina.....
    Si può ovviare?
    Grazie mille

    Certo che si potra' ovviare, ma senza indicazioni su come sono fatte le tabelle e come sono le query che usi e' impossibile dire qualsiasi cosa

    Anzi una cosa e' possibile dirla, quando leghi una tabella con un'altra, oppure esegui ricerche da codice su un db, assicurati fi farlo sempre usando campi indicizzati, altrimenti l'interrogazione e' costretta a scorrere tutti i record delle tabelle

Devi accedere o registrarti per scrivere nel forum
13 risposte