简体   繁体   中英

I need to create an Insert pass-through query to SQL Server in Access VBA

I need to create a pass-through query to SQL Server. I have a functioning pass-through query which only runs from the navigation bar or query design in Access, and it only creates a single record. I have dozens of records to insert into a SQL Server table based on values in an Access table. Thus, I need to loop through the Access table in code, and for each record there create the SQL string and execute it. I've got the looping working, just need the proper code to connect to the SQL database and execute the SQL string. Here's the ODBC Connect String in the pass-through query that works, with the PW and DB starred out: "ODBC;DSN=jobboss32; UID=support; PWD=****;DATABASE=****". The code below gives a Data Type Conversion Error.

Dim strFuturePTOSource, strPassThruInsert, strEmpName, strPTODate, strUpdated As String
Dim datPTODate As Date, lngPTOHours As Long
Dim rs As New ADODB.Recordset, q As QueryDef

    strFuturePTOSource = "qryROBERT"    'temporary data set to test, change to actual after working
    rs.Open strFuturePTOSource, CurrentProject.Connection, adOpenDynamic, adLockOptimistic
    With rs
        If Not .BOF And Not .EOF Then
            .MoveLast
            .MoveFirst

            While (Not .EOF)
                strEmpName = rs.Fields("EmpName")
                strEmpName = "'" & strEmpName & "', "
                datPTODate = rs.Fields("PTODate")
                strPTODate = "'" & Format(datPTODate, "mm/dd/yyyy") & "'"
                lngPTOHours = rs.Fields("PTOHrs") * 60
                strUpdated = "'" & Format(Now(), "mm/dd/yyyy") & "'"
                strPassThruInsert = "INSERT INTO Attendance (Attendance, Employee, Work_Date, Regular_Minutes, Attendance_Type, Lock_Times, Source, Last_Updated) VALUES (NewID(), "
                strPassThruInsert = strPassThruInsert & strEmpName & strPTODate & ",lngPTOHours,2,-1,0, '" & Now() & "');"
                Set q = CurrentDb.CreateQueryDef("", strPassThruInsert)
                CurrentDb.QueryDefs(q).Execute
                .MoveNext
            Wend
        End If
        .Close
    End With

First, you not given ANY valid reason to insert with a pass-through query.

And in fact, since EACH insert will be a 100% and separate and “discrete” insert?

Well, the time for the query processor to setup, parse the syntax, and do the insert?

Well, you not achieve ANY gains in performance, and in fact it will run no faster than a plain Jane and standard insert using DAO recodsets.

As a result: You are knowingly as a developer wasting all kinds of time pursuing this approach and this is AFTER having been given this advice on places such as UA.

And if you knowingly are wasting developer time, and seeking an approach that costs more developer time, does not speed up your insets, then you are with full knowledge seeking an approach that costs more time, and more resources than necessary.

I suppose if you are paying for this time, and don't care, or perhaps you are doing this as a learning exercise, then fine. But as for a working solution, the approach does not make sense, takes more code and time to develop and fix, and you are doing this will full knowledge as to the increased cost and time without any benefits I can see, or think of.

So then this begs the question? Why would you attempt a solution will FULL knowledge that costs more time, costs more effort, and does not increase performance? You do not given any reason as to the added benefits of this approach.

The following code is far less, far more simple, will run FASTER than your given approach. And FAR better is the date conversions that you have (are WRONG), are a non-issue.

So, you have to cook up a VERY good reason as to WHY you are perusing this course of action when:

It will not run faster than using the code I post here (In fact, such a PT query will run slower – and by A WIDE margin).

You have errors in your date formats – you have to use ISO sql server format (But, if you use ODBC and let the record set do the translation, then ZERO formatting and ZERO re-formatting of the date and date time values you have is NOT required).

So, until such time you explain why you are willing with full knowledge to write more code, cost more time, and cook up a solution that wastes time, and will not run faster?

Then I see little if any reason to pursue your approach?, right?

So, you need to come up with a VERY good reason as to why you are insisting on this pass-through query approach, and an approach that will run NOT run faster than what I am posting here... And in fact, as I point out, the posted solution will run MUCH faster. (Not I said a wee bit faster – but MUCH faster – in fact on a multiple order (not %, but factors of times faster). I can explain WHY this is the case (better speed) if you are wondering why.

In the meantime, you are going to have to figure out how to sleep with full knowledge of pursuing a solution that is going to cost more in developer time, and not yield any benefits in terms of speed, or even maintenance and writing of such code.

This code eliminates the date error conversions you have, and WILL RUN faster than your posted code and solution:

Dim strFuturePTOSource  As String

Dim rstFrom             As DAO.Recordset  ' from table
Dim rstTo               As DAO.Recordset  ' SQL server linked table.

Set rstFrom = CurrentDb.OpenRecordset("qryROBERT", dbOpenDynaset, dbSeeChanges)
Set rstTo = CurrentDb.OpenRecordset("Attendance", dbOpenDynaset, dbSeeChanges)

Do While rstFrom.EOF = False
   With rstTo
      .AddNew
      !Attendance = NewID()
      !Employee = rstFrom!EmpName
      !Work_Date = rstFrom!PTODate
      !Regular_Minutes = 600
      !Attendance_Type = 2
      !Lock_Times = -1
      !Source = 0
      !Last_UPdated = Now
      .Update
   End With
   rstFrom.MoveNext
Loop
rstTo.Close
rstFrom.Close

Edit

Given that the poster HAS made a good case that a PT query is to be used?

then this code should work:

We assume that you ALREADY created a working PT query. (and it has return records set = false). I tend to create ONE PT query in Access, and then any and all places in code can re-use that PT query at well. Also if NewID() is a scalar function (t-sql), as noted, it MUST be prefixed with dbo. So, we have to use dbo.NewID()

So, the code that is "close" or that I would suggest is this:

Dim rs         As DAO.Recordset
Dim strSQL     As String
Dim strSQLS    As String

strSQLS = "INSERT INTO Attendance (Attendance, Employee, Work_Date, Regular_Minutes, " & _
         "Attendance_Type, Lock_Times, Source, Last_Updated) " & _
         "VALUES (dbo.NewID(),"


Set rs = CurrentDb.OpenRecordset("qryROBERT")

With rs

  Do While .EOF = False
  
     strSQL = strSQLS
     
     strSQL = strSQL & qu(!EmpName) & "," & quDate(!PTODate) & _
              "," & quDate(Date) & "," & (!PTOHrs * 60) & ",2,-1,0," & quDate(Now())
              
     With CurrentDb.QueryDefs("MyPassQuery")
        .SQL = strSQL
        .Execute
     End With
  
     .MoveNext
  Loop
  rs.Close
  
End With
  

In addtion to the above, I used two helper functions to format the date, as it is a pain to do this "in-line" code, so, I have qu() (for strings), and qudate() for dates.

Public Function qudate(myDate As Variant) As String

  ' returns a formatted string of date, ISO format, for sql sesrver.
  ' format is yyyy-mm-dd regardless of local date settings

  If IsNull(myDate) = True Then
     qudate = ""
  Else
     ' use ISO date format
     qudate = "'" & Format(myDate, "yyyy-mm-dd HH:NN:SS") & "'"
  End If

End Function

And our qu() function

Function quS(vText As Variant) As String
  
  ' takes a string and surrounds it with single quotes
  
  If IsNull(vText) = False Then
     If InStr(vText, Chr(34)) > 0 Then
        vText = Replace(CStr(vText), Chr(34), "'")
     End If
  End If

  quS = "'" & vText & "'"
  
End Function

Edit 2

So, the steps are: From sql studio, get a example working that inserts data. Once you have a working insert command, then take that same (and known) working command and cut + paste it into an access query - but just ensure that the query on the Access side is created and set as pass-though. Once again, test running this PT query. If the existing sql worked in SSMS, then it will work in Access 100% exactly with the SAME query. Once that is working?

so, whatever name you gave this query is what you use in place of MyPassQuery. You can give it any name you want, but you have to use the correct (same) name in your VBA code that going to use/set the sql for that PT query. So, each loop run will in fact overwrite what is in the query, and then you do the.Execute to run it.

Well, then you run your code. And on this line for testing?

.SQL = strSQL

Do this:

debug.print strSQL
.SQL = strSQL

So, as you single step though that code, the sql the VBA is creating will have to match the known working sql you had. So, if the string output has any syntax errors or does not look 100% the same as that known working sql? Well, then you have to tweak the VBA code until such time it spits out the SAME sql string that you know works.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM