Search Criteria Form - sql

So I've managed to get my criteria form up and going in access, and it searchs Name/City , but it only searches for the EXACT city name, even though ive given it a wildcard, and the name field works as a search any part of the field? Anyone know why? here is my code:
'Text field example. Use quotes around the value in the string.
If Not IsNull(Me.txtFilterCity) Then
strWhere = strWhere & "([City] Like ""*" & Me.txtFilterCity & "*"") AND "
End If
'Another text field example. Use Like to find anywhere in the field.
If Not IsNull(Me.txtFilterMainName) Then
strWhere = strWhere & "([MainName] Like ""*" & Me.txtFilterMainName & "*"") AND "
End If
Thanks!

Here is a different approach to building strWhere.
' Text field example. Use quotes around the value in the string. '
If Not IsNull(Me.txtFilterCity) Then
strWhere = strWhere & " AND City Like ""*" & Me.txtFilterCity & "*"""
End If
' Another text field example. Use Like to find anywhere in the field. '
If Not IsNull(Me.txtFilterMainName) Then
strWhere = strWhere & " AND MainName Like ""*" & Me.txtFilterMainName & "*"""
End If
' chop off leading AND '
strWhere = Mid(strWhere, 6)
Debug.Print strWhere
I discarded the parentheses and square brackets because they weren't needed here. Square brackets surrounding a field name are needed if the field name includes spaces, punctuation characters, etc. Square brackets are also useful if the field name is a reserved word. I prefer to use only characters which don't require bracketing in object names, and avoid reserved words as object names.

if you are using access then
* allows you to match any string of any length (including zero length)
? allows you to match on a single character
# allows you to match on a single numeric digit
eg:
Like 'b*' would return all values that start with b
Like '*b*' would return all values that contain b
Like '*b' would return all values that end with b
Like 'b?' would return all values that start with b and are 2 characters in length
Like 'b#' would return all values that start with b and are 2 characters in length where the second character is a number

Related

Filter by multiple parameters on Access form

I have a MS Access form that I want to filter the database based on a SQL statement.
The form will use multiple parameters, but I want it so that not all fields are required to perform the filter.
An example would be: User wants to query only by Date and Product and leave Customer and Analysis blank.
These are the fields in the form:
So far I have tried the following statements and using "LIKE" but it is returning blank results. I have only tried with two fields and it isn't working.
Public Sub Command121_Click()
Dim task As String
task = "select * from SageOrderLines_Live where [PromisedDeliveryDate] = " & Format(Me.DateFrom, "\#dd\/mm\/yyyy\#") & " AND [CustomerAccountNumber] LIKE "" & Me.CustomerAccount & """
DoCmd.ApplyFilter task
End Sub
Using LIKE without wildcard might as well be = sign.
Use of quote delimiters is incorrect - really need another quote on each side.
" AND [CustomerAccountNumber] LIKE """ & Me.CustomerAccount & "*"""
Or make it easier to read and use apostrophe instead of doubled quotes.
" AND [CustomerAccountNumber] LIKE '" & Me.CustomerAccount & "*'"

Use a string (multivalued field from table) as where clause in SQL query

I try to redefine a SQL query with VBA to include the content of a multivalued table field as a where clause but I can't get it to work.
First I gather all the values from the specific multivalued field (formatted as text, can't use numbers due limitations during the import)
BLPN_Query = DLookup("Kostenstellen_Report", "Optionen_Reportgenerierung", "ID=1")
MsgBox will return this (mind the space between ";" and the next text):
34; 44
This string can contain several different text entries. I would like to use the string "BLPN_Query" as a where clause condition. What I've got so far is this:
"WHERE (PROKALK.NK_STK>0) AND (PROKALK.TERMIN>{d '" & Startjahr & "-01-01'}) AND (PROKALK.BLPNR='" & BLPN_Query & "')"
If there is only one entry it works, but in case there are more than one it won't work (obviously). Not sure but I guess the space between the semicolon and the next text is an issue as well as I have to use "IN" instead of "=" but I don't know how to do this.
Solution (thanks to Andre!)
1.) Get data from table (looks like: 34; 35; 36), remove the spaces and replace the semicolon by a comma including single quotes between the elements for the IN clause. Now it looks like: 34','35','36
BLPN_Query = DLookup("Kostenstellen_Report", "Optionen_Reportgenerierung", "ID=1")
BLPN_Query = Replace(BLPN_Query, " ", "")
BLPN_Query = Replace(BLPN_Query, ";", "','")
2.) Include the string within the where clause (and add a single quote before and after the string) --> Final string: '34','35','36'
AND (PROKALK.BLPNR IN ('" & BLPN_Query & "'))"
it's not pretty but will help us until we finally get the new ERP system and can replace all the old stuff
IN (...) expects comma, so you need to do:
BLPN_Query = Replace(BLPN_Query, ";", ",")
and then in the WHERE clause:
"WHERE ... AND (PROKALK.BLPNR IN (" & BLPN_Query & "))"
Extra spaces don't hurt. This also works if BLPN_Query contains a single value, but it doesn't if it is empty.

MS Query with various multiple value parameters where there can be an empty parameter

I have 5 multiple select listboxes in Excel. The selected content of every listbox is each written in one cell, separated with a comma.
For example in cell A1 all the selected names: Tim, Miranda, Laura
Those cells are the criteria for an Access query (where clause).
I open MS Query and add the following where clause to the query and define the parameters:
Where (Instr(?, [name])>0 And Instr(?, [number])>0 And Instr(?, [city])>0 And Instr(?, [phone])>0 And Instr(?, [email])>0)
It works quite well, however if one of the parameter fields is empty (for example the user didn't select any city) the query returns all lines, where city is empty instead of ignoring that clause in this case.
How do I solve this? Perhaps there is another solution using VBA and dynamic SQL.
Note: I have to use Excel for the listboxes instead of Access formulas because the tool shall also be used by persons who do not have access to the database.
I would use the LIKE operator.
In Excel populate other cells and point the parameters to them
=if(<cityCell> ="","_%",<cityCell>&"%")
and change the query to
Where [name] like ? And [number] like ? And[CITY] like and [phone] like ? And [email]like ?
In this way you can handle other transgressions by users i.e. change the Excel formula to
=if(<cityCell> ="","_%",PROPER(<cityCell>)&"%") OR
So user entry of toronto will look for Toronto. I use this a lot with UPPER as lots of databases contain uppercase entries.. i.e. Canadian postal codes
From my experience, it may be returning null in some cases and affecting your comparison in the clause.
what happens when you try the len function:
Where Instr(?, [name])>0 And Instr(?, [number])>0 And (Instr(?, [number])>0 and
NOT ISNULL([CITY])) And Instr(?, [phone])>0 And Instr(?, [email])>0)**
See the code change above. Sorry, saw your update, I just need to get a grip on the problem. Do you want all records including those that have "empty" cities? But I thought you wanted it to skip those? Another way I search for this is - NOT ISNULL([CITY]) . I work in an environment with a LOT of Access databases and they are quirky!
I solved the Problem by using dynamic SQL in VBA. If nothing is selected, I link all rows in the listbox to a string. This is an example for one listbox which contains names (the code does not contain the necessary connection to the database):
Dim string1 As String
Dim rngParams As Range, rngCell As Range
Dim i As Long
'String name
With Worksheets("Table1")
For i = 0 to .ListBox1.ListCount - 1
If .ListBox1.Selected(i) Then
If string1 = "" Then string1 = "("
string1 = string1 & "'" & .ListBox1.List(i) & "',"
End If
Next i
End With
If string1 <> "" Then
string1 = Left(string1, Len(string1) - 1)
string1 = string1 & ")"
End If
'If nothing is selected, use all names and write them into one string
If string1 = "" Then
string1 = "("
With Worksheets("table2")
Set rngParams = .Range("allNames")
For Each rngCell In rngParams.Cells
string1 = string1 & "'" & rngCell.Value & "',"
Next rngCell
string1 = Left(string1, Len(string1) - 1)
string1 = string1 & ")"
End With
End If
strSQL = "SELECT Name FROM accesstable WHERE Name IN " & string1

Escaping ' in Access SQL

I'm trying to do a domain lookup in vba with something like this:
DLookup("island", "villages", "village = '" & txtVillage & "'")
This works fine until txtVillage is something like Dillon's Bay, when the apostrophe is taken to be a single quote, and I get a run-time error.
I've written a trivial function that escapes single quotes - it replaces "'" with "''". This seems to be something that comes up fairly often, but I can't find any reference to a built-in function that does the same. Have I missed something?
The "Replace" function should do the trick. Based on your code above:
DLookup("island", "villages", "village = '" & Replace(txtVillage, "'", "''") & "'")
It's worse than you think. Think about what would happen if someone entered a value like this, and you haven't escaped anything:
'); DROP TABLE [YourTable]
Not pretty.
The reason there's no built in function to simply escape an apostrophe is because the correct way to handle this is to use query parameters. For an Ole/Access style query you'd set this as your query string:
DLookup("island", "village", "village = ? ")
And then set the parameter separately. I don't know how you go about setting the parameter value from vba, though.
Though the shorthand domain functions such as DLookup are tempting, they have their disadvantages. The equivalent Jet SQL is something like
SELECT FIRST(island)
FROM villages
WHERE village = ?;
If you have more than one matching candidate it will pick the 'first' one, the definition of 'first' is implementation (SQL engine) dependent and undefined for the Jet/ACE engine IIRC. Do you know which one would be first? If you don’t then steer clear of DLookup :)
[For interest, the answer for Jet/ACE will either be the minimum value based on the clusterd index at the time the database file was last compacted or the first (valid time) inserted value if the database has never been compacted. Clustered index is in turn determined by the PRIAMRY KEY if persent otherwise a UNIQUE constraint or index defined on NOT NULL columns, otherwise the first (valid time) inserted row. What if there is more than one UNIQUE constraint or index defined on NOT NULL columns, which one would be used for clustering? I've no idea! I trust you get the idea that 'first' is not easy to determine, even when you know how!]
I've also seen advice from Microsoft to avoid using domain aggregate functions from an optimization point of view:
Information about query performance in an Access database
http://support.microsoft.com/kb/209126
"Avoid using domain aggregate functions, such as the DLookup function... the Jet database engine cannot optimize queries that use domain aggregate functions"
If you choose to re-write using a query you can then take advantage of the PARAMETERS syntax, or you may prefer the Jet 4.0/ACE PROCEDURE syntax e.g. something like
CREATE PROCEDURE GetUniqueIslandName
(
:village_name VARCHAR(60)
)
AS
SELECT V1.island_name
FROM Villages AS V1
WHERE V1.village_name = :village_name
AND EXISTS
(
SELECT V2.village_name
FROM Villages AS V2
WHERE V2.village_name = V1.village_name
GROUP
BY V2.village_name
HAVING COUNT(*) = 1
);
This way you can use the engine's own functionality -- or at least that of its data providers -- to escape all characters (not merely double- and single quotes) as necessary.
But then, it should be like this (with one more doublequote each):
sSQL = "SELECT * FROM tblTranslation WHERE fldEnglish=""" & myString & """;"
Or what I prefer:
Make a function to escape single quotes, because "escaping" with "[]" would not allow these characters in your string...
Public Function fncSQLStr(varStr As Variant) As String
If IsNull(varStr) Then
fncSQLStr = ""
Else
fncSQLStr = Replace(Trim(varStr), "'", "''")
End If
End Function
I use this function for all my SQL-queries, like SELECT, INSERT and UPDATE (and in the WHERE clause as well...)
strSQL = "INSERT INTO tbl" &
" (fld1, fld2)" & _
" VALUES ('" & fncSQLStr(str1) & "', '" & fncSQLStr(Me.tfFld2.Value) & "');"
or
strSQL = "UPDATE tbl" & _
" SET fld1='" & fncSQLStr(str1) & "', fld2='" & fncSQLStr(Me.tfFld2.Value) & "'" & _
" WHERE fld3='" & fncSQLStr(str3) & "';"
I believe access can use Chr$(34) and happily have single quotes/apostrophes inside.
eg
DLookup("island", "villages", "village = " & chr$(34) & nonEscapedString & chr$(34))
Though then you'd have to escape the chr$(34) (")
You can use the Replace function.
Dim escapedString as String
escapedString = Replace(nonescapedString, "'", "''")
Parametrized queries such as Joel Coehoorn suggested are the way to go, instead of doing concatenation in query string. First - avoids certain security risks, second - I am reasonably certain it takes escaping into engine's own hands and you don't have to worry about that.
By the way, here's my EscapeQuotes function
Public Function EscapeQuotes(s As String) As String
If s = "" Then
EscapeQuotes = ""
ElseIf Left(s, 1) = "'" Then
EscapeQuotes = "''" & EscapeQuotes(Mid(s, 2))
Else
EscapeQuotes = Left(s, 1) & EscapeQuotes(Mid(s, 2))
End If
End Function
For who having trouble with single quotation and Replace function, this line may save your day ^o^
Replace(result, "'", "''", , , vbBinaryCompare)
put brackets around the criteria that might have an apostrophe in it.
SOmething like:
DLookup("island", "villages", "village = '[" & txtVillage & "]'")
They might need to be outside the single quotes or just around txtVillage like:
DLookup("island", "villages", "village = '" & [txtVillage] & "'")
But if you find the right combination, it will take care of the apostrophe.
Keith B
My solution is much simpler. Originally, I used this SQL expression to create an ADO recordset:
Dim sSQL as String
sSQL="SELECT * FROM tblTranslation WHERE fldEnglish='" & myString & "';"
When myString had an apostrophe in it, like Int'l Electrics, my program would halt. Using double quotes solved the problem.
sSQL="SELECT * FROM tblTranslation WHERE fldEnglish="" & myString & "";"

Returning records that partially match a value

I'm trying to get a query working that takes the values (sometimes just the first part of a string) from a form control. The problem I have is that it only returns records when the full string is typed in.
i.e. in the surname box, I should be able to type gr, and it brings up
green
grey
graham
but at present it's not bringing up anything uless the full search string is used.
There are 4 search controls on the form in question, and they are only used in the query if the box is filled in.
The query is :
SELECT TabCustomers.*,
TabCustomers.CustomerForname AS NameSearch,
TabCustomers.CustomerSurname AS SurnameSearch,
TabCustomers.CustomerDOB AS DOBSearch,
TabCustomers.CustomerID AS MemberSearch
FROM TabCustomers
WHERE IIf([Forms]![FrmSearchCustomer]![SearchMember] Is Null
,True
,[Forms]![FrmSearchCustomer]![SearchMember]=[customerid])=True
AND IIf([Forms]![FrmSearchCustomer].[SearchFore] Is Null
,True
,[Forms]![FrmSearchCustomer]![SearchFore] Like [customerforname] & "*")=True
AND IIf([Forms]![FrmSearchCustomer]![SearchLast] Is Null
,True
,[Forms]![FrmSearchCustomer]![SearchLast] Like [customersurname] & "*")=True
AND IIf([Forms]![FrmSearchCustomer]![Searchdate] Is Null
,True
,[Forms]![FrmSearchCustomer]![Searchdate] Like [customerDOB] & "*")=True;
There is an Access Method for that!
If you have your "filter" controls on the form, why don't you use the Application.buildCriteria method, that will allow you to add your filtering criterias to a string, then make a filter out of this string, and build your WHERE clause on the fly?
selectClause = "SELECT TabCustomers.* FROM TabCustomers"
if not isnull(Forms!FrmSearchCustomer!SearchMember) then
whereClause = whereClause & application.buildCriteria(your field name, your field type, your control value) & " AND "
endif
if not isnull(Forms!FrmSearchCustomer!SearchFore) then
whereClause = whereClause & application.buildCriteria(...) & " AND "
endif
if not isnull(Forms!FrmSearchCustomer!SearchLast) then
whereClause = whereClause & application.buildCriteria(...) & " AND "
endif
if not isnull(Forms!FrmSearchCustomer!SearchDate) then
whereClause = whereClause & application.buildCriteria(...) & " AND "
endif
--get rid of the last "AND"
if len(whereClause) > 0 then
whereClause = left(whereClause,len(whereClause)-5)
selectClause = selectClause & " WHERE " & whereClause
endif
-- your SELECT instruction is ready ...
EDIT: the buildCriteria will return (for example):
'field1 = "GR"' when you type "GR" in the control
'field1 LIKE "GR*"' when you type "GR*" in the control
'field1 LIKE "GR*" or field1 like "BR*"' if you type 'LIKE "GR*" OR LIKE "BR*"' in the control
PS: if your "filter" controls on your form always have the same syntax (let's say "search_fieldName", where "fieldName" corresponds to the field in the underlying recordset) and are always located in the same zone (let's say formHeader), it is then possible to write a function that will automatically generate a filter for the current form. This filter can then be set as the form filter, or used for something else:
For each ctl in myForm.section(acHeader).controls
if ctl.name like "search_"
fld = myForm.recordset.fields(mid(ctl.name,8))
if not isnull(ctl.value) then
whereClause = whereClause & buildCriteria(fld.name ,fld.type, ctl.value) & " AND "
endif
endif
next ctl
if len(whereClause)> 0 then ...
You have your LIKE expression backwards. I have rewritten the query to remove the unnecessary IIF commands and to fix your order of operands for the LIKE operator:
SELECT TabCustomers.*
FROM TabCustomers
WHERE (Forms!FrmSearchCustomer!SearchMember Is Null Or Forms!FrmSearchCustomer!SearchMember=[customerid])
And (Forms!FrmSearchCustomer.SearchFore Is Null Or [customerforname] Like Forms!FrmSearchCustomer!SearchFore & "*")
And (Forms!FrmSearchCustomer!SearchLast Is Null Or [customersurname] Like Forms!FrmSearchCustomer!SearchLast & "*")
And (Forms!FrmSearchCustomer!Searchdate Is Null Or [customerDOB] Like Forms!FrmSearchCustomer!Searchdate & "*");
I built that query by replicating the most likely circumstance: I created a dummy table with the fields mentioned and a form with the fields and a subform with the query listed above being refreshed when the search button was pushed. I can provide a download link to the example I created if you would like. The example works as expected. J only picks up both Jim and John, while John or Jo only pulls the John record.
Two things are going on - the comparisions should be reversed and you are not quoting strings properly.
It should be [database field] like "partial string + wild card"
and all strings need to be surrounded by quotes - not sure why your query doesn't throw errors
So the following should work:
,[customerforname] Like """" & [Forms]![FrmSearchCustomer]![SearchFore] & "*""" )=True
Note the """" that is the only way to append a single double-quote to a string.
My only thoguht is that maybe a () is needed to group the like
For example a snippet on the first part
,[Forms]![FrmSearchCustomer]![SearchFore] Like ([customerforname] & "*"))=True
It has been a while since I've used access, but it is the first thing that comes to mind
This is a complete re-write to allow for nulls in the name fields or the date of birth field. This query will not fail as too complex if text is entered in the numeric customerid field.
SELECT TabCustomers.CustomerForname AS NameSearch, TabCustomers.CustomerSurname AS SurnameSearch, TabCustomers.CustomerDOB AS DOBSearch, TabCustomers.customerid AS MemberSearch
FROM TabCustomers
WHERE TabCustomers.customerid Like IIf([Forms]![FrmSearchCustomer].[Searchmember] Is Null,"*",[Forms]![FrmSearchCustomer]![Searchmember])
AND Trim(TabCustomers.CustomerForname & "") Like IIf([Forms]![FrmSearchCustomer].[SearchFore] Is Null,"*",[Forms]![FrmSearchCustomer]![SearchFore] & "*")
AND Trim(TabCustomers.CustomerSurname & "") like IIf([Forms]![FrmSearchCustomer].[Searchlast] Is Null,"*",[Forms]![FrmSearchCustomer]![SearchLast] & "*")
AND (TabCustomers.CustomerDOB Like IIf([Forms]![FrmSearchCustomer].[SearchDate] Is Null,"*",[Forms]![FrmSearchCustomer]![SearchDate] ) Or TabCustomers.CustomerDOB Is Null)