Newsgroup: it.comp.lang.visual-basic
Titolo: [?] ciclo che esce troppo presto

situazione:
tabella con un campo data "giorno" e uno numerico "totgiorno"
il ciclo qui sotto dovrebbe sommare tutti i valori di totgiorno,
disponendoli per
* tot anno.mese
* tot anno
il ciclo va anche, ma esce troppo presto: non considera i valori di maggio
2000, per il quale ci sono 15 righe nella tabella.
come gliela spiego ?
(se qualcuno vuole fare delle prove, gli mando la tabella, 35k. ma uno piu'
sveglio di me dovrebbe vederlo subito :)
grazie in anticipo, a.
Private Sub cmdMonthDay_Click()
    On Error GoTo errman
    Dim rs As Recordset
    Dim oY As Integer, oM As Byte
    Dim cntY As Integer, cntM As Integer
    Dim outNum As Integer, outname As String
    outNum = FreeFile
    outname = MyPath & "stat-y-m.txt"
    Open outname For Output As #outNum
    Print #outNum, outname & " - " & Now
    Print #outNum,
    Set rs = MainDB.OpenRecordset("contdata")
    With rs
        .Index = "giorno"
        Do While Not .EOF
            cntY = 0: oY = Year(!giorno)
            Do While Year(!giorno) = oY
                cntM = 0: oM = Month(!giorno)
                Do While Month(!giorno) = oM
                    Caption = !giorno
                    cntM = cntM + !totgiorno
                    cntY = cntY + !totgiorno
                    .MoveNext
                    DoEvents
                Loop
                Print #outNum, oY & "_" & oM & ": " & cntM & " ---> (prox: "
& !giorno & ")"
            Loop
            Print #outNum, "---> " & oY & ": " & cntY & " ---> (prox: " &
!giorno & ")"
        Loop
    End With
    Close #outNum
    rs.Close: Set rs = Nothing
    Dim conf As Long
    conf = ShellExecute(Me.hwnd, vbNullString, _
        outname, vbNullString, MyPath, SW_SHOWNORMAL)
    Exit Sub
errman:
    Close #outNum
    rs.Close: Set rs = Nothing
End Sub
ultime righe del risultato:
1999_1: 318 ---> (prox: 01/02/1999)
1999_2: 262 ---> (prox: 01/03/1999)
1999_3: 329 ---> (prox: 01/04/1999)
1999_4: 307 ---> (prox: 01/05/1999)
1999_5: 373 ---> (prox: 01/06/1999)
1999_6: 278 ---> (prox: 01/07/1999)
1999_7: 331 ---> (prox: 01/08/1999)
1999_8: 254 ---> (prox: 01/09/1999)
1999_9: 559 ---> (prox: 01/10/1999)
1999_10: 613 ---> (prox: 01/11/1999)
1999_11: 1391 ---> (prox: 01/12/1999)
1999_12: 1098 ---> (prox: 01/01/2000)
---> 1999: 6113 ---> (prox: 01/01/2000)
2000_1: 1839 ---> (prox: 01/02/2000)
2000_2: 1809 ---> (prox: 01/03/2000)
2000_3: 1858 ---> (prox: 01/04/2000)
2000_4: 1593 ---> (prox: 01/05/2000)

    End With
Roal Zanazzi

 grazie in anticipo, a.
[cut]


 tabella con un campo data "giorno" e uno numerico "totgiorno"
intanto, grazie a roal e alessandra per le risposte.
risolta nel modo barbaro: goto
( il terzo che uso in vita mia, direi, e sempre per la storia dell'eof() )
il codice:
    Set rs = MainDB.OpenRecordset("contdata")
    With rs
        .Index = "giorno"
        xTot = 0: xCnt = 0
        Do While Not .EOF
            cntY = 0: totY = 0: oY = Year(!giorno)
            Do While Year(!giorno) = oY
                cntM = 0: totM = 0: oM = Month(!giorno)
                Do While Month(!giorno) = oM
                        Caption = !giorno
                        xCnt = xCnt + 1
                        xTot = xTot + !totgiorno
                        cntM = cntM + 1
                        cntY = cntY + 1
                        totM = totM + !totgiorno
                        totY = totY + !totgiorno
                        .MoveNext
If .EOF Then GoTo uscita
                        DoEvents
                Loop
                Print #OutNum, oY & "-" & oM & ": " & _
                    cntM & "C - " & totM & "T"
            Loop
            Print #OutNum, "---> " & oY & ": " & _
                cntY & "C - " & totY & "T"
        Loop
uscita:
        Print #OutNum, oY & "-" & oM & ": " & _
            cntM & "C - " & totM & "T"
        Print #OutNum, "---> " & oY & ": " & _
            cntY & "C - " & totY & "T"
        Print #OutNum,
        Print #OutNum, "Tot: " & xCnt & "C - " & xTot & "T"
    End With
il risultato (ci mette 5" per 1644 schede):
1999-1: 31C - 318T
1999-2: 28C - 262T
1999-3: 31C - 329T
1999-4: 30C - 307T
1999-5: 31C - 373T
1999-6: 30C - 278T
1999-7: 31C - 331T
1999-8: 31C - 254T
1999-9: 30C - 559T
1999-10: 31C - 613T
1999-11: 30C - 1391T
1999-12: 31C - 1098T
---> 1999: 365C - 6113T
2000-1: 31C - 1839T
2000-2: 29C - 1809T
2000-3: 31C - 1858T
2000-4: 30C - 1593T
2000-5: 15C - 559T
---> 2000: 136C - 7658T
Tot: 1644C - 20005T
i risultati vanno bene ( lo so per altre vie
ma si vede anche dai giorni dei mesi:
come dire, dei computer mi fido mica troppo :)
sulle query, ci avevo pensato, poi avevo deciso che per ottenere un
risultato tipo questo avrei comunque dovuto annidarle, e a occhio non mi
sembra si sarebbe pulito molto il codice. certo, cosi' ci perde in
portabilita', suppongo. e' invece giusto il fatto che tocca il fondo, ma ero
certo che avesse gia' incamerato i valori e cercavo il modo di farglieli
dire. e' un problema tipico delle ricorsioni: o all'inizio o alla fine,
molto spesso il codice si deve duplicare.
nell'ultima riga avrebbe dovuto mostrare non il primo giugno ma il 16
maggio, che non c'era, quindi andava in eof(), quindi ...
ora mi chiedo se e come si potrebbe ottimizzare, ma anch'io sono molto -ehm-
stanco...
ciao, a.

 come gliela spiego ?
[...]
Classico esempio di hard-code, ovvero codice alloopato.
Scritto in VBxxx. Fagli (fatti?) una doccia fredda e parti dal
problema fondamentale: "per ogni record in ...". Quindi:
    Set rs = MainDB.OpenRecordset("contdata")
    With rs
      If .BOF And .EOF Then
        ' Nessun record...
      Else
        Do While Not .EOF
          '
          ' Qui, solamente qui, farai
          ' le tue considerazioni...
          '
          Call .MoveNext
        Loop
      End If
      Call .Close
    End With: Set rs = Nothing
--
Ciao.
Maurizio

     End With: Set rs = Nothing
a'mmauri', o che non capisco piu' gnente io...
o, se ci pensi bene, il tuo codice e' in sostanza uguale al mio. "le mie
considerazioni" infatti stavano perlopiu' dentro al while !eof(). quelle al
di fuori e anche il goto label servono a gestire l'uscita dalla ricorsione,
che l'eof() appunto inchiodava.
quanto alla coppia bof/eof non l'ho messa perche' ero certo che la tabella
schede ne avesse.
la linea giusta era in fondo quella di roal (e'tte pareva !? :), anche se il
mio poco sql non mi permetteva di vedere la soluzione (ottimizzata: anche la
mia in qualche modo andava), che mi ha gentilmente spedito di pacca andrea
d'errico, e che allego sotto. con un po' di prove, rispetto al ciclo do
while, l'uso delle query migliora di un 50-75%.
riguardando il codice qui sotto: qual e' esattamente la diff tra
* while not eof
* until eof
???
grazie e ciao, a.
sql:
---> 1999: 365C - 6113T
2000-1: 31C - 1839T
2000-2: 29C - 1809T
2000-3: 31C - 1858T
2000-4: 30C - 1593T
2000-5: 15C - 559T
Time: 0,156
ciclo:
---> 1999: 365C - 6113T
2000-1: 31C - 1839T
2000-2: 29C - 1809T
2000-3: 31C - 1858T
2000-4: 30C - 1593T
2000-5: 15C - 559T
---> 2000: 136C - 7658T
Tot: 1644C - 20005T
Time: 0,219
Private Sub Command1_Click()
    Dim time1: time1 = Timer
    Dim sSql As String
    Dim rs As Recordset
    Dim oY As Integer, oM As Byte
    Dim totY As Integer, totM As Integer
    Dim cntY As Integer, cntM As Byte
    Dim xTot As Long, xCnt As Long
    Dim OutNum As Integer, OutName As String
    OutNum = FreeFile
    OutName = MyPath & "stat-y-m-sql.txt"
    Open OutName For Output As #OutNum
    Print #OutNum, OutName & " - " & Now
    Print #OutNum,
    totY = 0
    sSql = "SELECT Year([giorno]) AS Anno, Month([giorno]) AS Mese," & _
    "Count(contdata.giorno) AS CntXMese, Sum(contdata.totgiorno) AS TotXMese
" & _
    "From contdata " & _
    "GROUP BY Year([giorno]), Month([giorno]);"
    Set rs = MainDB.OpenRecordset(sSql, dbOpenSnapshot)
    rs.MoveFirst
    oY = rs!Anno
    Do Until rs.EOF
        If oY <> rs!Anno Then
            Print #OutNum, "---> " & oY & ": " & cntY & "C - " & totY & "T"
            cntY = 0: totY = 0: oY = rs!Anno
        End If
        cntY = cntY + rs!CntXMese
        totY = totY + rs!TotXMese
        Print #OutNum, oY & "-" & rs!Mese & ": " & rs!CntXMese & "C - " &
rs!TotXMese & "T"
        rs.MoveNext
    Loop
    Print #OutNum, "Time: " & Format(Timer - time1, "##0.###")
    Close #OutNum
    rs.Close: Set rs = Nothing
    Dim Conf As Long
    Conf = ShellExecute(Me.hwnd, vbNullString, _
        OutName, vbNullString, MyPath, SW_SHOWNORMAL)
    Exit Sub
errman:
    Close #OutNum


 Per curiosita' (mia), prova cosi' (e fammi sapere il risultato):
eseguito cinque triplette di test con 100k record (ogni volta nella
successione ciclo, sql-1, sql-rz). a parita' di conteggi:
ciclo:
Time: 9,059
Time: 8,898
Time: 8,84
Time: 8,848
Time: 8,73
sql 1
Time: 5,98
Time: 5,93
Time: 6,051
Time: 6,148
Time: 5,988
sql r.z.
Time: 5,828
Time: 5,93
Time: 5,871
Time: 5,93
Time: 5,82
non dovrebbe, ma, servisse, posso ripostare i sorgenti. perche'
(sfacciatissimo!) avrei una domanda: in quale punto l'sql r.z. si
avvantaggia sull'altro di quel pelino ?
grazie e ciao, a.

 appunto inchiodava. [...]
--
Ciao.
Maurizio
P.S. ;-) e :-) un po' dappertutto, ovviamente!...