Avatar billede tigerdyr2007 Praktikant
09. marts 2010 - 15:12 Der er 18 kommentarer og
1 løsning

Optimering

Hej

Jeg har et optimeringsproblem, det relaterer sig til spm http://www.eksperten.dk/spm/901908
Det er klart at nedenstående kode tager lang tid at afvikle (ca. 2 min for 1000 rekvirenter med 2000 kontakter). Og den tid vil jo vokse eksponentielt med udbygningen :-(
Har i nogen triks til hvordan det kan speedes op, eller gøres mere effektivt.
Opgaven er simpel, find alle dem som releterer til en post, tag deres initaler, og læg til en teksstreng i den pågældende post. Denne info skal opdateres ved nye/slettede relationer, og nemmest er jo at lave det hele igen.

Mvh.

Option Compare Database
Option Explicit

Public Function update_RR_contact_field()
On Error GoTo Errorhandler

    Dim db As DAO.Database
    Dim rst As DAO.Recordset
    Dim rst2 As DAO.Recordset
    Dim rst3 As DAO.Recordset
    Dim RR_contacts_string As String
    Dim sSQL As String
    Dim sSQL2 As String
    Dim sSQL3 As String
    Dim sSQL4 As String

    Dim temp As Integer
    temp = 0
    'DoCmd.Hourglass True
   
    'Vælg alle de rekvirenter som har en relation til RR-person
    sSQL = "SELECT tbl_rekvirent.ID, tbl_RR_person.forkortelse, tbl_rekvirent_RR_person.primarykontact, tbl_rekvirent_RR_person.julekort " & _
        "FROM tbl_rekvirent INNER JOIN (tbl_RR_person INNER JOIN tbl_rekvirent_RR_person ON tbl_RR_person.ID = tbl_rekvirent_RR_person.RR_personID) ON tbl_rekvirent.ID = tbl_rekvirent_RR_person.rekvirentID ORDER BY tbl_rekvirent.ID;"
    Set db = CurrentDb
    Set rst = db.OpenRecordset(sSQL, dbOpenSnapshot)
   
    DoCmd.SetWarnings False
   
    If Not rst.BOF Then rst.MoveFirst
    Do Until rst.EOF
        RR_contacts_string = ""
        'Find de primære relationer
        sSQL2 = "SELECT tbl_RR_person.forkortelse " & _
            "FROM tbl_rekvirent INNER JOIN (tbl_RR_person INNER JOIN tbl_rekvirent_RR_person ON tbl_RR_person.ID = tbl_rekvirent_RR_person.RR_personID) ON tbl_rekvirent.ID = tbl_rekvirent_RR_person.rekvirentID " & _
            "WHERE (((tbl_rekvirent.ID)=" & rst.Fields(0) & ") AND ((tbl_rekvirent_RR_person.primarykontact)=True));"
        Set rst2 = db.OpenRecordset(sSQL2, dbOpenSnapshot)
       
        If Not rst2.BOF Then rst2.MoveFirst
        Do Until rst2.EOF
            RR_contacts_string = RR_contacts_string & rst2.Fields(0) & " "
            'MsgBox rst2.Fields(0), vbOKOnly, RR_contacts_string & "rst2" ' testudlæsning
        rst2.MoveNext
        Loop
       
        'Find sekundære relationer
        sSQL3 = "SELECT tbl_RR_person.forkortelse " & _
            "FROM tbl_rekvirent INNER JOIN (tbl_RR_person INNER JOIN tbl_rekvirent_RR_person ON tbl_RR_person.ID = tbl_rekvirent_RR_person.RR_personID) ON tbl_rekvirent.ID = tbl_rekvirent_RR_person.rekvirentID " & _
            "WHERE (((tbl_rekvirent.ID)=" & rst.Fields(0) & ") AND ((tbl_rekvirent_RR_person.primarykontact)=False));"
        Set rst3 = db.OpenRecordset(sSQL3, dbOpenSnapshot)
       
        If Not rst3.BOF Then rst3.MoveFirst
        Do Until rst3.EOF
            RR_contacts_string = RR_contacts_string & rst3.Fields(0) & " "
            'MsgBox rst3.Fields(0) & " - " & rst.Fields(0), vbOKOnly, RR_contacts_string & "rst3" ' testudlæsning
        rst3.MoveNext
        Loop
       
        RR_contacts_string = Left(RR_contacts_string, Len(RR_contacts_string) - 1) 'Fjern sidste charecter
       
        'MsgBox RR_contacts_string, vbOKOnly, rst.Fields(0) ' testudlæsning
        'Opdater feltet rekvirenttabellen
        sSQL4 = "UPDATE tbl_rekvirent SET tbl_rekvirent.RR_contacts = '" & RR_contacts_string & "' WHERE (((tbl_rekvirent.ID)=" & rst.Fields(0) & "));"
        DoCmd.RunSQL sSQL4
       
        'Afbryder til begrænset test
        'temp = temp + 1
        'If temp = 5 Then
        '    GoTo Exit_fcn
        'End If
    rst.MoveNext 'næste rekvirent
    Loop
   
    GoTo Exit_fcn
   
Exit_fcn:
    'DoCmd.Hourglass = False
    DoCmd.SetWarnings True
    Set db = Nothing
    Set rst = Nothing
    Set rst2 = Nothing
    Set rst3 = Nothing
    Exit Function

Errorhandler:
    MsgBox Err, vbOKOnly, "Fejl"
    GoTo Exit_fcn
   
End Function
Avatar billede janus_007 Nybegynder
09. marts 2010 - 15:34 #1
Du kunne evt. lave det med en stored procedure! (terry is wrong here)

Det som nok er den største performancekiller her, er Access i sig selv, men på den anden side.. hvor ofte skal du trække data ud sådan?

Den bedste løsning ville nok være at forklare trykkeriet omkring data-relationer og så levere det som Xml :)
Avatar billede terry Ekspert
09. marts 2010 - 15:42 #2
(terry is wrong here)
How can I be wrong here if I havent made any comments here? :o)

And in Access you cant make stored procedures!
Avatar billede tigerdyr2007 Praktikant
09. marts 2010 - 15:44 #3
I think janus_007 is refering to you coment in my previous thread... I will still be very happy to recieve any comments.
Avatar billede janus_007 Nybegynder
09. marts 2010 - 17:59 #4
Jo man kan da godt lave sprocs i Access (Terry is wrong here, again) :-P

tigerdyr -> En måde du måske kunne speede det op på er ved at bruge memory lidt mere, tag denne her eks.vis:

sSQL2 = "SELECT tbl_RR_person.forkortelse " & _
            "FROM tbl_rekvirent INNER JOIN (tbl_RR_person INNER JOIN tbl_rekvirent_RR_person ON tbl_RR_person.ID = tbl_rekvirent_RR_person.RR_personID) ON tbl_rekvirent.ID = tbl_rekvirent_RR_person.rekvirentID " & _
            "WHERE (((tbl_rekvirent.ID)=" & rst.Fields(0) & ") AND ((tbl_rekvirent_RR_person.primarykontact)=True));"

For hver række i rst laver du en where-clause og eksekverer imod basen, jeg ville nok hellere læse det ind på en gang og så bruge de indbyggede .Filter på de ADO Recordsets der, altså lave et stort join, hvis du forstår :)
Avatar billede terry Ekspert
09. marts 2010 - 18:15 #5
stored queries in Access are the equivalent of Stored procedures in MSSQL.
http://www.stardeveloper.com/articles/display.html?article=2001050101&page=1

But they are NOT the same. Stored procedures in SQL server are MUCH more powerfull than what Access is capable of. In Access it is just SQL stataments, in SQL server you can execute code.

So if you mean that an Access query is the same as a stored procedure than OK, I'm wrong :o)
Avatar billede terry Ekspert
09. marts 2010 - 18:17 #6
again
Avatar billede janus_007 Nybegynder
09. marts 2010 - 18:23 #7
Jamen det ved jeg da godt :-P Jeg har nu heller aldrig påstået at man kunne skrive T-sql deri, anyway.. lad os nu prøve at finde en god løsning til tigerdyret :)
Avatar billede tigerdyr2007 Praktikant
09. marts 2010 - 19:25 #8
Hehe, har jeg nu fået aktiveret en hel diskussion af Access vs MYSQL

Jeg vil lige se om jeg har forstået dig ret janus_007. Giver det lige et shot, men er ikke 100 på hvad du mener.
Avatar billede janus_007 Nybegynder
09. marts 2010 - 21:06 #9
Hvad præcist er du ikke med på?

Hvis du ikke er så meget på at lave en stort join, så hent som det mindste alle records ud, sådan her:

sSQL = "SELECT tbl_rekvirent.ID, tbl_RR_person.forkortelse, tbl_rekvirent_RR_person.primarykontact, tbl_rekvirent_RR_person.julekort " & _
        "FROM tbl_rekvirent INNER JOIN (tbl_RR_person INNER JOIN tbl_rekvirent_RR_person ON tbl_RR_person.ID = tbl_rekvirent_RR_person.RR_personID) ON tbl_rekvirent.ID = tbl_rekvirent_RR_person.rekvirentID ORDER BY tbl_rekvirent.ID;"

Behold den, og så i din anden:
sSQL2 = "SELECT tbl_RR_person.forkortelse " & _
            "FROM tbl_rekvirent INNER JOIN (tbl_RR_person INNER JOIN tbl_rekvirent_RR_person ON tbl_RR_person.ID = tbl_rekvirent_RR_person.RR_personID) ON tbl_rekvirent.ID = tbl_rekvirent_RR_person.rekvirentID " & _
            "WHERE (((tbl_rekvirent.ID)=" & rst.Fields(0) & ") AND ((tbl_rekvirent_RR_person.primarykontact)=True));"
        Set rst2 = db.OpenRecordset(sSQL2, dbOpenSnapshot)

Den skriver du om til:
sSQL2 = "SELECT tbl_RR_person.forkortelse " & _
            "FROM tbl_rekvirent INNER JOIN (tbl_RR_person INNER JOIN tbl_rekvirent_RR_person ON tbl_RR_person.ID = tbl_rekvirent_RR_person.RR_personID) ON tbl_rekvirent.ID = tbl_rekvirent_RR_person.rekvirentID " & _
            "WHERE tbl_rekvirent_RR_person.primarykontact=True;"
        Set rst2 = db.OpenRecordset(sSQL2, dbOpenSnapshot)

Det henter du bare ud og så ved at bruge .Filter på Recordsettet kan du se at tingene kører langt hurtigere end når du laver en 'where' og eksekverer hver gang.
Avatar billede tigerdyr2007 Praktikant
10. marts 2010 - 10:00 #10
Hmm, jeg er nok et fjols, men jeg kan ikke finde ud af at bruge .filter, får hele tiden en fejl...
Jeg har indsat hele den opdaterede kode nedenunder igen.
Og jeg ved godt at rst2 og rst3 kan være det samme hvis jeg blot udbygger filter en smule. Vil lige have det til at funke først.

Her er hvad jeg gør
        strfilter = "tbl_rekvirent.ID = " & rst.Fields(0)
        rst2.filter = strfilter
        Set rstnew2 = rst2.OpenRecordset
       
        If Not rst2.BOF Then rst2.MoveFirst
        Do Until rst2.EOF
            RR_contacts_string = RR_contacts_string & rst2.Fields(0) & " "
            'MsgBox rst2.Fields(0), vbOKOnly, RR_contacts_string & "rst2" ' testudlæsning
        rst2.MoveNext
        Loop


Det er linien "Set rstnew2 = rst2.OpenRecordset" der giver fejl.

--------------------------------------
Hele koden:
--------------------------------------

Public Function update_RR_contact_field()
'On Error GoTo Errorhandler

    Dim db As DAO.Database
    Dim rst As DAO.Recordset
    Dim rst2 As DAO.Recordset
    Dim rst3 As DAO.Recordset
   
    Dim rstnew2 As DAO.Recordset
    Dim rstnew3 As DAO.Recordset
   
    Dim RR_contacts_string As String
    Dim sSQL As String
    Dim sSQL2 As String
    Dim sSQL3 As String
    Dim sSQL4 As String

    Dim temp As Integer
    temp = 0
    DoCmd.Hourglass True
    Dim strfilter As String
   
    'Vælg alle de rekvirenter som har en relation til RR-person
    sSQL = "SELECT tbl_rekvirent.ID, tbl_RR_person.forkortelse, tbl_rekvirent_RR_person.primarykontact, tbl_rekvirent_RR_person.julekort " & _
        "FROM tbl_rekvirent INNER JOIN (tbl_RR_person INNER JOIN tbl_rekvirent_RR_person ON tbl_RR_person.ID = tbl_rekvirent_RR_person.RR_personID) ON tbl_rekvirent.ID = tbl_rekvirent_RR_person.rekvirentID ORDER BY tbl_rekvirent.ID;"
    Set db = CurrentDb
    Set rst = db.OpenRecordset(sSQL, dbOpenSnapshot)
   
    DoCmd.SetWarnings False

    sSQL2 = "SELECT tbl_RR_person.forkortelse " & _
        "FROM tbl_rekvirent INNER JOIN (tbl_RR_person INNER JOIN tbl_rekvirent_RR_person ON tbl_RR_person.ID = tbl_rekvirent_RR_person.RR_personID) ON tbl_rekvirent.ID = tbl_rekvirent_RR_person.rekvirentID " & _
        "WHERE tbl_rekvirent_RR_person.primarykontact=True;"
    Set rst2 = db.OpenRecordset(sSQL2, dbOpenSnapshot)
       
    sSQL3 = "SELECT tbl_RR_person.forkortelse " & _
        "FROM tbl_rekvirent INNER JOIN (tbl_RR_person INNER JOIN tbl_rekvirent_RR_person ON tbl_RR_person.ID = tbl_rekvirent_RR_person.RR_personID) ON tbl_rekvirent.ID = tbl_rekvirent_RR_person.rekvirentID " & _
        "WHERE tbl_rekvirent_RR_person.primarykontact=False;"
    Set rst3 = db.OpenRecordset(sSQL3, dbOpenSnapshot)


    If Not rst.BOF Then rst.MoveFirst
    Do Until rst.EOF
        RR_contacts_string = ""
       
        'MsgBox "hmm"
        strfilter = "tbl_rekvirent.ID = " & rst.Fields(0)
        rst2.filter = strfilter
        Set rstnew2 = rst2.OpenRecordset
       
        If Not rst2.BOF Then rst2.MoveFirst
        Do Until rst2.EOF
            RR_contacts_string = RR_contacts_string & rst2.Fields(0) & " "
            'MsgBox rst2.Fields(0), vbOKOnly, RR_contacts_string & "rst2" ' testudlæsning
        rst2.MoveNext
        Loop
       
        'Find sekundære relationer
        strfilter = "tbl_rekvirent.ID = " & rst.Fields(0)
        rst3.filter = strfilter
        Set rstnew3 = rst3.OpenRecordset
       
        If Not rst3.BOF Then rst3.MoveFirst
        Do Until rst3.EOF
            RR_contacts_string = RR_contacts_string & rst3.Fields(0) & " "
            'MsgBox rst3.Fields(0) & " - " & rst.Fields(0), vbOKOnly, RR_contacts_string & "rst3" ' testudlæsning
        rst3.MoveNext
        Loop
       
        RR_contacts_string = Left(RR_contacts_string, Len(RR_contacts_string) - 1) 'Fjern sidste character
       
        'MsgBox RR_contacts_string, vbOKOnly, rst.Fields(0) ' testudlæsning
        'Opdater feltet i rekvirenttabellen
        sSQL4 = "UPDATE tbl_rekvirent SET tbl_rekvirent.RR_contacts = '" & RR_contacts_string & "' WHERE (((tbl_rekvirent.ID)=" & rst.Fields(0) & "));"
        DoCmd.RunSQL sSQL4
       
        'Afbryder af loop til begrænset test
        temp = temp + 1
        If temp = 5 Then
            GoTo Exit_fcn
        End If
    rst.MoveNext 'næste rekvirent
    Loop
   
    GoTo Exit_fcn
   
Exit_fcn:
    DoCmd.Hourglass False
    DoCmd.SetWarnings True
    Set db = Nothing
    Set rst = Nothing
    Set rst2 = Nothing
    Set rst3 = Nothing
    Exit Function

Errorhandler:
    MsgBox Err, vbOKOnly, "Fejl"
    GoTo Exit_fcn
   
End Function
Avatar billede janus_007 Nybegynder
10. marts 2010 - 12:50 #11
Når filteret er sat, så skal du ikke åbne recordsettet.
Dvs.

'MsgBox "hmm"
        strfilter = "tbl_rekvirent.ID = " & rst.Fields(0)
        rst2.Filter = strfilter
       
        If Not rst2.BOF Then rst2.MoveFirst
        Do Until rst2.EOF
            RR_contacts_string = RR_contacts_string & rst2.Fields(0) & " "
            'MsgBox rst2.Fields(0), vbOKOnly, RR_contacts_string & "rst2" ' testudlæsning
        rst2.MoveNext
        Loop


:)
Avatar billede tigerdyr2007 Praktikant
10. marts 2010 - 15:36 #12
Hmm, ja nu kører det, men har ikke rigtigt sparet noget, det tager stadig ca. samme tid, måske en anelse hurtigere, men ikke noget revolutionerende...
Avatar billede tigerdyr2007 Praktikant
10. marts 2010 - 19:55 #13
Hov, jo man skal åbne recordset! Fandt lige ud af den ikke havde filtereret, bare lagt alle data ind i hvert eneste felt.
Fra Access hjælp:

"Recordset.Filter Property: Sets or returns a value that determines the records included in a subsequently opened Recordset object (Microsoft Access workspaces (Access workspace only). Read/write String."
Avatar billede janus_007 Nybegynder
11. marts 2010 - 01:19 #14
hej tigerdyr.

Nope... se her: http://www.asp101.com/tips/index.asp?id=86 , godt nok er det lang tid siden jeg arbejdede med ASP, men jeg syntes nu nok at kunne huske :)

Det undrer mig en del at det ikke går hurtigere, det lyder ekstremt anderledes nemlig.

Prøv evt. også at rette din cursortype til adOpenForwardOnly :)

Derudover kunne jeg godt tænke mig du bare tog tid på den første med og uden Filter, og egentlig også tid på open connection og OpenRecordSet, sådan at vi få nogle mere afskærmede informationer.
Avatar billede janus_007 Nybegynder
11. marts 2010 - 01:20 #15
DAO -> dbOpenForwardOnly
Avatar billede tigerdyr2007 Praktikant
11. marts 2010 - 10:33 #16
Hey janus_007, det er altså ikke rigtigt. Mit citat er taget fra MS Access hjælp, og hvis jeg ikke gør som jeg har beskrevet får jeg ikke rst. filtreret først, og får alle records med.
ASP er vel heller ikke det samme som Access...

Jeg takker dog meget for indsatsen, og vil prøve med tidsmålinger, dbOpenForwardOnly etc. senere.
Avatar billede janus_007 Nybegynder
11. marts 2010 - 14:20 #17
hmm næh nej det har du jo ret i :)
Avatar billede tigerdyr2007 Praktikant
14. marts 2010 - 20:24 #18
Har leget lidt med tidsmåling på openrecordset og dbOpenForwardOnly, umiddelbart giver de begge metoder samme tid.
Avatar billede tigerdyr2007 Praktikant
22. august 2010 - 14:22 #19
Ingen andre har givet et svar, jeg lukker.
Avatar billede Ny bruger Nybegynder

Din løsning...

Tilladte BB-code-tags: [b]fed[/b] [i]kursiv[/i] [u]understreget[/u] Web- og emailadresser omdannes automatisk til links. Der sættes "nofollow" på alle links.

Loading billede Opret Preview
Kategori
Dyk ned i databasernes verden på et af vores praksisnære Access-kurser

Log ind eller opret profil

Hov!

For at kunne deltage på Computerworld Eksperten skal du være logget ind.

Det er heldigvis nemt at oprette en bruger: Det tager to minutter og du kan vælge at bruge enten e-mail, Facebook eller Google som login.

Du kan også logge ind via nedenstående tjenester