VBA issues when trying to run a SQL query between 2 dates - sql-server-2008

To give a brief summary of the objective of this macro, I am trying to get the sum of cash between certain dates based on a set of parameters. I keep getting an error in VBA when I try to run a SQL query with a date. I'm not sure what the issue is but I think it's related to how I have the date formatted. I have tried running it multiple ways but keep getting a syntax error, whether it be related to 'a', '#', or 'table'.
Here's the query I'm using. Any help would be greatly appreciated.
Sub GetCashBalance()
Dim cn As New ADODB.Connection
Dim rs As ADODB.Recordset
Dim SQL As String
Dim StartDate As String
Dim EndDate As String
StartDate = InputBox("Enter Start Date in mm/dd/yy format.")
EndDate = InputBox("Enter End Date in mm/dd/yy format.")
SQL = "Provider=SQLOLEDB.1;Integrated Security=SSPI;Persist Security Info=True;Initial Catalog=master;Data Source=SERVER\ODS"
Set cn = New ADODB.Connection
Set rs = New ADODB.Recordset
cn.Open SQL
Set rs = cn.Execute("SELECT SUM(a.CASH)" & _
"FROM CUSTOMER_DATA.dbo.TRANSACTION_HISTORY a" & _
"LEFT JOIN CUSTOMER_DATA.dbo.DAILY_TRANSACTION b" & _
"ON a.T01_KEY = b.T01_KEY" & _
"WHERE PROC_DATE BETWEEN #StartDate# AND #EndDate#" & _
"AND a.CODE NOT IN ('22','23','M','2-L','36-R')" & _
"AND isnull(a.DESCRIPTION, '') NOT IN ('01','02','03','0DO1','0NF2');")
If Not rs.EOF Then
Sheets(7).Range("A40").CopyFromRecordset rs
rs.Close
Else
MsgBox "Error", vbCritical
End If
cn.Close
End Sub

Set rs = cn.Execute("SELECT SUM(a.CASH)" & _
"FROM CUSTOMER_DATA.dbo.TRANSACTION_HISTORY a" & _
"LEFT JOIN CUSTOMER_DATA.dbo.DAILY_TRANSACTION b" & _
"ON a.T01_KEY = b.T01_KEY" & _
"WHERE PROC_DATE BETWEEN '" & StartDate & "' AND '" & EndDate & _
"# AND a.CODE NOT IN ('22','23','M','2-L','36-R')" & _
"AND isnull(a.DESCRIPTION, '') NOT IN ('01','02','03','0DO1','0NF2');")
Take a look at below piece, that should be changed
BETWEEN '" & StartDate & "' AND '" & EndDate & _
"' AND ....
I think it is better to use ADODB.Command and Parameters instead of doing a concatenation in the sql query.

Related

Use value in Excel cell as condition for WHERE clause in SQL query

Sub Get_Data()
Dim conn As New ADODB.Connection
Dim rs As New ADODB.Recordset
Dim dateVar As Date
Set conn = New ADODB.Connection
conn.ConnectionString = "DRIVER={MySQL ODBC 5.1 Driver}; SERVER=localhost; DATABASE=bi; UID=username; PWD=password; OPTION=3"
conn.Open
strSQL = " SELECT " & _
" Products " & _
" FROM Logistics " & _
" WHERE DATE(insert_timestamp) = ""2020-02-24"" " & _
" GROUP BY 1"
Set rs = New ADODB.Recordset
rs.Open strSQL, conn, adOpenStatic
Sheet5.Range("A1").CopyFromRecordset rs
rs.Close
conn.Close
End Sub
I run the above VBA to extract values fom the database.
It works exactly how I need it.
Now, instead of having a pre-defined date within strSQL I want to use the data that is entered into Cell B1 in my Excel spreadsheet. Therefore, I changed the strSQL part in the VBA to:
strSQL = " SELECT " & _
" Products " & _
" FROM Logistics " & _
" WHERE DATE(insert_timestamp) = " & Sheet1.Range("B1") & " " & _
" GROUP BY 1"
However, now I get runtime error -2147217887 (80040e21) on rs.Open strSQL, conn, adOpenStatic.
What do I need to change in my VBA to make it work?
Consider the industry best practice of SQL parameterization using the ADO Command object which can properly handle data types without string concatenation or various formatting:
Dim conn As ADODB.Connection ' REMOVE New INITIALIZATION IN Dim LINES
Dim rs As ADODB.Recordset
Dim cmd As ADODB.Command
Dim dateVar As Date
Set conn = New ADODB.Connection
conn.ConnectionString = "DRIVER={MySQL ODBC 5.1 Driver}; ..."
conn.Open
' STRING STATEMENT WITH QMARK PLACEHOLDER
strSQL = " SELECT " & _
" Products " & _
" FROM Logistics " & _
" WHERE DATE(insert_timestamp) = ?" & _
" GROUP BY Products"
' CONVERT CELL TO DATE TYPE (IF CELL FORMATTED AS STRING)
dateVar = CDate(Sheet1.Range("B1").Value)
Set cmd = New ADODB.Command
With cmd
.ActiveConnection = conn
.CommandText = strSQL
.CommandType = adCmdText
' BIND PARAMS TO QMARK POSITIONAL PLACEHOLDER
.Parameters.Append .CreateParameter("mydate", adDate, adParamInput, , dateVar)
' PASS RESULT TO RECORDSET
Set rs = .Execute
End With
Sheet5.Range("A1").CopyFromRecordset rs
Try:
strSQL = " SELECT " & _
" Products " & _
" FROM Logistics " & _
" WHERE DATE(insert_timestamp) = '" & Format(Sheet1.Range("B1").Value, "YYYY-MM-DD") & "' " & _
" GROUP BY 1"
Just be aware, if insert_timestamp does indeed contain a time other than midnight, you might not get the results you're after without converting to just a date or altering your SQL.

Query Returns Multiple Rows Instead of the max

I have a table in Access with the relevant fields EFTRecID, EFTRecIDNum and CreatedBy. EFTRecIDNum is an auto number field, and EFTRecID concatenates the required format of "CE" & EFTRedIDNum. I am attempting to return the highest value in the EFTRecID field that was created by the current user. To do this I am trying to do a sub query that finds the Max(EFTRecIDNum) WHERE Created = my name. However instead of returning the max, it is returning all values with my name. I know I could use the format option to just format the EFTRecIDNum field, but I need to be able to search in the format CE456.
TL;DR: Query returns all records with my name, rather then max with my name.
Public Sub DownloadMyRecords()
Dim intI As Integer 'Used for looping in a variety of locations
strSQL = "SELECT EFTRecID FROM tblEFTRec WHERE (SELECT MAX(EFTRecIDNum) FROM tblEFTRec WHERE CreatedBy = '" & Application.UserName & "')"
Set cnn = New ADODB.Connection
With cnn
.Provider = "Microsoft.ACE.OLEDB.12.0;Data Source=" & dbLocation & "\" & dbName & ";Jet OLEDB:Database Password=" & DBPWord
.Open dbLocation & "\" & dbName
End With
Debug.Print strSQL
Set rst = New ADODB.Recordset
rst.CursorLocation = adUseServer
rst.Open Source:=strSQL, ActiveConnection:=cnn, _
CursorType:=adOpenForwardOnly, LockType:=adLockOptimistic, _
Options:=adCmdText
Debug.Print (rst.GetString)
rst.Close
cnn.Close
Set rst = Nothing
Set cnn = Nothing
End Sub
Try this SQL statement instead:
strSQL = "SELECT EFTRecID
FROM tblEFTRec
WHERE EFTRecIDNum = (SELECT MAX(EFTRecIDNum) FROM tblEFTRec WHERE CreatedBy = '" & Application.UserName & "')"
Try using:
"SELECT EFTRecID FROM tblEFTRec
WHERE EFTRecIDNum = (SELECT MAX(EFTRecIDNum) FROM tblEFTRec
WHERE CreatedBy = '" & Application.UserName & "')"
Is there a reason the following doesn't work?
"SELECT MAX(EFTRecID) FROM tblEFTRec
WHERE CreatedBy = '" & Application.UserName & "'"

How to locate data source for unbound control?

I've inherited some Access VBA code and there are controls on a form (such as a listbox named lstOrderID, mentioned below) which have no RowSource property set (an empty string). I look in the code and find statements like this in various places:
Forms!frm_Customer.lstOrderID = rstCust!OrderID ' set from a record set
Forms!frm_Customer.lstOrderID.Requery
Me.lstOrderID = Me.lstOrderID.ItemData(0) ' set to first item in self
But nowhere in the code is lstOrderID.RowSource being set.
How can Requery be called on a listbox that has no RowSource?
How can a listbox be set to a single value (rstCust!OrderID) from a record set, unless this is a list of values (although the debugger shows an integer in lstOrderID.Value)?
Here is more code:
Dim rstCust As Recordset
Set db = CurrentDb
Set rstCust = db.OpenRecordset("SELECT * FROM Orders WHERE CustID=" & ID & _
"AND Datetaken =Date() " & _
"AND VendorID='" & Forms!frm_Customer.cboVendorID & "'")
Forms!frm_Customer.lstOrderID = rstCust!OrderID
rstCust.Close
db.Close
Another section:
Dim rstCust As Recordset
Dim blStatus As Boolean
Dim strSql As String
Set db = CurrentDb
strSql = "SELECT Orders.OrderID " & _
"FROM Orders " & _
"WHERE (((Orders.DateTaken)=#" & Date & "#) " & _
"AND ((Orders.VendorID)='" & Forms!frm_Customer.cboVendorID & "') " & _
"AND ((Orders.CustID)=" & ID & "));"
Set rstCust = db.OpenRecordset(strSql)
Forms!frm_Customer.lstOrderID = rstCust!OrderID
Forms!frm_Customer.lstOrderID.Requery
Forms!frm_Customer.lstOrderID = rstCust!OrderID
rstCust.Close
db.Close
Also this:
Me.lstOrderID.Requery
Me.lstOrderID = Me.lstOrderID.ItemData(0)

Dlookup retrieves a date wrong

I'm trying to get a date in my database but when the day is less than 12, the month and the day are switched
Example: In the database 2012-02-10 (2 october 2012), the value I get when I do that:
lastDateMill = Nz(DLookup("LastContactDate", "Mills", "MillID = " & lstMills.Column(0, i)), 0)
is
lastDateMill = "10/02/2012"
So I thought that was only a format thing but when I do
Format(lastDateMill, "Long Date")
it equals "February-10-12"
This is how I update the date
DoCmd.RunSQL "UPDATE Mills SET LastContactDate = #" & SalesCallDate & "# WHERE MillID = " & lstMills.Column(0, i)
And the SalesCallDate = "2/10/2012" so the good date
So why the day and the month are switched?
The front end is ms-access-2010 and the back end is on SQL SERVER 2012
Your SalesCallDate variable contains a date as text:
SalesCallDate = "2/10/2012"
Apparently you intend the date format of that string to be m/d/yyyy, but it gets interpreted as d/m/yyyy format.
Store the string value in yyyy/mm/dd format to eliminate confusion due to locale issues ... 2012/02/10
Since it turns out that SalesCallDate is actually a text box containing a date value, change your UPDATE approach to avoid date problems due to locale.
Dim strUpdate As String
Dim db As DAO.Database
strUpdate = "UPDATE Mills SET LastContactDate = " & _
Format(Me.SalesCallDate, "\#yyyy-m-d\#") & vbCrLf & _
"WHERE MillID = " & Me.lstMills.Column(0, i)
Debug.Print strUpdate
Set db = CurrentDb
db.Execute strUpdate, dbFailonerror
Set db = Nothing
Try this, by explicitly specifying a format
DoCmd.RunSQL "UPDATE Mills SET LastContactDate = #" & _
Format$(SalesCallDate,"yyyy/mm/dd") & "# WHERE MillID = " & _
lstMills.Column(0, i)
UPDATE:
Maybe there is better way to do it, that is independent of any formattings. The idea is to tranfer the date from table to table, without any combo-, list- or text in between. Therefore any conversion from a date type to string and then back to a date field is avoided.
If the tables can be joined (assuming that MillID is the bound field of the listbox):
DoCmd.RunSQL "UPDATE Mills " & _
"INNER JOIN sourceTable ON Mills.MillID = sourceTable.MillID " & _
"SET LastContactDate = sourceTable.SalesCallDate " & _
"WHERE Mills.MillID = " & lstMills
Otherwise
DoCmd.RunSQL "UPDATE Mills SET LastContactDate = " & _
"(SELECT SalesCallDate FROM sourceTable WHERE ID = " & sourceID & ")" _
"WHERE Mills.MillID = " & lstMills

Invalid Argument Error: MSAccess and SQL

I am trying to access certain lines from my SQL database from MSAccess and I keep getting an Invalid Argument Error on this line:
Set rs = CurrentDb.OpenRecordset("SELECT TimeID " & _
"FROM tblLunchTime " & _
"WHERE ProductionID = prodSelect AND EndTime is NULL AND StartTime < dateAdd('h', 3, NOW())", [dbSeeChanges])
Is something not right in this?
Private Sub cmdClockEnd_Click()
'Check if a group has been selected.
If frmChoice.value = 0 Then
MsgBox "Please select a production line."
End
End If
'Setup form for user input.
lblEnd.Visible = True
'Save end of lunch value.
lblEnd.Caption = Format(Now, "MMM/DD/YY hh:mm:ss AMPM")
'Declare database variables.
Dim dbName As DAO.Database
Dim strValuesQuery As String
Dim rs As DAO.Recordset
Dim prodSelect As String
Dim sSQL As String
Dim timeValue As String
Set dbName = CurrentDb
'Get values of Production Line.
If frmChoice.value = 1 Then
prodSelect = "L2"
ElseIf frmChoice.value = 2 Then
prodSelect = "L3"
End If
'Get the last TimeID with the following parameters.
sSQL = "SELECT TimeID " & _
"FROM tblLunchTime " & _
"WHERE ProductionID = prodSelect AND EndTime is NULL AND StartTime < #" & DateAdd("h", 3, Now()) & "#"
Set rs = dbName.OpenRecordset(sSQL, dbSeeChanges)
strValuesQuery = _
"UPDATE tblLunchTime " & _
"SET EndTime = '" & Now & "'" & _
"WHERE TimeID = " & rs![TimeID] & " "
'Turn warning messages off.
DoCmd.SetWarnings False
'Execute Query.
DoCmd.RunSQL strValuesQuery
'Turn warning messages back on.
DoCmd.SetWarnings True
End Sub
You need to put prodSelect outside the quotes:
"WHERE ProductionID = " & prodSelect & " AND ...
It is nearly always best to say:
sSQL="SELECT TimeID " & _
"FROM tblLunchTime " & _
"WHERE ProductionID = " & prodSelect & _
" AND EndTime is NULL AND StartTime < dateAdd('h', 3, NOW())"
''Debug.print sSQL
Set rs = CurrentDb.OpenRecordset(sSQL)
You can see the advantage in the use of Debug.Print.
AHA prodSelect is text! You need quotes!
sSQL="SELECT TimeID " & _
"FROM tblLunchTime " & _
"WHERE ProductionID = '" & prodSelect & _
"' AND EndTime is NULL AND StartTime < dateAdd('h', 3, NOW())"
There appears to be confusion about tblLunchTime ... whether it is a native Jet/ACE table or a link to a table in another database. Please show us the output from this command:
Debug.Print CurrentDb.TableDefs("tblLunchTime").Connect
You can paste that line into the Immediate Window and press the enter key to display the response. (You can open the Immediate Window with CTRL+g keystroke combination.)
Just in case the response starts with "ODBC", suggest you try this line in your code:
Set rs = CurrentDb.OpenRecordset(sSQL, dbOpenDynaset, dbSeeChanges)
Update: Now that you're past that hurdle, suggest you change your approach with the UPDATE statement. Don't turn off warnings; try something like this instead:
'Execute Query. '
CurrentDb.Execute strValuesQuery, dbFailOnError
And add an error handler to deal with any errors captured by dbFailOnError.
I think I would do the date criterion concatenation client-side, too, since it's one more thing that could go wrong:
"...StartTime < #" & DateAdd("h", 3, Now()) & "#"
I don't know that SQL Server doesn't have DateAdd() and Now() function nor that they don't behave exactly the same as in Access, but I wouldn't take the chance -- I'd do this calculation on the client instead of handing it off to the server.