Newsgroup: it.comp.appl.access
Titolo: [x esperti] - Calcolare di un evento la competenza sui mesi

Scenario :
1) Ho una tabella "zzzzz_BMI" di eventi (Malattie e Infortuni) 
che tra gli altri campi, ne ha uno "Inizio" e uno "fine" e un 
CmpTipo (1:Malattia-2:Infortunio)
2) Ho una tabella "festività" in cui sono registrate per data e 
descrizione tutte le festività dell'anno
Obbiettivo : 
Il mio scopo è quello di sapere l'incidenza degli eventi sui 
singoli mesi, tenendo presente che :
1) la mia settimana lavorativa è : Lun-Ven
2) che le festività non vanno conteggiate
3) calcolare solo la parte di competenza dell'evento sul periodo 
di riferimento (ovvero se un evento inizia il 15/01/08 e finisce 
il 03/03/08, e io sto analizzando il mese di Marzo, l'evento 
dovrà esser conteggiato solo dal 01/03/08 al 03/03/08 e per le 
parti eccedenti sui mesi precedenti o successivi)
Rappresentazione grafica : 
Il risultato deve essere qualcosa di questo tipo :
---------------
|Mes|Mal.|Inf.|
---------------
|01 |150 |0   |
|02 |375 |18  |
|03 |63  |22  |
|04 |0   |280 |
|05 |0   |0   |
|06 |56  |120 |
|07 |0   |0   |
|08 |159 |160 |
|09 |141 |17  |
|10 |122 |10  |
|11 |0   |0   |
|12 |1   |0   |
---------------
Metodo utilizzato per ottenere il risultato di cui sopra:
Creato modulo : 
'================================================================
=====
' Dichiarazioni variabili pubbliche
'================================================================
=====
Public gg_lavorabili            As Integer
Public gg_perse                 As Integer
'================================================================
=====
'================================================================
=====
Public Function contaggNEW(xStart As Date, xStop As Date, mesRif 
As Integer) As Variant
    Dim PrimoGgMese         As Date
    Dim UltimoGgMese        As Date
    'Calcolo il primo giorno del mese passato alla funzione    
    PrimoGgMese = "01/" & Format(mesRif, "00") & "/08"
    'Calcolo l'ultimo giorno del mese passato alla funzione
    UltimoGgMese = DateAdd("d", -1, DateAdd("m", 1, CVDate
(PrimoGgMese)))
 
    'Escludo gli eventi che non sono nel range del mese 
    If xStart > UltimoGgMese Or xStop < PrimoGgMese Then
        contaggNEW = 0
        Exit Function
    End If
 
    xStart = IIf([xStart] > PrimoGgMese, [xStart], PrimoGgMese)
    xStop = IIf([xStop] < UltimoGgMese, [xStop], UltimoGgMese)
 
    Debug.Print "3) DAL : " & xStart & " AL : " & xStop
 
    Call calcoladays(xStart, xStop)
    contaggNEW = gg_perse * 8
End Function
'================================================================
=====
'================================================================
=====
'Calcolo i giorni
Private Sub calcoladays(xStart As Date, xStop As Date)
On Error GoTo err:
 
        Dim contatore               As Long
        Dim maxdiff                 As Long
 
        gg_perse = 0
 
        'Calcolo Sabato e Domenica
        contatore = 0
        maxdiff = Int(DateDiff("d", xStart, xStop))
 
        For contatore = 0 To maxdiff
            Call FEST(DateAdd("d", contatore, CVDate(Format
(xStart, "dd/mm/yy"))))
        Next contatore
 
        Exit Sub
err:
    MsgBox err.Number & " - " & err.Description
End Sub
'================================================================
=====
'================================================================
=====
'Conta i giorni persi
Public Sub FEST(xSabDom As Long)
    Dim trafsormDate    As Date
    trafsormDate = CVDate(xSabDom)
 
    If trovaFEST(trafsormDate) = 0 Then
        ' NON E' UNA FESTIVITA'
        Dim WEK As Integer
        WEK = Weekday(xSabDom)
        'Verifico solo il range 2-6, perchè 1 è la
        'domenica e 7 è il sabato (li escludo entrambi)
        If WEK > 1 And WEK < 7 Then
            gg_perse = gg_perse + 1
        Else: End If
    Else: End If
End Sub
'================================================================
=====
'================================================================
=====
'funzione per verificare se è festivo o meno
Public Function trovaFEST(trasformDate As Date) As Boolean
        Dim rs As DAO.Recordset
        Set rs = CurrentDb.OpenRecordset("select data from 
festività where data=#" & Format$(trasformDate, "m/d/yy") & "#")
            trovaFEST = Not rs.EOF
        Set rs = Nothing
        Set db = Nothing
End Function
'================================================================
=====
Creata un SQL di Unione : 
SELECT CVDate("01") AS Mese, Sum(IIf([CmpTipo]=1,contaggNew
([DataStart],[DataStop],1),0)) AS OreMal, Sum(IIf([CmpTipo]
=2,contaggNew([DataStart],[DataStop],1),0)) AS OreInf
FROM zzzzz_BMI
UNION SELECT CVDate("02") AS Mese, Sum(IIf([CmpTipo]=1,contaggNew
([DataStart],[DataStop],2),0)) AS OreMal, Sum(IIf([CmpTipo]
=2,contaggNew([DataStart],[DataStop],2),0)) AS OreInf
FROM zzzzz_BMI
UNION SELECT CVDate("03") AS Mese, Sum(IIf([CmpTipo]=1,contaggNew
([DataStart],[DataStop],3),0)) AS OreMal, Sum(IIf([CmpTipo]
=2,contaggNew([DataStart],[DataStop],3),0)) AS OreInf
FROM zzzzz_BMI
UNION SELECT CVDate("04") AS Mese, Sum(IIf([CmpTipo]=1,contaggNew
([DataStart],[DataStop],4),0)) AS OreMal, Sum(IIf([CmpTipo]
=2,contaggNew([DataStart],[DataStop],4),0)) AS OreInf
FROM zzzzz_BMI
UNION SELECT CVDate("05") AS Mese, Sum(IIf([CmpTipo]=1,contaggNew
([DataStart],[DataStop],5),0)) AS OreMal, Sum(IIf([CmpTipo]
=2,contaggNew([DataStart],[DataStop],5),0)) AS OreInf
FROM zzzzz_BMI
UNION SELECT CVDate("06") AS Mese, Sum(IIf([CmpTipo]=1,contaggNew
([DataStart],[DataStop],6),0)) AS OreMal, Sum(IIf([CmpTipo]
=2,contaggNew([DataStart],[DataStop],6),0)) AS OreInf
FROM zzzzz_BMI
UNION SELECT CVDate("07") AS Mese, Sum(IIf([CmpTipo]=1,contaggNew
([DataStart],[DataStop],7),0)) AS OreMal, Sum(IIf([CmpTipo]
=2,contaggNew([DataStart],[DataStop],7),0)) AS OreInf
FROM zzzzz_BMI
UNION SELECT CVDate("08") AS Mese, Sum(IIf([CmpTipo]=1,contaggNew
([DataStart],[DataStop],8),0)) AS OreMal, Sum(IIf([CmpTipo]
=2,contaggNew([DataStart],[DataStop],8),0)) AS OreInf
FROM zzzzz_BMI
UNION SELECT CVDate("09") AS Mese, Sum(IIf([CmpTipo]=1,contaggNew
([DataStart],[DataStop],9),0)) AS OreMal, Sum(IIf([CmpTipo]
=2,contaggNew([DataStart],[DataStop],9),0)) AS OreInf
FROM zzzzz_BMI
UNION SELECT CVDate("10") AS Mese, Sum(IIf([CmpTipo]=1,contaggNew
([DataStart],[DataStop],10),0)) AS OreMal, Sum(IIf([CmpTipo]
=2,contaggNew([DataStart],[DataStop],10),0)) AS OreInf
FROM zzzzz_BMI
UNION SELECT CVDate("11") AS Mese, Sum(IIf([CmpTipo]=1,contaggNew
([DataStart],[DataStop],11),0)) AS OreMal, Sum(IIf([CmpTipo]
=2,contaggNew([DataStart],[DataStop],11),0)) AS OreInf
FROM zzzzz_BMI
UNION SELECT CVDate("12") AS Mese, Sum(IIf([CmpTipo]=1,contaggNew
([DataStart],[DataStop],12),0)) AS OreMal, Sum(IIf([CmpTipo]
=2,contaggNew([DataStart],[DataStop],12),0)) AS OreInf
FROM zzzzz_BMI;
RISULTATI : 
Il risultato che ottengo, è assolutamente preciso e perfetto... 
unica pecca, la velocità.
Con tabelle locali e con pochi records, tanto quanto si può 
ancora gestire... ma quando i records aumentano, e le taelle 
divengono collegate, per fare un calcolo su 207 records ad 
esempio che coprono 4 mesi, di mette sui 10 secondi...
RICHIESTA:
Metodi alternativi a questo?
Tnks a lot
Pablitomf (Maurizio Facciuti)
-- 
Postato da Alice Newsgroup: lo usi da web ma con le funzioni del newsreader 
http://newsgroup.alice.it
Gerarchie it, italia, it-alt, tin, it.binari. Unico!

Scusa se ho skippato tutto... ma era lungissimo...!
Non fai prima se ti crei una funzione che passando 2 date cardine ti
calcola il Numero di giorni lavorativi...???
Alla fine i giorni persi sono solo quelli lavorativi...!
Se strutturi bene la funzione... gli passi il Mese di
analisi(numerico) e lo Start della malattia o infortunio e lo Stop se
esiste(optional) ed all'interno della funzione ti definisci
o ricavi le 2 date cardine...
Nella Query poi chiami la Function passando il Month(now) o il
Parametro selezionato nella Form, e le date di Start/Stop
dell'assenza....
Boh forse ho capito male...
@Alex

 ha scritto nel messaggio 
news:0fe5a924-96cc-48d5...@2g2000hsn.googlegroups.com...

Si, hai capito male... nel senso che hai detto tutto quello che la parte 
skippata fà.... ;) 


Ho l'impressione che alla "boccia" fai fare un sacco di giri
contaggNew(viene eseguita sempre perche' la iif e' "stupida")---> 
calcoladays (questa quando ci arriva cicla)--> fest (questa cicla a sua 
volta per ogni ciclo di calcoladays)
ci ho azzeccato?......che ho vinto?
Eliminare i due cicli interni sarebbe una buona cosa
Per quanto  mi riguarda (a meno che G. Rancati non abbia conigli nel 
cappello) me la farei via codice appoggiando il risultato su una tab 
risultato o direttamente su una list box. Se devi fare un report ti arrangi.
-- 
ac
y-1=x

 Pablitomf wrote:
[cut]
P.S. sorry non avevo notato che era per esperti ;<))))))))))))))
-- 
ac
y-1=x

Se devi fare un report ti arrangi.
No, devo far peggio... il risultato č la base di una miliardata di cose.
APPELLO x Giorgino : vieni a ESSEQUELLARE un pň tra noi sů... :-D 

 P.S. sorry non avevo notato che era per esperti ;<))))))))))))))
Eh, te lo volevo dire, ma poi ho pensato "mò mi diverto un pò e lo bastono 
alla prima falla" :D
Ciao Alessà 

 
-- 
ac
y-1=x

 datawharehouse
Hourse direi ;) 

 
-- 
ac
y-1=x

 CmpTipo (1:Malattia-2:Infortunio)
Cut
Prima di tutto aggiungerei di corsa un campo "giorni" alla tabella e 
qualsiasi calcolo tu faccia lo farei al momento di inserire la data fine 
evento...il calcolo dei giorni, poi, diventa solo una somma matematica. Per 
essere più precisi ti servirebbe anche una tabella Mesi/Persona/Tipo/Giorni 
(molti a uno con IDPersona) dove, via codice e all'inserimento di datafine, 
accoderai i giorni per mese nel caso di periodi a cavallo tra mesi o anche 
di un solo mese.
Se riesci a fare questo cioè ad applicare il calcolo dei giorni per ciascun 
record e mettere il risultato nel campo "giorni", secondo me elimini gran 
parte del codice che hai postato. Ovviamente fino a quando non ti sarai 
messo al passo, per una sola volta, dovrai ciclare sul recordset e 
aggiornare il campo "giorni" e "giorni/mese di ciascun record.
Addirittura potresti evitare il campo "giorni" della prima tabella se metti 
la seconda tabella che ho suggerito.
Che ne dici?
Se ti va bene vediamo di creare un algoritmo.
Ciao, Carlo 

 Se ti va bene vediamo di creare un algoritmo.
Diciamo che in origine era già così, ma poi per esigenze ho 
stravolto... quindi la strada non posso percorrerla se non ri-
stravolgo poi tutto...


 wrote:

Era quello che intendevo io... anche se forse non era chiaro... ma
Maurizio vuole la Botte ubriaca e la moglie piena... ;-)
In effetti sembrava anche a me la soluzione più performante...
@Alex

In effetti sembrava anche a me la soluzione più performante...
@Alex
Ciao, Carlo 

 -- 
Ok. Puoi sempre applicare questa logica (quella originale) "al volo" con 
tabelle temporanee esaminando record per record e "traslando" i calcoli su 
tempTbl.
Ciao, Carlo 

 Tnks a lot
Risolto :
FORM con listbox e pulsante : 
Private Sub MioPulsante_Click()
Dim Mal             As Long
Dim Inf             As Long
Dim I               As Integer
Dim cont            As Integer
Dim db              As DAO.Database
Dim rs              As DAO.Recordset
Dim InizioMese      As String
Dim FineMese        As String
GgFest = ""
Me.Elenco0.RowSource = "MESE;MAL;INF"
cont = 1
Set db = CurrentDb
    For I = 1 To Me.Cmp_mesi ' MESI
        Me.MioPulsante.Caption = "Mese: " & I
        InizioMese = "01/" & Format(I, "00") & "/08"
        FineMese = Format(DateAdd("d", -1, DateAdd("m", 1, CVDate
(InizioMese))), "mm/dd/yyyy")
        InizioMese = Format(InizioMese, "mm/dd/yyyy")
 
        Set rs = db.OpenRecordset("SELECT DataStart AS START, 
DataStop AS STOP,* FROM Tbl_Malattie WHERE DataStart<=#" & 
FineMese & "# AND DataStop>=#" & InizioMese & "#", 
dbOpenDynaset, dbSeeChanges)
            While rs.EOF = False
                With rs
                    If rs!CmpTipo = 1 Then Mal = Mal + 
contaggFORM(rs!start, rs!Stop, I)
                    If rs!CmpTipo = 2 Then Inf = Inf + 
contaggFORM(rs!start, rs!Stop, I)
                End With
                rs.MoveNext
                cont = cont + 1
            Wend
        Set rs = Nothing
        Me.Elenco0.RowSource = Me.Elenco0.RowSource & ";" & 
Format(I, "00") & ";" & Mal & ";" & Inf
        DoEvents
 
        Mal = 0
        Inf = 0
        cont = 1
    Next I
Set db = Nothing
Me.MioPulsante.Caption = "AVVIO"
End Sub
Private Sub Form_Open(Cancel As Integer)
Dim db      As DAO.Database
Dim rs      As DAO.Recordset
'=========================================
'Creo la stringa delle festività che poi mi serve per il 
confronto.
Set rs = CurrentDb.OpenRecordset("festività")
    While rs.EOF = False
        GgFest = GgFest & "|" & Format(rs!data, "dd/mm/yy")
        rs.MoveNext
    Wend
Set rs = Nothing
Set db = Nothing
'=========================================
End Sub
poi... MODULO DI CLASSE : 
'=========================================
' Dichiarazioni variabili pubbliche
'=========================================
Public gg_perse                 As Integer
Public GgFest                   As String
'=========================================
Public Function contaggFORM(xStart As Date, xStop As Date, 
mesRif As Integer) As Variant
    Dim PrimoGgMese         As Date
    Dim UltimoGgMese        As Date
    Dim contatore           As Long
    Dim maxdiff             As Long
    Dim trafsormDate        As Date
    Dim WEK                 As Integer
 
 
    PrimoGgMese = "01/" & Format(mesRif, "00") & "/08"
    UltimoGgMese = DateAdd("d", -1, DateAdd("m", 1, CVDate
(PrimoGgMese)))
 
    xStart = IIf([xStart] > PrimoGgMese, [xStart], PrimoGgMese)
    xStop = IIf([xStop] < UltimoGgMese, [xStop], UltimoGgMese)
 
 
        gg_perse = 0
        contatore = 0
 
        maxdiff = Int(DateDiff("d", xStart, xStop))
 
        For contatore = 0 To maxdiff
 
                'Aumento la data iniziale del contatore in giorni
                trafsormDate = CVDate(DateAdd("d", contatore, 
CVDate(Format(xStart, "dd/mm/yy"))))
 
                If InStr(1, GgFest, Format
(trafsormDate, "dd/mm/yy")) = 0 Then
                    WEK = Weekday(trafsormDate)
                    If WEK > 1 And WEK < 7 Then gg_perse = 
gg_perse + 1
                Else: End If
 
        Next contatore
 
    contaggFORM = gg_perse * 8
 
End Function
'=========================================
i 207 records esaminati prima, che impiegavano i loro 8-10 
secondi adesso sono processati in 0.22 secondi...
6 secondi li ha impiegati adesso per processare 54mila record e 
non 207...
passare le festività a una stringa (+ o - quello che diceva 
Alessandro) per poi fare il confronto ha abbattuto molto i tempi


Ma come....?
E' apparsa magicamente questa:
Public Function contaggFORM(xStart As Date, xStop As Date,
mesRif As Integer) As Variant
Aspè che rileggo quello che ti dissi per capire....!!!!
;-)
@Alex

 non 207...
troppo! ;<)
-- 
ac
y-1=x