Query ricorsiva

di il
11 risposte

Query ricorsiva

Ciao a tutti.
Ho un problemino e magari qualcuno ha già affrontato qualcosa di simile in passato.
Data una tabella del tipo:

DATA_inizio||completato||ammontare
31/07/2014||si||1000
15/08/2014||si||1500

dovrei fare ad una certa data futura (es data_futura: 20/09/2014) il calcolo seguente:
ammontare*(data_futura-data_inizio)/365*1% e mi aspetterei quindi:

1000*15/365*1%+(<------essendoci un successivo campo completato in "SI" prende il numero di gg totale del periodo)
1500*35/365*1% (<-------prende l'ultimo dato per cui trova il campo "completato" in SI)

nel caso ci fosse un nuovo record nel recordset del tipo:
31/08/2014||si||2000

il risultato diventerebbe:

1000*15/365*1%+
1500*16/365*1%+
2000*20/365*1%

Spero di essermi spiegato. Scusatemi in caso contrario.
Mi sa tanto da query ricorsiva, gestibile solo con VBA. Cosa ne pensate? Qualcuno sa come uscirne?

11 Risposte

  • Re: Query ricorsiva

    Non ho capito nulla...
    Una Query ricorsiva in Access non esiste, potrai fare una Funzione ricorsiva richiamata nella query... ma sinceramente non so dirti se è il tuo caso visto l'incomprensione globale.
  • Re: Query ricorsiva

    Si, avevo dato un'occhiata in giro e visto che le query ricorsive esistono solo in SQL e non in Access. Per questo credo sia indispensabile VBA.

    Provo a spiegare meglio quello che devo fare. C'è una tabella:
    DATA_inizio||completato||ammontare
    31/07/2014||si||1000
    15/08/2014||si||1500

    e devo fare un'interrogazione ad una certa data.

    Se la data fosse tra il 31/07 e il 15/08 (es 10 agosto) non andrei ad interessare il secondo record e dovrei applicare 1% alla differenza tra 10 agosto e 31/07 sull'ammontare di 1000. Questo sarebbe facile.

    Se la data fosse oltre l'ultimo record (es 30 agosto) dovrei applicare l'1% alla differenza tra 30 agosto e 15/08 sull'ammontare di 1500. Anche questo sarebbe facile. il problema è che a questo ammontare dovrei poi sommare la "quota parte" prodotta dal primo record, che è ottenuto come 1% dell'ammontare di 1000 (riferito al 1°record) moltiplicato per la totalità del periodo (che, essendo ormai terminato, inizia il 31/07 e finisce il 15/08).
    Scusami se non mi sono ancora spiegato bene...
  • Re: Query ricorsiva

    Innanzitutto il termine RICORSIVA è sbagliato.

    Secondo me nel tuo caso stai guardando il lavoro alla rovescia.

    Ipotiziamo che la tua interrogazione restituisca 1 Record(quello di Luglio) andresti ad effettuare il tuo calcolo solo su quello...

    Se la tua interrogazione restituisse 2 Records, andresti a fare sul 1° lo stesso calcolo di prima, ed al 2° passaggio dovresti avere la somma del 1° + il 2°
    Se ne avessi 3... stessa cosa.

    Quindi predisponi una Funzione alla quale passi il Parametro per il FILTRO, che immagino possa essere la tua DATA, apri un Recordset che restituisca i Record compatibili con il criterio costruito, ed a quel punto generi un LOOP che:
    1) fa il calcolo base
    2) predispone le variabili per memorizzare i TotaliParziali
    3) incrementi il Record Analizzato(passa al successivo) e ricomincia...

    La cosa è semplice, relativamente alla tua conoscenza del VBA.
  • Re: Query ricorsiva

    Scusa Alex ma temo di non essermi spiegato bene...
    Ipotiziamo che la tua interrogazione restituisca 1 Record(quello di Luglio) andresti ad effettuare il tuo calcolo solo su quello....
    giusto
    Se la tua interrogazione restituisse 2 Records, andresti a fare sul 1° lo stesso calcolo di prima, ed al 2° passaggio dovresti avere la somma del 1° + il 2°
    non proprio... il calcolo del 2° sarebbe uguale a quello del primo fintanto non c'è un terzo record che lo "finisce"(per il periodo 15/08 al 30/08). Invece il primo non verrebbe calcolato per tutto il periodo 31/07-30/08 ma solo per il periodo 31/07-15/08, quando comincerebbe il secondo record.

    Se nella tabella avessi per ciascun record il momento in cui finisce il calcolo, sarebbe facile. Ma questa informazione la posso ottenere solo dal record successivo: quando inizia vuol dire che finisce il precedente.

    In vba per Excel ci metterei 5 minuti a risolverlo. Ma vba di Access è completamente diverso, come logica intendo e mi sto trovando malissimo...

    grazie e scusami se non riesco a spiegarmi meglio o forse non ho ben capito quello che intendi dire tu.
  • Re: Query ricorsiva

    galantik ha scritto:


    ...
    il risultato diventerebbe:

    1000*15/365*1%+
    1500*16/365*1%+
    2000*20/365*1%
    Allora... ho buttato giù questi appunti volanti per analizzare quello che c'è da fare, ipotizzando di lanciare il codice da un pulsante in una maschera ed ottenere il risultato in una semplice msgbox.
    ===
    Dati di partenza
    Importo || Data
    1000 || 20/07/2014
    1500 || 10/08/2014
    2000 || 02/09/2014
    1300 || 09/09/2014
    ===
    Dataselezione = 12/09/2014 (che poi può essere preso da una textbox nella maschera)
    ===
    Risultato da ottenere
    1300 * 1% * (12/09/2014 - 09/09/2014)/365
    2000 * 1% * (09/09/2014 - 02/09/2014)/365
    1500 * 1% * (02/09/2014 - 10/08/2014)/365
    1000 * 1% * (10/08/2014 - 20/07/2014)/365
    ===
    apro un recordset che ha questa struttura:
    SELECT Importo, data FROM T2 WHERE data < dataselezione ORDER BY data DESC
    Importantissimo l'ordine decrescente per data.
    dim DataSelezione As Date
    dim lngInteressi as Long
    dim strRiga as String
    
    If (EOF or BOF) then
    	'gg = datediff(DataSuccessiva, datadata)
    	'nel primo calcolo Datasuccessiva = dataselezione
    	'e data è quella del recordset
    	Datasuccessiva = Dataselezione
    	Do while EOF
    		lngInteressi = Importo * 0,01% * Datediff("d", data, datasuccessiva) / 365
    		strRiga = strRiga & Importo & " * 0,01% * " & Datediff("d", data, datasuccessiva) / 365 & " = " & interessi) & VbCrLf
    		Datasuccessiva = data
    	Loop
    	Msgbox (strRiga)
    Else Msgbox("Nessun record soddisfa i criteri")
    ...
    Sono appunti di getto. Non ho guardato la virgola del decimale (0,01 o 0.01) e la formattazione delle date.
    Devi poi fare la somma dei singoli risultati?
    Non ho poi capito questa precisazione

    galantik ha scritto:


    1000*15/365*1%+(<------essendoci un successivo campo completato in "SI" prende il numero di gg totale del periodo)
    1500*35/365*1% (<-------prende l'ultimo dato per cui trova il campo "completato" in SI)
    Per carità: imposta la query fin da subito perché selezioni i record che hanno "SI" su [completato]... non tirarlo in ballo qui.
  • Re: Query ricorsiva

    Ciao Phil.
    Hai capito bene quello che devo fare.
    Per rispondere alla tua domanda, si, dovrei farne poi la somma.
    Per quanto riguarda la precisazione che non hai capito volevo solo dire quello che in realtà hai capito correttamente. La condizione si sul campo "completato" è necessario perché la tabella di origine ha già 24 record pronti che andranno completati con il loro ammontare nel tempo. quindi è possibile che se fai un'interrogazione alla data del 5 ottobre, l'ultimo record disponibile che trova un valore "ammontare" sia (ad esempio) il 4°, che si riferisce alla data del 5/settembre. quindi dovrei fare il calcolo per ciascun record "completato" e per l'ultimo record "completato" applicare la formula per il periodo 05/09-05/10. la tabella in sostanza prevede già un successivo record con data 20/09 ma non è ancora stato completato.

    in sostanza la query corretta è:
    SELECT Nav.NaVamount, Nav.navdate
    FROM Nav
    WHERE (((Nav.[NavDate])<[Valuation Date]) AND ((Nav.VFSEvaluation)<>No))
    ORDER BY Nav.navdate DESC;
    
    per il resto... ti ho mandato un messaggio privato.
    grazie infinite.
  • Re: Query ricorsiva

    galantik ha scritto:


    Ciao Phil.
    Hai capito bene quello che devo fare.
    ...
    in sostanza la query corretta è:
    SELECT Nav.NaVamount, Nav.navdate
    FROM Nav
    WHERE (((Nav.[NavDate])<[Valuation Date]) AND ((Nav.VFSEvaluation)<>No))
    ORDER BY Nav.navdate DESC;
    
    ...
    Mi rimangio praticamente tutto quello che ho finora pubblicato.
    Questa è già più "decente": una funzione (molto liberamente tratta da: ) da caricare in un modulo e da chiamare nella query QryNav
    
    Function TrovaDataSuccessiva(KeyName As String, KeyValue)
    Dim RS As DAO.Recordset
    Dim DataFine As Date
    
    On Error GoTo Err_TrovaDataSuccessiva
    Set RS = DBEngine(0)(0).OpenRecordset("QryNav")
    
    RS.FindFirst "[" & KeyName & "] = #" & Format(KeyValue, "mm/dd/yyyy") & "#"
    RS.MoveNext
    If Not RS.EOF Then
    	DataFine = RS(KeyName).Value
    	Else
    	DataFine = #9/18/2014#
    End If
    
    Set RS = Nothing
    
    Esci_TrovaDataSuccessiva:
             ' Restituisci il risultato.
             TrovaDataSuccessiva = DataFine
             Exit Function
    
    Err_TrovaDataSuccessiva:
        MsgBox (Err.Number & " = " & Err.Description)
        Exit Function
    
    End Function
    Nella query poi aggiungi un campo
    Datasuccessiva: TrovaDataSuccessiva("navdate",[navdate])
    Ora bisogna fargli capire fino a quale data eseguire la selezione e passargli quel parametro in modo che sia accessibile anche dalla funzione, perché per ora e per verificare che funzionasse mi sono limitato ad assegnargli manualmente un valore nella funzione, ma così non può andare. Il lavoro continua
  • Re: Query ricorsiva

    Philcattivocarattere ha scritto:


    galantik ha scritto:


    in sostanza la query corretta è:
    SELECT Nav.NaVamount, Nav.navdate
    FROM Nav
    WHERE (((Nav.[NavDate])<[Valuation Date]) AND ((Nav.VFSEvaluation)<>No))
    ORDER BY Nav.navdate DESC;
    
    ...
    Mi rimangio praticamente tutto quello che ho finora pubblicato.
    E quando dico che mi rimangio tutto intendo anche

    Philcattivocarattere ha scritto:


    Importantissimo l'ordine decrescente per data.
    anzi: ora è obbligatorio l'ordinamento crescente per data.
  • Re: Query ricorsiva

    Philcattivocarattere ha scritto:


    Nella query poi aggiungi un campo
    Datasuccessiva: TrovaDataSuccessiva("navdate",[navdate])
    Ora bisogna fargli capire fino a quale data eseguire la selezione e passargli quel parametro in modo che sia accessibile anche dalla funzione, perché per ora e per verificare che funzionasse mi sono limitato ad assegnargli manualmente un valore nella funzione, ma così non può andare. Il lavoro continua
    Ho rinunciato ad inserire la funzione direttamente nella query: non ne uscivo più con quel parametro che spuntava da tutte le parti. Questa è la cosa che per ora si avvicina ad un uso quasi normale.
    Query QryNav_param =
    SELECT Nav.NaVamount, Nav.navdate, Nav.VFSEvaluation
    FROM Nav
    WHERE (((Nav.navdate)<[DataValutazione:]) AND ((Nav.VFSEvaluation)=Yes))
    ORDER BY Nav.navdate;
    Funzione =
    Function TrovaDataSuccessiva(strnomecampo As String, Campo)
    Dim rs As DAO.Recordset
    Dim qrydef As QueryDef
    Dim DataFine As Date
    
    On Error GoTo Err_TrovaDataSuccessiva
    Set rs = CodeContextObject.RecordsetClone
    
    rs.FindFirst "[" & strnomecampo & "] = #" & Format(Campo, "mm/dd/yyyy") & "#"
    rs.MoveNext
    If Not rs.EOF Then
    DataFine = rs(strnomecampo).Value
    Else
    DataFine = [Forms]![maschera1]![Testo2]
    End If
    
    Set rs = Nothing
    
    Esci_TrovaDataSuccessiva:
             ' Restituisci il risultato.
             TrovaDataSuccessiva = Format(DataFine, "dd/mm/yyyy")
             Exit Function
    
    Err_TrovaDataSuccessiva:
    ...
    End Function
    Una maschera1 dove ci sono due caselle di testo: una dove inserire la data [testo2] e una per il tasso [PercTasso] (sai mai che invece dell' 1% si voglia fare qualcos'altro) e un pulsante di comando con associato questo codice
    Private Sub Comando1_Click()
    Dim qdf As DAO.QueryDef
    Dim rs As DAO.Recordset
    Set qdf = CurrentDb.QueryDefs("Qrynav_param")
    qdf.Parameters(0).Value = Me.Testo2.Value
    Set rs = qdf.OpenRecordset
    If rs.EOF And rs.BOF Then
       MsgBox "Nessun Movimento soddisfa il criterio"
       Exit Sub
    Else
        DoCmd.OpenForm "frmQry_param"
        Set Forms!frmQry_param.Recordset = rs
    End If
    End Sub
    Maschera continua frmQry_param inizialmente creata con la procedura guidata, sulla query QryNav_param, per avere automaticamente i campi della query, poi però ho eliminato l'origine dati, per gestirla dalla maschera1 che ne chiama l'apertura. Impostate a No le proprietà di aggiunta, modifica, cancellazione (deve solo visualizzare). Nella maschera continua ho aggiunto una casella di testo la cui origine richiama la funzione TrovaDataSuccessiva
    =TrovaDataSuccessiva('navdate';[navdate])
    , una casella di testo che calcola i giorni
    =DateDiff("g";[navdate];[Datasuccessiva])
    ed infine una casella di testo che calcola "gli interessi" (ma solo perché la formula è quella degli interessi semplici C x i x t).
    =[NaVamount]*[CasellaGiorni]*Maschere!Maschera1!PercTasso/365
    Si apre la maschera1, si inseriscono la data di selezione e il "tasso". Il pulsante di comando apre frmQry_param... e magia. Totali a pié di maschera ecc: spazio alla creatività.
    Allegati:
    14797_298dec034895cd121cf1cad6e32a7009.jpg
    14797_298dec034895cd121cf1cad6e32a7009.jpg
  • Re: Query ricorsiva

    Grazie Phil.
    Interessante la tua soluzione.
    Io c'ero arrivato con un altro metodo. In sostanza usavo la query che ti avevo già postato e da quella trovavo i diversi sottoperiodi. poi ho usato un codice che essenzialmente faceva un loop andando a ritrovo e memorizzando quando iniziava ciascun periodo (che corrispondeva natualmente al momento in cui terminava il periodo precedente).
    Credo di aver seguito il suggeriemtno di Alex.
    E' stato interessantissimo confrontarmi con voi!
    a presto
  • Re: Query ricorsiva

    Philcattivocarattere ha scritto:


    Ho rinunciato ad inserire la funzione direttamente nella query: non ne uscivo più con quel parametro che spuntava da tutte le parti...
    Risolto! Ora la funzione si può mettere direttamente nella query. La funzione è così:
    Dim rs As DAO.Recordset
    Dim qrydef As QueryDef
    Dim DataFine As Date
    
    On Error GoTo Err_TrovaDataSuccessiva
    Set qrydef = DBEngine(0)(0).QueryDefs("QryNav_param")
    qrydef.Parameters(0).Value = Eval(qrydef.Parameters(0).Name) 'lo pesca da [Forms]![maschera1]![Testo2]
    Set rs = qrydef.OpenRecordset 'ecco dove sbagliavo! Dovevo aprire il recordset della querydef qui dichiarata, non della query stessa che andrebbe a cercare ancora il parametro
    rs.FindFirst "[" & strnomecampo & "] = #" & Format(Campo, "mm/dd/yyyy") & "#"
    ...
    In questo modo è possibile scrivere direttamente nella query [QryNav_param] il campo
    Datasucc: TrovaDataSuccessiva('navdate';[Navdate])
    e via via tutti gli altri valori che interessano: i giorni con DateDiff e gli Interessi. La maschera1 che contiene, come prima, due caselle di testo con la data di selezione e il tasso di interesse e il pulsante di comando si limita ad aprire una normalissima maschera che ha come origine dati "QryNav_param". Infinitamente più veloce della soluzione precedente.
Devi accedere o registrarti per scrivere nel forum
11 risposte