27. december 2006 - 20:00Der er
48 kommentarer og 1 løsning
Recordset - loop
Hej med jer,
Jeg har forsøgt at udligne nogle poster i en tabel. Dette gør jeg ved at lave to recordset, et med negative poster(KreditRS og et andet med positive poster(ModrRS). Fidusen skulle være, at jeg tager den første negative post i yderste loop og udligner med den første positive post i inderste loop som give et netto = 0. Derefter skal det inderste loop resettes og der skal laves et nyt positiv recordset, som ikke medtager de tidligere. På den måde håber jeg at kunne udgå at løbe recordsetne igennem mange gange.
Mit problem er, at jeg ikke kan få den inderste loop til at hoppe ud og lave et nyt recordset.
Det er lidt svært at forklare, og min programeringsevner er ikke så gode.
Håber nogen kan hjælpe.
Her er koden:
Sub ADOUdl()
Dim KreditRS As New Recordset Dim ModrRS As New Recordset
KreditRS.Open "select * from tbltest where netto<0 and AnnulleretStatus=0 order by EksNr", CurrentProject.Connection, adOpenDynamic, adLockOptimistic
KreditRS.MoveFirst Do While Not KreditRS.EOF Debug.Print KreditRS("AutoID") ModrRS.Open "Select * from tbltest where Netto>=0 and AnnulleretStatus=0 order by Eksnr", CurrentProject.Connection, adOpenDynamic, adLockOptimistic ModrRS.MoveFirst Do While Not ModrRS.EOF If ModrRS("Netto") + KreditRS("Netto") = 0 Then Debug.Print ModrRS("AutoID") ModrRS("AnnulleretStatus").Value = 1 ModrRS.Update KreditRS("AnnulleretStatus").Value = 1
ModrRS.MoveLast Else ModrRS.MoveNext End If Loop KreditRS.MoveNext Loop 'KreditRS ModrRS.Close Set ModrRS = Nothing KreditRS.Close Set KreditRS = Nothing End Sub
Uden at have fingrene i din tabel / kode kan jeg kun se at du i hvert fald mangler en KreditRS.Update efter KreditRS("AnnulleretStatus").Value = 1 Ellers vil du vel få den samme record hele tiden i din yderste loop.
Hvis du vil have et nyt recordset for hver record i den yderste loop er du nødt til at lukke det inderste recordset inde i din loop vil jeg tro (ModrRS.Close Set ModrRS = Nothing) ellers burde du få en fejl hver gang du looper og laver ModrRS.Open som jo allerede er åben.
Jeg er ikke sikker på, at det vil løse hele dit problem, men jeg ville starte med at erstatte dit
ModrRS.MoveLast
i det inderste loop med
ModrRS.Close Exit Do
Close lukker recordsettet og Exit Do lader dig gå ud af Do loopet uden at der er ModrRS.EOF
Men iøvrigt er det jo lidt tungt sådan at lukke og åbne et recordset, så jeg synes du skulle overveje at bruge .Find funktionen i stedet inde i det ydre loop
ModrRS.Find "AnnulleretStatus=0 And Netto=" & KreditRS.Fields("Netto")
Sådanne som jeg har kunne læse mig frem til, så kan metoden Find kun bruges på en kolonne,og jeg har 4 kolonner som skal matche - EksNr, AnnulleretStatus, VareNr og Netto.
Koden herunder gør som den skal, men kommer med fejlen:
Runtime Error 3021, Enten er BOF eller EOF sand, eller den aktuelle post er blevet slettet. Den anmodede handling kræver en aktuel post.
på ModrRS ved sidste gennemløb, og jeg kan ikke gennemskue hvorfor.
Private Sub ADOSoeg()
Dim KreditRS As New Recordset Dim ModrRS As New Recordset
KreditRS.Open "select * from tbltest where netto<0 and AnnulleretStatus=0 order by EksNr", CurrentProject.Connection, adOpenDynamic, adLockOptimistic KreditRS.MoveFirst Do While Not KreditRS.EOF Debug.Print KreditRS("AutoID") ModrRS.Open "Select * from tbltest where Netto>=0 and AnnulleretStatus=0 and EksNr in(select EksNr from tbltest where netto<0 and AnnulleretStatus=0 order by EksNr) order by Eksnr", CurrentProject.Connection, adOpenDynamic, adLockOptimistic ModrRS.MoveFirst Do While Not ModrRS.EOF If ModrRS("Netto") + KreditRS("Netto") = 0 And ModrRS("VareNr") = KreditRS("VareNr") And ModrRS("EksNr") = KreditRS("EksNr") Then Debug.Print ModrRS("AutoID") ModrRS("AnnulleretStatus").Value = 1 ModrRS.Update KreditRS("AnnulleretStatus").Value = 1 KreditRS.Update ModrRS.MoveLast ModrRS.MoveNext Else ModrRS.MoveNext End If Loop 'ModrRS ModrRS.Close Set ModrRS = Nothing KreditRS.MoveNext Loop 'KreditRS
ModrRS.Close Set ModrRS = Nothing KreditRS.Close Set KreditRS = Nothing
Det er ikke testet, men det jeg tænkte på mht. ADO instruktionen .Find var noget lignende dette her:
Private Sub ADOSoeg()
Dim KreditRS As New ADODB.Recordset Dim ModrRS As New ADODB.Recordset Dim Kriterie As String KreditRS.Open "select * from tbltest where netto<0 and AnnulleretStatus=0 order by EksNr", CurrentProject.Connection, adOpenDynamic, adLockOptimistic ModrRS.Open "Select * from tbltest where Netto>=0 and AnnulleretStatus=0 and EksNr in (select distinct EksNr from tbltest where netto<0 and AnnulleretStatus=0) order by Eksnr", CurrentProject.Connection, adOpenDynamic, adLockOptimistic KreditRS.MoveFirst Do While Not KreditRS.EOF Debug.Print KreditRS("AutoID") Kriterie = "Varenr = " & KreditRS("VareNr") & "And Eksnr = " & KreditRS("EksNr") & "And Netto = " & -KreditRS("Netto") & " And AnnulleretStatus=0" ModrRS.Find Kriterie, , adSearchForward 'Find første række hvor kriteriet er opfyldt... If Not ModrRS.NoMatch Then Debug.Print ModrRS("AutoID") ModrRS("AnnulleretStatus").Value = 1 ModrRS.Update KreditRS("AnnulleretStatus").Value = 1 KreditRS.Update End If KreditRS.MoveNext Loop 'KreditRS
ModrRS.Close Set ModrRS = Nothing KreditRS.Close Set KreditRS = Nothing
Hej Så er din db på vej retur - jeg blev kaldt ud til en opgave så jeg var fraværende nogen timer :-) Koden jeg har lavet bør optimeres/tilrettes lidt men jeg synes det ser ud til at den gør det den skal. Jeg vil lige kigge lidt mere på det ved lejlighed.
Jeg ved ikke om det er aktuelt, men du kunne jo også prøve at bruge gode gamle DAO i stedet (du skal måske ind at tilføje DAO under referencer, før du kan bruge det):
Private Sub DAOSoeg()
Dim KreditRS As DAO.Recordset Dim ModrRS As DAO.Recordset Dim Kriterie As String Set KreditRS = CurrentDb.OpenRecordset("select * from tbltest where netto<0 and AnnulleretStatus=0 order by EksNr") Set ModrRS = CurrentDb.OpenRecordset("Select * from tbltest where Netto>=0 and AnnulleretStatus=0 and EksNr in (select distinct EksNr from tbltest where netto<0 and AnnulleretStatus=0) order by Eksnr") KreditRS.MoveFirst Do While Not KreditRS.EOF Debug.Print KreditRS("AutoID") Kriterie = "Varenr = " & KreditRS("VareNr") & "And Eksnr = " & KreditRS("EksNr") & "And Netto = " & -KreditRS("Netto") & " And AnnulleretStatus=0" ModrRS.FindFirst Kriterie 'Find første række hvor kriteriet er opfyldt... If Not ModrRS.NoMatch Then Debug.Print ModrRS("AutoID") ModrRS.Edit ModrRS("AnnulleretStatus").Value = 1 ModrRS.Update KreditRS.Edit KreditRS("AnnulleretStatus").Value = 1 KreditRS.Update End If KreditRS.MoveNext Loop 'KreditRS
ModrRS.Close Set ModrRS = Nothing KreditRS.Close Set KreditRS = Nothing
'On Error Resume Next Dim rst_Kredit As New ADODB.Recordset Dim rst_Modregn As New ADODB.Recordset
Dim strSQL_Kredit As String Dim strSQL_Modregn As String
strSQL_Kredit = "SELECT * FROM tbltest WHERE netto<0 AND AnnulleretStatus=0 ORDER BY EksNr" strSQL_Modregn = "SELECT * FROM tbltest WHERE netto>=0 AND AnnulleretStatus=0 ORDER BY EksNr"
Do While Not rst_Kredit.EOF Debug.Print rst_Kredit("AutoID") rst_Modregn.Open strSQL_Modregn, CurrentProject.Connection, adOpenDynamic, adLockOptimistic rst_Modregn.MoveFirst Do While Not rst_Modregn.EOF If (rst_Modregn("Netto") + rst_Kredit("Netto") = 0) And (rst_Modregn("VareNr") = rst_Kredit("VareNr")) And (rst_Modregn("EksNr") = rst_Kredit("EksNr")) Then Debug.Print rst_Modregn("AutoID") 'Modregnet af post nr. rst_Modregn!fldID = rst_Kredit!AutoID rst_Modregn("AnnulleretStatus").Value = 1 rst_Modregn.Update
rst_Kredit("AnnulleretStatus").Value = 1 rst_Kredit.Update Exit Do Else rst_Modregn.MoveNext End If Loop
rst_Kredit.MoveNext
rst_Modregn.Close Set rst_Modregn = Nothing Loop 'rst_kredit
"Hej kjulius, Det fungere stadigvæk ikke rigtigt, men jeg syntes ideen er god." -- Ja, idéen er god - det er bare udførelsen, der halter..
"Her er Hugopedersens forslag, som fungere fint." -- Fint, det er jo hovedsagen. :-)
"Jeg tror jeg vil arbejde videre med hugopedersens forslag, og takker for hjælpen." -- Helt i orden. :-)
Det irriterer mig lidt, at jeg ikke fik oprettet et par testdata, så jeg ku' have leveret en ordentlig "vare". Men du fik jo den nødvendige hjælp fra anden side, så... vi må videre!
Hej Kjulius, det er da stadigvæk muligt at fået en kopi af min testdatabase. Jeg har kørt hugopedersens på den originale, og den fungere fint, men en kørsel tager omkring et par timer. Hvilke måske nok er forståeligt, da der jo laves et nyt recordset for hver loop.
Okay, one more shot at it! Jeg kom til at tænke på, at recordsættet i det indre loop tager alt for mange rækker med. Det må kunne begrænses på samme måde som jeg foreslog med min .Find/.Findfirst:
Private Sub ADOSoeg()
'On Error Resume Next Dim rst_Kredit As New ADODB.Recordset Dim rst_Modregn As New ADODB.Recordset
Dim strSQL_Kredit As String Dim strSQL_Modregn As String
strSQL_Kredit = "SELECT * FROM tbltest WHERE netto<0 AND AnnulleretStatus=0 ORDER BY EksNr" strSQL_Modregn = "SELECT * FROM tbltest WHERE netto>=0 AND AnnulleretStatus=0 ORDER BY EksNr"
Do While Not rst_Kredit.EOF Debug.Print rst_Kredit("AutoID") strSQL_Modregn = "SELECT * FROM tbltest WHERE netto=" & cstr(- rst_Kredit("Netto")) & " AND AnnulleretStatus=0 AND varenr=" & cstr(rst_Kredit("VareNr")) & " AND EksNr=" & cstr(rst_Kredit("EksNr"))
rst_Modregn.Open strSQL_Modregn, CurrentProject.Connection, adOpenDynamic, adLockOptimistic If Not rst_Modregn.EOF Debug.Print rst_Modregn("AutoID") 'Modregnet af post nr. rst_Modregn!fldID = rst_Kredit!AutoID rst_Modregn("AnnulleretStatus").Value = 1 rst_Modregn.Update
rst_Kredit("AnnulleretStatus").Value = 1 rst_Kredit.Update End If
rst_Modregn.Close rst_Kredit.MoveNext
Loop 'rst_kredit
rst_Kredit.Close Set rst_Kredit = Nothing Set rst_Modregn = Nothing End Sub
Der er nu kun ét loop, da selve kriteriet sikrer, at der kun selekteres rækker fra det tidligere indre loop, som opfylder udligningskriteriet. Kriteriet for det indre recordsæt er med andre ord nu dynamisk. Det burde alt andet lige være hurtigere end at indlæse en masse rækker, hvoraf kun én bruges, hvorefter det lukkes og åbnes igen.
Hej kjulius, Der er et par problemer i din kode, som jeg ikke rigtig forstå. 1. strSQL_Modregn optræder 2 gange. 2. Hvor er det man finder posten til at modregne.
Jeg får i øvrigt følgenden fejl, når jeg køre koden: Run-time erro '-214721793 (80040e07)': Datatyperne stemmer ikke overens i kriterieudtrykket.
i følgende linie: rst_Modregn.Open strSQL_Modregn, CurrentProject.Connection, adOpenDynamic, adLockOptimistic
ad 1) Det er rigtigt, at strSQL_Modregn bliver tildelt en streng to steder. Det er en fejl, at jeg ikke fik slettet den første tildeling. Det er et levn fra din (eller rettere hugopedersens) oprindelige kode. Men rent praktisk har det ingen betydning, da den jo bare får tildelt en ny "betydning" efterfølgende, uden at have været brugt.
ad 2) Det må betyde, at et af de felter, jeg har antaget var numeriske (et talfelt) i virkeligheden er et karakter-felt. Hvis f.eks. VareNr er et karakterfelt i databasen, skal værdien i strSQL_Modregn strengen indkapsles i anførselstegn (').
Hvordan er definitionen af felterne i tabellen? Hvordan ser strSQL_Modregn ud lige før rst_Modregn.Open?
Do While Not rst_Kredit.EOF Debug.Print rst_Kredit("AutoID") strSQL_Modregn = "SELECT * FROM tbltest WHERE netto=" & cstr(- rst_Kredit("Netto")) & " AND AnnulleretStatus=0 AND varenr='" & rst_Kredit("VareNr") & "' AND EksNr=" & cstr(rst_Kredit("EksNr"))
rst_Modregn.Open strSQL_Modregn, CurrentProject.Connection, adOpenDynamic, adLockOptimistic If Not rst_Modregn.EOF Debug.Print rst_Modregn("AutoID") 'Modregnet af post nr. rst_Modregn!fldID = rst_Kredit!AutoID rst_Modregn("AnnulleretStatus").Value = 1 rst_Modregn.Update
rst_Kredit("AnnulleretStatus").Value = 1 rst_Kredit.Update End If
rst_Modregn.Close rst_Kredit.MoveNext
Loop 'rst_kredit
rst_Kredit.Close Set rst_Kredit = Nothing Set rst_Modregn = Nothing End Sub
Ooops, der er én ting mere jeg ikke havde helt styr på. Når man bruger CStr funktionen, returneres en streng som tager hensyn til systemopsætningen. Det betyder f.eks. at et beløb på 100 kr. og 25 øre bliver returneret som 100,25 (hvilket er perfekt i en dansk sammenhæng). Desværre bruger SQL konsekvent amerikansk formattering. Derfor duer det ikke. Heldigvis kan man i stedet ty til den gamle Str funktion, som vil returnere det som 100.25, hvilket er hvad SQL forventer.
Derfor bør SQL strengen ændres til:
strSQL_Modregn = "SELECT * FROM tbltest WHERE netto=" & str(- rst_Kredit("Netto")) & " AND AnnulleretStatus=0 AND varenr='" & rst_Kredit("VareNr") & "' AND EksNr=" & str(rst_Kredit("EksNr"))
Jeg burde sikkert ikke gøre det, i hvert fald ikke før du har meldt tilbage med resultatet af testen på SQLserveren. Det kan måske forvirre for meget. Men jeg gør det nu alligevel - kommer med et helt nyt koncept for opdateringen. Denne gang baseret på en lidt mere avanceret SQL med subqueries til at selektere de poster der skal udlignes:
Sub Udlign() Dim strUpdateSQL As String Dim strSQL As String Dim rs As New ADODB.Recordset Dim cn As New ADODB.Connection cn = CurrentProject.Connection strSQL = "SELECT *" & vbCrLf & _ "FROM Testdata3 AS P, Testdata3 AS M" & vbCrLf & _ "WHERE P.Annulleretstatus=0 And P.Netto>=0" & vbCrLf & _ " AND M.AutoID = (SELECT nz(MIN(AutoID),0) FROM Testdata3 WHERE (P.Annulleretstatus=Annulleretstatus) AND (P.Netto=-Netto) AND (P.EksNr=EksNr) AND (P.Varenr=Varenr) AND (P.Dato=Dato))" & vbCrLf & _ " AND P.AutoID = (SELECT nz(MIN(AutoID),0) FROM Testdata3 WHERE (M.Annulleretstatus=Annulleretstatus) AND (M.Netto=-Netto) AND (M.EksNr=EksNr) AND (M.Varenr=Varenr) AND (M.Dato=Dato))" rs.Open strSQL, cn, adOpenKeyset, adLockReadOnly Do While Not rs.EOF strUpdateSQL = "UPDATE Testdata3 SET AnnulleretStatus = 1 WHERE AutoID = " & rs.Fields("P.AutoId") & " OR AutoId = " & rs.Fields("M.AutoId") 'Opdater post og modpost med annuleretstatus cn.Execute strUpdateSQL rs.MoveNext Loop rs.Close Set rs = Nothing Set cn = Nothing End Sub
Den nyeste "opfindelse" gør brug af subqueries til at selektere den række med det mindste AutoId, som "passer" til en anden post. På den måde kan man sammensætte posterne, også selv om der skulle være flere potentielle "partnere". Forespørgslen finder altså i første omgang kun de "første" poster der skal udlignes. Men da disse poster efter at vare sat til udlignet vil droppe ud af forespørgslens "scope", så træder den næste potentielle post/modpost ind i billedet, da det nu er nogle andre poster, som har det laveste AutoId. På den måde vil alle poster, som skal udlignes blive udpeget.
Hvis man altså bare udfører forespørgslen for at kigge på de poster den vælger til udligning, vil den kun vise de første potentielle udligningskandidater i en transaktion selvom der er flere. Men efter at den første er udlignet, vil den efterfølgende pludselig være synlig.
Jeg ved ikke om metoden er mere optimal i forhold til de tidligere, men potentialet er der...
Jeg har ikke rigtig kunne konverter din kode til min sqlforespørgelse, men da den forrige fungere fint, syntes jeg du skal have nogen point for dette arbejde. Lad mig lige høre om du er interesseret.
Helt i orden. Nej, det med point er sådan set lidt underordnet. Det var mere opgaven i sig selv, der var interessant. Så behold du bare dine point. :-)
Jamen så vil jeg bare sige tak for hjælpen. Kanon arbejde.
Synes godt om
Ny brugerNybegynder
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.