Media mobile ponderata access

di il
21 risposte

Media mobile ponderata access

Ciao a tutti, ho creato un database che mi valorizza i prodotti in magazzino. Dopo varie relazioni e query ho una tabella denominata giacenze che ha questa struttura:
Material Data Tipologia Unità Valore

Ovvero codice prodotto, data (di arrivo o uscita), tipologia cioè se stock, acquisto o vendita, e valore di stock o di acquisto.
Il mio obiettivo è quello di calcolare la media mobile ponderata (MAP) per i prodotti in uscita. Vi faccio un esempio:
Material    Data                Tipologia    Unità   Valore   Giacenze(campo calcolato)    Valoretot(campo calcolato)       MAP(campo calcolato)
1          01/01/2017            Stock          10       100                    10                       unità*valore:10*100       valoretot/giacenze:1000/10
2          01/01/2017            Acquisto      5          90                     10+5=15                     (10*100)+(5*90)              (1000+ 400)/15  
3         01/01/2017             Vendita       10  X=MAP(record prec)    15-10                       ecc                            ecc 
4         01/01/2017             Acquisto      10        95                     5+10                           ecc                           ecc

Il primo prodotto in uscita avrà come valore la media del record precedente. le giacenze le riesco a calcolare tramite dsum, ma in SQL poichè la map dipende dal valore, si crea un riferimento circolare e non funziona.
La mia idea è quella di fare una prima query e trovare tutti codici prodotto, salvarli in un array e poi darli in pasto ad un ciclo for che di volta in volta mi filtra la tabella ordinata per data e al primo valore nullo inserisce la map del record precedente. Ho tentato di farlo in vba ma con pochi risultati.
Qualche consiglio?
Grazie

21 Risposte

  • Re: Media mobile ponderata access

    Per fare la media ponderata, si sfrutta la matematica, ovvero Log ed Exp...
    Leggi questo interessante esempio di Giorgio Rancati, ben documentato con tanto di esempi:
    http://www.riolab.org/index.php?option=com_content&view=article&id=134:media-geometrica-semplice-e-media-geometrica-ponderata&catid=46:access&Itemid=69
  • Re: Media mobile ponderata access

    Non è un problema calcolare la media, ma associare la media di un record al valore del record successivo se quest'ultimo è una vendita. Non so se sono stata chiara.
  • Re: Media mobile ponderata access

    eleonora92 ha scritto:


    Non è un problema calcolare la media, ma associare la media di un record al valore del record successivo se quest'ultimo è una vendita. Non so se sono stata chiara.
    Parli di Valorizzare un prodotto per una tranzazione immagino...?
    Quindi in fase di inserimento di un NUOVO RECORD...?(nuova transazione)

    Il calcolo del valore di Media a quel punto ti servirà come Valorizzazione del Valore di Default della vendita o sbaglio...?
  • Re: Media mobile ponderata access

    @Alex ha scritto:


    eleonora92 ha scritto:


    Non è un problema calcolare la media, ma associare la media di un record al valore del record successivo se quest'ultimo è una vendita. Non so se sono stata chiara.
    Parli di Valorizzare un prodotto per una tranzazione immagino...?
    Si esatto; ho una tabella finale che contiene tutte le informazioni di stock di magazzino, entrate ed uscite future. Poichè devo calcolarmi la media per quarter vado a valorizzare ogni prodotto in uscita cosicchè le nuove transazioni tengano conto di tutti i prodotti passati.
    Ti allego un pò di codice su cui sto lavorando:

    Dim cn As ADODB.Connection
       Dim rs As ADODB.Recordset
       Dim array1 As Variant
       'Use the ADO connection that Access uses
       Set cn = CurrentProject.AccessConnection
       'Create an instance of the ADO Recordset class, and
       'set its properties
       Set rs = New ADODB.Recordset
       With rs
          Set .ActiveConnection = cn
          .Source = "SELECT Material from giacenze group by material;"
          .LockType = adLockOptimistic
          .CursorType = adOpenKeyset
          .Open
       End With
       
       
       array1 = rs.GetRows
       Set rs = Nothing
       Set cn = Nothing
       Set cn = CurrentProject.AccessConnection
       Set rs = New ADODB.Recordset
       
       ' for di tutti gli ID
       for i=0 to ubound(array1)
       ID = array1(i) ' errore
       With rs
          Set .ActiveConnection = cn
          .Source = "Select * from giacenze where Material='" & ID & "' order by giacenze.data;"
          .LockType = adLockOptimistic
          .CursorType = adOpenKeyset
          .Open
       End With
       Next 

    Dopo aver risolto questa parte di codice, gli dovrei dire che per ogni ciclo, quando trova un record con un valore nullo, gli deve inserire il valore map del record precedente. così da potersi calcolare la nuova MAP aggiornata
  • Re: Media mobile ponderata access

    @Alex ha scritto:


    eleonora92 ha scritto:



    Il calcolo del valore di Media a quel punto ti servirà come Valorizzazione del Valore di Default della vendita o sbaglio...?
    Esattamente!! e successivamente se ci fossero acquisti di quel prodotto, dovrebbero tener conto della media dipendente dalla vendita.
  • Re: Media mobile ponderata access

    eleonora92 ha scritto:


    @Alex ha scritto:


    eleonora92 ha scritto:



    Il calcolo del valore di Media a quel punto ti servirà come Valorizzazione del Valore di Default della vendita o sbaglio...?
    Esattamente!! e successivamente se ci fossero acquisti di quel prodotto, dovrebbero tener conto della media dipendente dalla vendita.
    Crei una Public Function che restituisce un Valore(quello della media mobile del prodotto) e lo assegni alla proprietà DEFAULTVALUE del controllo Associato al prodotto.
  • Re: Media mobile ponderata access

    Credo di aver capito il tuo consiglio, ma ho una domanda; se ho 2 prodotti in uscita e il valore MAP è diverso perchè magari tra i due prodotti in uscita ne sono arrivati altri in magazzino? come faccio?
  • Re: Media mobile ponderata access

    Capito nulla...
  • Re: Media mobile ponderata access

    Hai ragione ho scritto veramente male!! ti allego il codice che ho fatto, non funziona perchè inserisce a tutti il primo valore map che si calcola, non capisco perchè:
    
    Private Sub Form_Open(Cancel As Integer)
    Dim cn As ADODB.Connection
       Dim rs As ADODB.Recordset
       Dim array1 As Variant
       Dim strsql As String
       Dim Valoretot As Double
       Dim magazzino As Integer
       Dim map As Double
       Dim mapprev As Double
       'Use the ADO connection that Access uses
       Set cn = CurrentProject.AccessConnection
       'Create an instance of the ADO Recordset class, and
       'set its properties
       Set rs = New ADODB.Recordset
       strsql = "SELECT giacenze.* FROM giacenze WHERE (((giacenze.data) Is Not Null)) ORDER BY giacenze.Material, giacenze.data"
       With rs
          Set .ActiveConnection = cn
          .Source = strsql
          .LockType = adLockOptimistic
          .CursorType = adOpenKeyset
           .Open
          Valoretot = DSum("Input*Valore-Output*Valore", "giacenze", "material='" & [Material] & "' and data<=" & CLng([Data]))
          magazzino = DSum("Input-Output", "giacenze", "material='" & [Material] & "' and data<=" & CLng([Data]))
          map = [Valoretot] / [magazzino]
        
       rs.MoveFirst
        Do While Not rs.EOF
       mapprev = map
       
       ID = rs.Fields("Material").Value
       rs.MoveNext
       If rs.EOF = "True" And rs.BOF = "True" Then
       Exit Do
       ElseIf IsNull(rs.Fields("Valore").Value) And ID = rs.Fields("Material").Value Then
       rs.Fields("Valore").Value = mapprev
        rs.Update
        End If
    
    Loop
    End With
    
       rs.Close
       Set rs = Nothing
       Set cn = Nothing
    
    End Sub
    
    

    L'idea è che per ogni riga si salva il valore map calcolato, movenext--> controlla se il valore è nullo ed ha lo stesso ID del precedente allora inserisce la media.
    Mi inserisce a tutti la prima media calcolata... perchè non la calcola per ogni record? scusa ma non sono molto brava con vba.
  • Re: Media mobile ponderata access

    Il codice di altri lo guardo SOLO quando ho capito cosa serve, altrimenti con l'ottica di capire cosa hai fatto fare al codice, rischio di perdere il senso..., perchè se tu non hai basi discrete per la logica strutturata, diventa complicato capire se fai errori di base o di concetto...
    Purtroppo errori di base ne fai diversi...
    
     rs.MoveNext
    If rs.EOF = "True" And rs.BOF = "True" Then
    Le Proprietà EOF e BOF restituiscono un varType di tipo Boolean, perchè tu le confronti con una STRINGA...?
    Funziona perchè access fa i miracoli e fa delle conversioni implicite... ma è una cosa da non vedere.
    Dopo un MoveNext si può controllare la proprietà BOF secondo te...?
    Usare ADO con Access è un controsenso, il che mi fa presupporre che non usi Access... quindi il tuo codice eviterei per ora di leggerlo, pensando possa essere deviante alla comprensione, preferisco capire cosa devi fare in modo chiaro.
  • Re: Media mobile ponderata access

    Concordo con @Alex.

    Cosa c'azzecca una connessione ADO con connessione DAO ???
    Set cn = CurrentProject.AccessConnection
    'quel codice' non può funzionare in alcun modo.
  • Re: Media mobile ponderata access

    Grazie per aver risposto, allora ricominciamo.
    Uso Access, che per fortuna sopperisce alla mia poca conoscenza di vba!!
    Con le query e la costruzione di un db me la cavo, ma non si può fare tutto senza le macro.
    Allora, il mio obiettivo è quello di calcolare il valore del magazzino e dei singoli prodotti in uscita. Ho una tabella con tutti gli Stock e Acquisti dei prodotti. In un'altra tabella sono riuscita ad ottenere tutti i prelievi dei prodotti (con la data specifica) che poi dovranno entrare in produzione. Per valorizzare il prodotto in uscita uso la media mobile che in parole povere "valorizza" il materiale in uscita al costo medio di quel materiale. Cioè se in magazzino ho 10 pezzi di uno specifico materiale che ho pagato 10 €, e domani mi arrivano altri 5 pezzi che però pagherò 5€, la media pesata sarà (10*10+5*5)/(10+5).
    Tutti i pezzi che verranno prelevati da domani in poi avranno un valore di 125/15.
    Mi ricavo una tabella che ordina per data e per materiale tutti i movimenti di magazzino, quindi stock acquisti e vendita(prelievi). la struttura della tabella è la seguente:
    - Material: codice prodotto
    - Data: data dell'operazione
    - Input: quantità del materiale nel caso di stock o acquisto
    - Output: quantità nel caso di prelievo
    - Valore: importo singolo degli input (per gli output sarà null)

    Per calcolare la media ho bisogno di calcolare il valore totale del magazzino e le giacenze (progressivi), il loro rapporto sarà quindi la mia MAP che dovrò associare ai prodotti in uscita.
    La mia idea, che non so perchè ma funziona, è questa:
    - creo un recordset con la tabella totale in cui per ogni materiale avrò come prima voce lo stock (situazione attuale del magazzino), poi gli eventuali acquisti e/o prelievi.
    - mi sposto alla prima riga, salvo l'id del materiale e la data e creo un nuovo recordset che mi seleziona tutti i prodotti che hanno quell'id e sono precedenti a quella data
    - mi calcolo e creo variabili per valoretot (valore totale del magazzino) e giacenze e relativa media.
    - mi sposto nella riga successiva e se il valore del record è null (quindi è un prelievo) gli inserisco come valore la variabile map calcolata al punto precedente.
    -ricalcolo recordset valoretot giacenze e map e poi movenext...

    Per quanto riguarda il controllo dell'eof e bof, ho dovuto modificarlo così sperando di non ricevere errori ma continuo ad averne, se avete consigli su come modificare il codice sono tutt'orecchie(o occhi in questo caso).
    premettendo che funziona( è abbastanza lento ma sono 15mila record), vi allego il codice così mi dite quali e quante cretinate ho scritto:
    
    Private Sub Form_Open(Cancel As Integer)
    Dim cn As ADODB.Connection
       Dim rs As ADODB.Recordset
       Dim rs2 As ADODB.Recordset
       Dim array1 As Variant
       Dim strsql As String
       Dim Valoretot As Double
       Dim magazzino As Integer
       Dim map As Double
       Dim ID As String
       
       Dim Material As String
       Dim Data As Date
       'Use the ADO connection that Access uses
       Set cn = CurrentProject.AccessConnection
       'Create an instance of the ADO Recordset class, and
       'set its properties
       Set rs = New ADODB.Recordset
       strsql = "SELECT giacenze.* FROM giacenze WHERE (((giacenze.data) Is Not Null)) ORDER BY giacenze.Material, giacenze.data"
       With rs
          Set .ActiveConnection = cn
          .Source = strsql
          .LockType = adLockOptimistic
          .CursorType = adOpenKeyset
           .Open
          
          
         
        
       rs.MoveFirst
        Do While Not rs.EOF
        Material = .Fields("Material").Value
        Data = .Fields("data").Value
        
        Set rs2 = New ADODB.Recordset
       strsql = "SELECT giacenze.* FROM giacenze WHERE material='" & Material & "' and data<=" & CLng([Data]) & " ORDER BY  giacenze.data"
       With rs2
          Set .ActiveConnection = cn
          .Source = strsql
          .LockType = adLockOptimistic
          .CursorType = adOpenKeyset
           .Open
          Valoretot = DSum("Input*Valore-Output*Valore", "giacenze", "material='" & [Material] & "' and data<=" & CLng([Data]))
          magazzino = DSum("Input-Output", "giacenze", "material='" & [Material] & "' and data<=" & CLng([Data]))
          End With
          rs2.Close
          Set rs2 = Nothing
         
        
       
    
        
        'Valoretot1 = Valoretot(a, b)
        
       'magazzino1 = magazzino(.Fields("Material").Value, .Fields("data").Value)
       If Valoretot = 0 Or magazzino = 0 Then
       map = 0
       Else: map = [Valoretot] / [magazzino]
       End If
       ID = rs.Fields("Material").Value
       rs.MoveNext
       If rs.EOF = "True" And rs.BOF = "True" Then
       Exit Do
       ElseIf IsNull(rs.Fields("Valore").Value) And ID = rs.Fields("Material").Value Then
       rs.Fields("Valore").Value = map
        rs.Update
        End If
        
        
    Loop
    End With
    
    
    
    
       rs.Close
       Set rs = Nothing
       Set cn = Nothing
    
    End Sub
    
    
    
  • Re: Media mobile ponderata access

    E' simpatico perchè ti avevo suggerito un metodo per calcolare la Media Mobile che non necessitava di codice, e mi hai risposto che sei capace di calcolare la media, ed in effetti ci spieghi anche come in modo corretto... di fatto tuttavia se facessi come ti ho suggerito eviteresti il codice sbrodolato che hai scritto in modo pessimo e che oggettivamente non serve a nulla, perchè se scrivi bene la Query SQL che calcola la Media Mobile passando il CodiceMateriale usando LOG e EXP, poi con un DSUM potresti assegnarlo in modo banale al valore di Default, come ti avevo detto fin dall'inizio.

    Poi ti è stato detto che Access è bene venga usato con ADO non ADO... ed anche quì hai fatto quello che volevi...

    Insomma vedi tu fai come credi.
  • Re: Media mobile ponderata access

    Ho cercato di fare come mi hai consigliato, ma poichè la media la calcolo tramite il valore, e il valore tramite la map, mi dava errore credo per un riferimento circolare; quindi non credo di aver capito bene come strutturare il tutto! potresti esse più esplicito nel tuo suggerimento per favore? come dovrebbe essere fatta la query per calcolare la map? grazie molte per le risposte e la pazienza
Devi accedere o registrarti per scrivere nel forum
21 risposte