VBA Query returning nulls - vba

Using the query builder in Access I am able to find the total, but I need to find the total using the vba code builder. The code given here gives me a null value.
Dim rst As DAO.Recordset
Dim dbs As Database
Dim strSQL As String
Set dbs = CurrentDb
strSQL = "SELECT Sum(GiftRcvd.Rcvdamount) AS SumOfRcvdamount FROM OurEvents INNER JOIN GiftRcvd ON OurEvents.EventName = GiftRcvd.EventName " & _
"WHERE ((([OurEvents].[EventDate])>" & Me.DateFrom.Value & " And ([OurEvents]![EventDate])< " & Me.DateTo.Value & "));"
Set rst = dbs.OpenRecordset(strSQL)
SumOfRcvdamount = rst![SumOfRcvdamount]
MsgBox SumOfRcvdamount

It's likely that your query is returning an empty recordset. Assuming you have data, this most likely means that your HAVING clause is filtering out the records you want.
As I remember, date literals in Access have to be in the format #1/30/2019#: a clause in the form [EventDate] > 1/30/2019 will not evaluate the way you want.
So try bracketing those date parameters with #:
[OurEvents].[EventDate])> "#" & Me.DateFrom.Value & "#"
Strictly speaking, you should avoid assembling queries from strings (due to the possibility of SQL Injection attacks): you should instead parameterize them and pass parameter values. BUT, that's harder to do in Access than in other forms of SQL.

You have to format your date values to valid string expressions:
"WHERE ((([OurEvents].[EventDate])> #" & Format(Me.DateFrom.Value, "yyyy\/mm\/dd") & "# And ([OurEvents]![EventDate])< #" & Format(Me.DateTo.Value, "yyyy\/mm\/dd") & "#));"

Related

VBA&SQL - delete date range from database

I am trying to create an option on my Access database that deletes all records between 2 dates.
I code I have below does not seem to work consistently. For example, if my database has dates 01/01/18 to 01/30/18, and the range I specify is exactly 01/01/18 to 01/30/18, then it works and deletes all data.
But if I specify any other date range (like 01/01 - 01/15), it will fail and no records will be deleted.
The [Trade Date] is in short text format instead of date, but all the entries are in MM/DD/YY. Would prefer to keep it this way unless that is the issue and no other alternatives.
Please let me know what I am doing wrong or could do better. Thank you in advance.
Dim trFrmDat As String
Dim trToDat As String
Dim dbsDelete As DAO.Database
Dim qdfToDelete As DAO.QueryDef
Dim countString As String
Dim count As Long
Set dbsDelete = CurrentDb
trFrmDat = InputBox("Trade Date To Be Deleted From [MM/DD/YY]:")
trToDat = InputBox("Trade Date To Be Deleted To [MM/DD/YY]:")
If Len(trFrmDat) <> 8 Or Len(trToDat) <> 8 Then
MsgBox ("The correct date or answer has not been entered. Process Aborted.")
Exit Sub
Else
countString = "SELECT COUNT(PK_ID) FROM AR_Consolidated WHERE [Trade Date] BETWEEN " & trFrmDat & " AND " & trToDat & ""
count = dbsDelete.OpenRecordset(countString).Fields(0).Value
Set qdfToDelete = dbsDelete.CreateQueryDef("", "DELETE FROM AR_Consolidated WHERE [Trade Date] BETWEEN " & trFrmDat & " AND " & trToDat & "")
qdfToDelete.Execute dbFailOnError
MsgBox ("" & count & " records have been deleted from AR_Consolidated")
End If
EDIT:
I ended up using one of the suggestions below, but still had formatting issues with the date, so I conceded I cannot keep the actual field as short text. I just injected an alter line and everything works perfectly.
DoCmd.RunSQL "ALTER TABLE AR_Consolidated ALTER COLUMN [Trade Date] Datetime"
In order to represent a date value in Jet SQL, you need to surround it with #, such that your SQL ends up something like this:
DELETE FROM AR_Consolidated WHERE [Trade Date] BETWEEN #01/01/18# AND #01/30/18#
Note that in general, combining strings with user inputs to make SQL statements is a bad idea. Aside from issues similar to yours -- properly representing date or other literals in SQL statements -- you are also liable to SQL injection, where a malicious user inserts additional SQL statements or clauses that cause your code to fail or do something unwanted. For example, the user could pass in as the second parameter the following string:
01/01/18 OR 1 = 1
and the resulting SQL statement:
DELETE FROM AR_Consolidated WHERE [Trade Date] BETWEEN 01/01/18 AND 01/01/18 OR 1 = 1
would delete all the records in AR_Consolidated.
The correct way to avoid both classes of issues is to use parameterized queries.
Dim sql As String
sql = _
"PARAMETERS FromDate DATETIME, TillDate DATETIME; " & _
"DELETE FROM AR_Consolidated " & _
"WHERE [Trade Date] BETWEEN FromDate AND TillDate"
Dim qdfToDelete As DAO.QueryDef
Set qdfToDelete = dbsToDelete.CreateQueryDef("", sql)
qdfToDelete.Parameters("FromDate") = trFromDate
qdfToDelete.Parameters("TillDate") = trToDate
'You may have to convert the string values to dates first
qdfToDelete.Execute dbFailOnError
For a comprehensive description of how to avoid SQL injection when programming against Jet / ACE, see the Microsoft Access and COM / Automation pages on bobby-tables.com.
I would take a different approach and use parameters instead of string concatenation.
The query's SQL would be something like this:
PARAMETERS [prmDateFrom] DateTime, [prmDateTo] DateTime;
DELETE *
FROM AR_Consolidated
WHERE [Trade Date] Between [prmDateFrom] And [prmDateTo];
Then to call my query in VBA:
Sub Something()
Dim trFrmDat As String
Dim trToDat As String
trFrmDat = InputBox("Trade Date To Be Deleted From [MM/DD/YY]:")
trToDat = InputBox("Trade Date To Be Deleted To [MM/DD/YY]:")
If Len(trFrmDat) <> 8 Or Len(trToDat) <> 8 Then
MsgBox ("The correct date or answer has not been entered. Process Aborted.")
Exit Sub
End If
Dim qdf As DAO.QueryDef
Dim count As Long
Set qdf = CurrentDb().QueryDefs("QueryName")
With qdf
.Parameters("[prmDateFrom]").Value = CDate(trFrmDat)
.Parameters("[prmDateTo]").Value = CDate(trToDat)
.Execute dbFailOnError
count = .RecordsAffected
End With
MsgBox " " & count & " records have been deleted from AR_Consolidated"
End Sub
However, you need to enhance your input validation as a normal string with 8 characters which cannot be converted to a date would cause a runtime error.
You can validate the input and then format the values:
trFrmDat = InputBox("Trade Date To Be Deleted From [MM/DD/YY]:")
trToDat = InputBox("Trade Date To Be Deleted To [MM/DD/YY]:")
If IsDate(trFrmDat) And IsDate(trToDat) Then
trFrmDat = Format(CDate(trFrmDat), "yyyy\/mm\/dd")
trToDat = Format(CDate(trToDat), "yyyy\/mm\/dd")
countString = "SELECT COUNT(PK_ID) FROM AR_Consolidated WHERE [Trade Date] BETWEEN #" & trFrmDat & "# AND #" & trToDat & "#"
End If
and similar for the delete SQL.

MS Access DLookUp malfunction

I have a very weird problem occurring in MS Access which I can't seem to figure out.
Summary: I have a table from Sharepoint that is connected to my MS Access database and a Person table in my Ms Access db. I pull the information row by row from the Sharepoint table and add it to Person Table.
However, before adding the new data I must check if that specific Person already exists in my table. I check for 'Lastname', 'Firstname' and 'Date created' using DLookup function.
Here where everything goes side ways. DLookup returns me a NULL for almost half of the records that already exist in Person Table. After playing a lot with the condition in DLookup statement my conclusion is that there is a problem with the 'Date created' parameter, yet I have tried using "#" and CDate and even Format, nothing works.
I can't share the data, since it's sensitive, however the syntax for DLookup I'm using is the following:
sqlStr = "LastName=" & Chr(34) & rs![Last Name] & Chr(34)
& " AND FirstName=" & Chr(34) & rs![First Name] & Chr(34)
& " AND DateLastModified=" & Format(dateVar, "dd/mm/yyyy")
DLookup("LastName", "table_Person", sqlStr)
P.S: I have tried DCount, same thing happens. DCount returns 0 yet I know for a fact the record is there.
To build criterias BuildCriteria is your Friend.
Sub TestBuildCriteria()
Dim strCriteria As String
strCriteria = BuildCriteria("OrderDate", dbDate, [Date created])
MsgBox strCriteria
End Sub
Sub YourCode()
sqlStr = BuildCriteria("LastName", dbText, "=" & rs![Last Name]) & _
" AND " & BuildCriteria("FirstName", dbText, "=" & rs![First Name]) & _
" AND " & BuildCriteria("DateLastModified", dbDate, "=" & dateVar)
End Sub
This echoes the proper formated date. Also useful for other data-type. E.g. it escapes Quotation Marks in Strings. Read Custom Filters using BuildCriteria() too.
But there is a far easier alternative.
Create a unique composite index on LastName, FirstName and DateLastModified in the the table. Now you can't insert a duplicate as it has to be unique. If you try you will receive an error msg. Be aware of transaction rollbacks (e.g. Multiple inserts, one fails by key violation -> all actions will be reverted due transaction rollback if you use db.Execute SQL, dbFailOnError).
To check for dates use:
"DateLastModified=#" & FormatDateTime(dateVar, vbShortDate) & "#"
if dateVar can be null you need something like this:
FormatDateTime(Nz(dateVar,CDate("1/1/2000")), vbShortDate)
And of course that just checks the date part. If your dateVar can also have a time part then you have to use
DateValue(dateVar)
Your syntax is not correct. You should put square brackets around field names as is pointed out in documented examples at MSDN
sqlStr = "[LastName]=" & Chr(34) & rs![Last Name] & Chr(34)
& " AND [FirstName]=" & Chr(34) & rs![First Name] & Chr(34)
& " AND [DateLastModified]=#" & Format(dateVar, "dd/mm/yyyy") & "#"
DLookup("[LastName]", "table_Person", sqlStr)
In this situation, my advice would be to simplify the criteria part of the DLOOKUP/DCOUNT until you get something that works, and only then start to make the criteria more complex. I call this 'sanity checking'.
Date/Time criteria often cause problems, so first check that you can make it work without the Date part of the criteria.
For example, in your case, check that this works.
Use the Debug Window (Ctril+G) to test this:
? DCount("*", "table_Person", "LastName=" & Chr(34) & rs![Last Name] & Chr(34))
Then try:
? DCount("*", "table_Person", "LastName=" & Chr(34) & rs![Last Name] & Chr(34) & " AND FirstName=" & Chr(34) & rs![First Name] & Chr(34))
Once you have that working, add in the Date criteria.
Building the criteria up in stages like this, allows you to confirm which part is actually causing the problem.
I'm in the UK, and so I have my dates displayed in UK format - 'DD/MM/YYYY'.
However, when specifying a date criteria for a DLOOKUP/DCOUNT, I always have to format the date to US format. I've often used a simple function to swap the digits into the correct order for the criteria:
Function HashDate(dD As Date) As String
HashDate = "#" & Format$(dD, "MM/DD/YYYY") & "#"
End Function
In the the Debug Window:
? Date
09/03/2018
? HashDate(Date)
#03/09/2018#

Updating a field dependent on a date range in Access with VisualBasic and SQL

A friend and I have been trying for hours with little progress to a get a piece of code right for an invoicing system we're designing as a project.
We are trying to update the field InvoiceNo to a value (worked out earlier in the VisualBasic code), where the CustomerNo is the is a specific value and the FinishDate is between two dates. At first I was trying to use TO_DATE but then we realized that wasn't the same in the SQL that Access uses (after much searching).
This has been the simple statement I've been using to just test and try to get something working to then translate into VisualBasic and put in our variables. It's a little easier to read so I thought I'd provide it.
UPDATE tblJob SET tblJob.InvoiceNo = '8' WHERE tblJob.CustomerNo = '1' AND (tblJob.FinishDate BETWEEN cdate(format('08/09/2013', '##/##/####')) AND cdate(format('03/10/2013', '##/##/####')));
I have a feeling after looking at a few examples that our date is meant to be without an forward slashes. So I tried that and it wasn't working either.
Here's the VisualBasic code that has come out of all of this, it's exactly the same but using some variables rather than our set values that I've been using for testing.
DoCmd.RunSQL ("UPDATE tblJob SET tblJob.InvoiceNo = '" & newInvoiceNo & "' WHERE tblJob.CustomerNo = '" & VbCustNo & "' AND (tblJob.FinishDate BETWEEN cdate(format('" & Forms![frmMainMenu][txtFirstDate] & "', '##/##/####')) AND cdate(format('" & Forms![frmMainmenu][txtEndDate] & "', '##/##/####')));")
We had a look at: Convert a string to a date in Access and it helped us realize that it was cdate(format()) rather than TO_DATE as it is in Oracle. But we just can't seem to get it to run properly, any help would be much appreciated.
If you will be running the query from within an Access application session, you can let the db engine use the Access expression service to grab the values from the text boxes on your form.
Dim db As DAO.Database
Dim strUpdate As String
strUpdate = "UPDATE tblJob" & vbCrLf & _
"SET InvoiceNo = '" & newInvoiceNo & "'" & vbCrLf & _
"WHERE CustomerNo = '" & VbCustNo & "'" & vbCrLf & _
"AND FinishDate BETWEEN Forms!frmMainMenu!txtFirstDate AND Forms!frmMainmenu!txtEndDate;"
Debug.Print strUpdate
Set db = CurrentDb
db.Execute strUpdate, dbFailOnError
Set db = Nothing
However, if you prefer to build the literal date values from those text boxes into your UPDATE statement, you can use Format().
"AND FinishDate BETWEEN " & _
Format(Forms!frmMainmenu!txtFirstDate, "\#yyyy-m-d\#") & _
" AND " & Format(Forms!frmMainmenu!txtEndDate, "\#yyyy-m-d\#") & ";"
Either way, using a string variable to hold your UPDATE statement gives you an opportunity to examine the completed statement you're asking the db engine to execute.
You can view the output from Debug.Print in the Immediate window (go there with Ctl+g). For troubleshooting, you can copy the statement text from there and then paste it into SQL View of a new Access query.

Use a VB variable in a SQL statement

I'm trying to create a sql statement but I require the use of a VB variable. The problem is, I keep getting an error about too few parameters when I try to just put the variable in. Is there some sort of format I need to use in order to add a VB variable into a SQL statement?
Set rs = CurrentDb.OpenRecordset("SELECT StartTime " & _
"FROM tblLunchTime " & _
"WHERE TimeID = (SELECT max(TimeID-count) FROM tblLunchTime);")
The variable in this situation is 'count'.
concatenate the variable like so:
Set rs = CurrentDb.OpenRecordset("SELECT StartTime " & _
"FROM tblLunchTime " & _
"WHERE TimeID = (SELECT max(TimeID-" & count & ") FROM tblLunchTime);")
Well... using non-parameterized sql like you want to is usually a very bad idea. There are many articles on how to parameterize a sql query or use stored procs for VB (6 and .NET).
You need to concatenate it:
Set rs = CurrentDb.OpenRecordset("SELECT StartTime " & _
"FROM tblLunchTime " & _
"WHERE TimeID = (SELECT max(TimeID-" & count & ") FROM tblLunchTime);")

VBA select not returning any rows

Below query is not returning any rows into the listbox.There is no error message:
lstDiff.RowSource = "select TestScenario,TestId from tblTesting where empid= '" & Me.txtEmpId.Value & "' and testid= '" & Me.txtAutoNumber.Value & "'"
Could anyone help?
Your values in the field are numeric, so the extra single quotes aren't needed. Code should look like the following:
Me.lstDiff.RowSource = "select TestScenario,TestId from tblTesting where empid= " & Me.txtEmpId & " and testid= " & Me.txtAutoNumber & ";"
I've also dropped .Value from the field references, they're not harmful, but also aren't necessary.
And I've added a semi-colon to the end of your statement.
Depending on when/where you insert this code, you may need to add the following statement as well:
Me.lstDiff.Requery
You keep posting questions about the exact same WHERE clause with exactly the same apparent error in each one. SO users dutifully point out your error and then a few days later, you show up with a related question utilizing the same faulty WHERE clause.
DLookup Problem:
txtContSunStart1.Value = DLookup("Start1", "tblContract", _
"TestId = " & _
lstResults.Value & _
"" And "Day = 'Sunday'")
VBA Update Query:
DoCmd.RunSQL (" Update tbltesting set IsDiff ='Yes' where empid= " & Me.txtEmpId.Value & " and testid= " & Me.txtAutoNumber.Value & ";")
VBA SQL Problem
DoCmd.RunSQL ("insert into tbltesting (IsDiff)values ('Yes') where empid= '" & Me.txtEmpId.Value & "' and testid= '" & Me.txtAutoNumber.Value & "'")
And then in the current question:
lstDiff.RowSource = "select TestScenario,TestId from tblTesting where empid= '" & Me.txtEmpId.Value & "' and testid= '" & Me.txtAutoNumber.Value & "'"
You are having difficulties with exactly the same set of problems repeatedly.
Here are the rules for concatenating SQL strings with the correct delimiters in Access:
numeric values do not need delimiters:
"... AND testid= " & Me!txtAutoNumber
text values need quote delimiters. In Access, it's general practice to use double quotes, but much easier to use single quotes since it's a pain to type double quotes in a form that will work (typing """ or """" depending on context is counterintuitive and silly to me, so I always define a global constant that holds the double quote symbol and concatenate with that).
"... AND textfield=" & Chr(34) & Me!MyTextField & Chr(34)
date values use the # delimiter:
"... AND datefield=#" & Me!MyDateField & "#"
Boolean fields require no quotes and it works best to use True and False:
"... AND IsDiff=True"
These rules apply both to WHERE clause criteria and to SET statements in UPDATE queries. The rules apply in writing a SQL string that you pass to DoCmd.RunSQL or CurrentDB.Execute, as well as to writing SQL strings to be used as the recordsource of a form or report or as the rowsource of a combo box or listbox.
Personally, whenever I use SQL statements in code, I prefer to store the statement in a variable. While testing, on the line after you assign your statement to a variable, you can use Debug.Print to see what your SQL statement looks like after parsing your txtempid and txtautonumber. It would look something like this.
Dim sSQL as String
sSQL = "select TestScenario,TestId from tblTesting where empid= '" & Me.txtEmpId.Value & "' and testid= '" & Me.txtAutoNumber.Value & "'"
Debug.Print sSQL
lstDiff.RowSource = sSQL
Then as long as your immediate window is visible (Ctrl-G), you can see what your SQL statement really is. If it looks right in the immediate window, copy and paste it into the query builder and run it there.
Try running the query in your SQL Management Studio. Do you get any row(s) back?
Edit: Just noticed the access-tag. Are you sure your table contains at least one post with supplied ids?
My Access is a bit rusty, but if all else fails try using a recordset to capture the data from the SQL and loop through it adding the values to the list box. Example Code