I've created a VBA script that updates the CommandText of an SQL connection in order to pass a parameter from Excel to a stored procedure - this works fine except that the query doesn't update itself..
Private Sub CommandButton1_Click()
Dim BillDate As Date
Dim BillDateFormat As String
BillDate = Sheets("Sheet1").Range("B4").Value
BillDateFormat = Format(BillDate, "yyyy-mm-dd")
With ActiveWorkbook.Connections("BillDateConnection").OLEDBConnection
.CommandText = "EXEC TTKWBillingTest #BillDate = '" & BillDateFormat & "'"
End With
ActiveWorkbook.Connections("BillDateConnection").Refresh
End Sub
I'm finding that the data only refreshes on the first refresh and subsequent refreshes update the CommandText but the data on the worksheet does not refresh.
I've tried adding..
ActiveWorkbook.RefreshAll
but this doesn't make any difference.
Any ideas?
Put code in ThieWorkbook object so that it fires when Excel opens
Private Sub Workbook_open()
For Each objConnection In ThisWorkbook.Connections
'Get current background-refresh value
bBackground = objConnection.OLEDBConnection.BackgroundQuery
'Temporarily disable background-refresh
objConnection.OLEDBConnection.BackgroundQuery = False
objConnection.Refresh 'Refresh this connection
'Set background-refresh value back to original value
objConnection.OLEDBConnection.BackgroundQuery = bBackground
Next
End sub
NB This can cause problems if the data connection is set to update on open! Best to remove this option from the connection manager!
To update via a button:
Private Sub Cmd_RefreshData_Click()
Application.Run "'" & ThisWorkbook.Name & "'!ThisWorkbook.Workbook_Open"
ActiveWorkbook.RefreshAll
Lbl_LastRefresh_Click
End Sub
Lbl_LastRefresh uses Now() to display the last refresh time
...I figured this out.
'Enable background refresh' needs to be deselected in the connection properties.
The table refreshes every time without this option.
Related
I'm trying to retrieve and update rows in Excel/SQL and found this script which works but I tried to make it update as soon as I enter the "existencia" value instead of hitting run on the update macro, found the Worksheet change function but I'm not sure how to add it to this macro.
Private Sub Worksheet_Change(ByVal Target As Range)
Dim con As New ADODB.Connection
Dim cmd As New ADODB.Command
Dim rst As New ADODB.Recordset
Dim i As Long
Dim vDB As Variant
Dim Ws As Worksheet
con.ConnectionString = "Provider=SQLOLEDB; data source=LAPTOP\SQLEXPRESS;initial catalog=Inventario;Integrated Security=SSPI;"
con.Open
Set cmd.ActiveConnection = con
Set Ws = ActiveSheet
'The assumption is that the data on the Excel sheet is listed from a1 Cell, including fields.
vDB = Ws.Range("a1").CurrentRegion
For i = 2 To UBound(vDB, 1)
cmd.CommandText = "UPDATE Productos SET Existencia='" & vDB(i, 4) & "' WHERE id_cod=" & vDB(i, 1) & " "
cmd.Execute
Next i
con.Close
Set con = Nothing
End Sub
I have used the Worksheet_Change event to do a lot of things, but I have never ever used Worksheet_Change to update data in a SQL Server table. This doesn't really make sense, if you think about it. You can make all kinds of changes in an Excel file, all day long. People do this all the time. When you are totally done with your updates, changes, or whatever, you can push the data to a table in a database. You should not do this intermittently. Otherwise, you will have to do all kinds of cleanup in your database table(s). You don't want to create this kind of overhead for yourself.
I have an Access application (Access Front-end, SQL backend using Linked tables) and I'm having this issue:
The user enters in a part number. Then they enter in a quantity. New business logic says I have to check this part number against a table, to see if we have quoted it to a customer within the past year. If we have, we can quote it now; if not, we have to reject the part.
When the program was originally written, they used a datasheet format to allow the user to copy from Excel a list of parts, paste them into the datasheet, and then copy and paste in a list of quantities. Once the quantity is entered/pasted for a line, that line is processed.
If I manually enter in a part number, then the quantity, the needed code checks to see if the part number has been quoted within the last year, and everything is peachy.
But if I copy and paste in a list of parts, the first time through the process it works fine; but every other time fails.
Here's the basic code:
Dim cn21 As ADODB.Connection
Set cn21 = New ADODB.Connection
Dim strsql21 As String
Dim cm21 As New ADODB.Command
Dim rs21 As New ADODB.Recordset
' gblODBCString = "ODBC;Description=PartsPortalsSql;DRIVER=SQL Server;SERVER=db-TEST-partsptl-primary;Trusted_Connection=Yes;APP=Microsoft Office 2010;DATABASE=PartsPortalSQL;"
cn21.Open gblODBCString
cm21.ActiveConnection = cn21
' All I want to know is how many records there are for this part within the last year...
strsql21 = "Select count(*) from tblQuoteDetail tqd INNER JOIN tblQuotes tq on tqd.quoteid = tq.quoteid WHERE " _
& " tqd.qdetailpartno = '" & Me.QDetailPartNo & "' AND tq.quotesentdate >= '" & OneYearAgo & "' AND tqd.qdetailunitprice > 0"
cm21.CommandText = strsql21
Set rs21 = New ADODB.Recordset
Set rs21 = cn21.Execute(strsql21, varparams, adCmdText)
If rs21(0) = 0 Then ' nothing found! can't be escalated...
blah blah blah...
end if
' done with this part... clean up
Set cm21 = Nothing
rs21.Close
Set rs21 = Nothing
Set cn21 = Nothing
Then it will return here when it gets to the next part...
But if i do it manually, it works fine. But when it is running through the loop of parts, it gets to the:
Set rs21 = cn21.Execute(strsql21, varparams, adcmdtext)
line
and takes about 30 seconds to 'process/time out', and then the rs21(0) returns "Run-time error '3265': Item cannot be found in the collection corresponding to the requested name or ordinal."
I've verified every field in the sql query is correctly populated. I've never run into this issue before.
Other things I've attempted - use DAO instead of ADODB... no luck...
If anyone has any suggestions I'm all ears... short of blowing it up... I'm in the testing phase of a replacement version, that doesn't use that copy/paste functionality. But that's still a month or two away from production.
Thanks
Access 2016, Linked tables to Microsoft SQL Server 2016 I believe...
Windows 10 64 bit
Also running on a VPN, connected to a VDI.
I htink youre using the wrong commands to check for recordsets. I could be wrong as I dont use ADO as much as I should, but here is my template for it if you thin itll help
'needs the MSO AtiveX Data Objects Library
Dim vbSql As String, cnnstr as string
Dim cnn As ADODB.Connection
Dim rs As New ADODB.Recordset
vbSql = "SELECT ;"
Set cnn = New Connection
cnnstr = ""
cnn.Open cnnstr
rs.CursorLocation = adUseClient
rs.Open vbSql, cnn
cnn.Close
Set cnn = Nothing
I have a multi user Access UI linked to a SQL Server database that is used to perform loan reviews for a mortgage company. Users are currently using the 'Find Record' function that already exists in Access and it takes a very long time to locate the record. While I do understand why it takes so long and what it theoretically needs to do, I can't figure out the best way to write code to do directly to the record needed. It needs to be a message box similar to the existing find record function, but that performs the most efficient way. I tried the following, which doesn't include a message box, and it doesn't work:
Private Sub txtFindLoan_AfterUpdate()
Dim sSQL As String
sSQL = "SELECT * FROM dbo_PreCloseAuditEnc WHERE LOAN_NUM = '" &
Me.txtFindLoan & "'"
Me.RecordSource = sSQL
Me.Requery
End Sub
Thanks in advance!
There is no bottleneck, except that you don't need a requery. And, of course, make sure you have an index on the LOAN_NUM field.
You could have an InputBox to pick up the loan number. The code could be called from a button:
Private Sub LoanButton_Click()
Dim sLoanNo As String
Dim sSQL As String
sLoanNo = InputBox("Loan Number:", "Select Loan Number")
If sLoanNo <> "" Then
' Input provided.
' Validate input.
' IsValidLoanNumber will be a function to do this. Must return True/False.
If IsValidLoanNumber(sLoanNo) Then
sSQL = "SELECT * FROM dbo_PreCloseAuditEnc WHERE LOAN_NUM = '" & sLoanNo & "'"
Me.RecordSource = sSQL
End If
End If
End Sub
You're probably off best using ADODB since you're only using external data, and binding the form directly to a filtered ADODB recordset.
As Sean Lange said, you should parameterize your query, and you can do that either using DAO or ADO, but not by modifying the Me.RecordSource property
Private Sub txtFindLoan_AfterUpdate()
Dim sSQL As String
'I'm assuming your table name in MS SQL is dbo.PreCloseAuditEnc
sSQL = "SELECT * FROM dbo.PreCloseAuditEnc WHERE LOAN_NUM = ?"
Dim adoConn As New ADODB.Connection
'Enter a valid OLEDB connection string here:
adoConn.Open "Provider=SQLNCLI11;Server=myServerAddress;Database=myDataBase;Trusted_Connection=yes;"
Dim cmd As New ADODB.Command
cmd.ActiveConnection = adoConn
cmd.CommandText = sSQL
cmd.Parameters.Append cmd.CreateParameter(Type:=adChar, Value:=Me.txtFindLoan)
Me.RecordSet = cmd.Execute
End Sub
I am trying to navigate through records (next and prev) in a form that i have created in Microsoft Access 2013. The database is connected to SQL Server 2008. First i have loaded the database for the table using SQL:
Private Sub Form_Load()
Dim strSQL As String
Dim dbs As DAO.Database
Dim Rs As DAO.Recordset
Set dbs = CurrentDb()
strSQL = " SELECT [dbo_tblRank].* " & _
" FROM [dbo_tblRank] "
Set Rs = dbs.OpenRecordset(strSQL, dbOpenDynaset)
Me.rankNo.Value = Rs![rankNo]
Me.rankName.Value = Rs![rankName]
Me.rankDescription.Value = Rs![rankDescription]
Me.noOfRequiredDivings.Value = Rs![noOfRequiredDivings]
End Sub
Now i have created a 'next' button, which i would like to update the following fields to the next values. I have written the code (which doesn't do anything):
Private Sub btnNext_Click()
Me.Recordset.MoveNext
End Sub
What Am i doing wrong?
The issue is that the form is unbound. As a result of this, you've had to write more code in form_open than would have been required were the form/controls bound; Similarly then, in btnNext_Click, you will again have to write more code.
Re-create the recordset, find the current record in the recordset, then move to the next record, and then re-populate your unbound controls from the new record in this new recordset.
Another way would be for you to make you recordset public so that you don't have to reconnect each time.
You will still have to write code to find the current record so that you can move pass it in your btnNext_Click event.
The easy way would be to bound your Form...
I am doing some maintenance work on a linked-table application in Microsoft Access 2010 and experiencing this little gem of a problem. The application is linked to a SQL Server 2008 database. The application has a main form that allows a user to choose a combination of park code and resource and pops up an edit form for the details of that particular combination. If that combo doesn't exist in the database, the application inserts a new record in, but the issue is that 2 records get inserted.
Here's the seat of the problem code, it gets called when I need to insert a new record in a details popup form:
Private Sub New_Rec(unit_code As String, resource As String, sql As String)
DoCmd.RunSQL ("INSERT INTO PARK_RESOURCES (unit_code, resource, sensitivity) VALUES " _
& "('" & unit_code & "','" & resource & "','public')")
'Force an explicit save
'http://www.pcreview.co.uk/forums/update-cancelupdate-without-addnew-edit-t1150554.html
If Me.Dirty Then
Me.Dirty = False
End If
Me.RecordSource = sql
End Sub
Creating a "new" record results in 2 records getting inserted into the Recordset. It doesn't seem to matter if I move the explicit save code before or after setting the RecordSource. In either order (and stopping after either) produces 2 new records inserted in the database (verified by querying in SSMS).
When I set the RecordSource property and step through the code, the event chain looks like: Me.RecordSource = sql --> Form_BeforeUpdate() --> Form_AfterUpdate() --> Form_After_Insert() --> Form_Current(). The duplicate is not present at the close of BeforeUpdate, but by the time I get to AfterUpdate, the duplicate has already been inserted. What happens between BeforeUpdate and AfterUpdate that causes this to happen?
According to MSDN, the order is: BeforeInsert → BeforeUpdate → AfterUpdate → AfterInsert. They also state that setting the value of a control through Visual Basic doesn't trigger these events. But when I update the RecordSource in code, the last 3 events certainly fire; BeforeInsert is the only one that a step-through doesn't stop on.
As per Daniel Cook's request, here is the calling code.
Private Sub Form_Load()
On Error GoTo Err_Form_Load
Me.KeyPreview = True
If Not IsNull(Me.OpenArgs) Then
ProcessOpenArgs (Me.OpenArgs)
Me.lblHeader.Caption = Me.unit_code & ": Resource - " & Me.resource
Else
Me.lblHeader.Caption = "Information Needs"
End If
... (error trapping)
End Sub
And the ProcessOpenArgs sub (OpenArgs get set as "park;resource"):
Private Sub ProcessOpenArgs(open_args As String)
On Error GoTo Err_ProcessOpenArgs
Dim Args() As String
Args = Split(open_args, ";")
Me.unit_code = Args(0)
Me.resource = Args(1)
'Check to see if there are records in the database for current unit/resource combo'
Dim rs As DAO.Recordset
Dim sql As String
sql = "SELECT * FROM PARK_RESOURCES " & _
"WHERE resource='" & Me.resource & "' AND unit_code='" & Me.unit_code & "'"
Set rs = CurrentDb.OpenRecordset(sql, dbOpenDynaset, dbSeeChanges)
'if there aren''t, create a new record'
If (rs.RecordCount = 0) Then
New_Rec Me.unit_code, Me.resource, sql
Else 'go to the current listing'
Me.RecordSource = sql
End If
Exit_ProcessOpenArgs:
Exit Sub
Err_ProcessOpenArgs:
MsgBox Err.Number & Err.description
Resume Exit_ProcessOpenArgs
End Sub
I will continue to comb through the event documentation and as a last resort I may go totally nuts and just stick every possible event in my VBA code and step through them, but does anyone know what could be happening to cause the duplicates?
In your ProcessOpenArgs, the If (rs.RecordCount = 0) Then line could be a problem unless you first use rs.MoveLast - see here
When I'm setting Me.unit_code and Me.unit here:
Private Sub ProcessOpenArgs(open_args As String)
On Error GoTo Err_ProcessOpenArgs
Dim Args() As String
Args = Split(open_args, ";")
Me.unit_code = Args(0)
Me.resource = Args(1)
the code is creating 1 record and then New_Rec inserts a second record in the DB. When the Form automatically Requeries after Me.RecordSource = sql, it sticks the first record (created by the Me.xxx = yyyy statements in ProcessOpenArgs into the DB too and then pulls both back out to the Form Recordset. That's where the double insert is coming from.
In order to correct it, I changed Me.unit_code and Me.resource to local subroutine variables l_unit_code and l_resource and used those instead in ProcessOpenArgs. That took care of this problem as well as a second problem that I had with records form one resource type bleeding into other resource types.
Thanks all for the assist!