Parameter Form to Input Query Criteria with Multiple Fields Either With a Value or Null - sql

I have built a form with unbound fields designed so a user can input a date range, a facility name (these come from a combobox), and a badge number to generate a query in Access. I want to be able to return results within the selected date range for all facilities if the field is left blank or just the ones for a particular facility if one is selected. I also want to be able to limit the results to those that match an person's badge number.
So the possibilities I want would be:
Date Range = defined by user | Facility - All if not selected | Badge # = All if not selected
Date Range = defined by user | Facility - All if not selected | Badge # = defined by user
Date Range = defined by user | Facility - defined by user | Badge # = All if not selected
Date Range = defined by user | Facility - defined by user | Badge # = defined by user
I originally built it with just the date range and facility name and it worked fine. When I try to add in the Badge # it doesn't really work correctly.
My SQL for the WHERE TO section is:
WHERE (((Diversion.Transaction_Date) Between [Forms]![Parameters]![FromDate] And [Forms]![Parameters]![ToDate])
AND ((Diversion.Employee_Badge_Number)=[Forms]![Parameters]![BadgeNumber])
AND ((Diversion.Facility)=[Forms]![Parameters]![FacilitySelect]))
OR (((Diversion.Transaction_Date) Between [Forms]![Parameters]![FromDate] And [Forms]![Parameters]![ToDate])
AND ((Diversion.Facility)=[Forms]![Parameters]![FacilitySelect])
AND ((([Diversion].[Employee_Badge_Number]) Like [Forms]![Parameters]![BadgeNumber]) Is Null))
OR (((Diversion.Transaction_Date) Between [Forms]![Parameters]![FromDate] And [Forms]![Parameters]![ToDate])
AND ((Diversion.Employee_Badge_Number)=[Forms]![Parameters]![BadgeNumber])
AND ((([Diversion].[Facility]) Like [Forms]![Parameters]![FacilitySelect]) Is Null))
OR (((Diversion.Transaction_Date) Between [Forms]![Parameters]![FromDate] And [Forms]![Parameters]![ToDate])
AND ((([Diversion].[Employee_Badge_Number]) Like [Forms]![Parameters]![BadgeNumber]) Is Null)
AND ((([Diversion].[Facility]) Like [Forms]![Parameters]![FacilitySelect]) Is Null))
OR (((([Diversion].[Facility]) Like [Forms]![Parameters]![FacilitySelect]) Is Null));
To me, it looks like it is including the four possible results that I want to get from the form, but it isn't working right. For instance, if I leave the facility field blank, and define the badge number, it is still giving me all of the results. If I define the facility and define the badge number it does give me the correct results.
Any ideas?

This might give you some ideas building a dynamic query with multiple criteria values. In this example the user can pick any number of the criteria. It is written in VB.Net. It works with Access. I check each field to see if any criteria was provided then append it to the query if there is a valid value.
I used Interpolated strings just because it is easier to see where the spaces go. The alternative is:
String.Format("RoasterId = {0} ", itgRoaster)
I also used a String builder which is an efficient way to alter strings without the overhead of creating and disposing of them with each append. You could just use &= if this is not available in VBA.
Dim bolNeedAnd As Boolean = False
Dim sb As New Text.StringBuilder
sb.Append("SELECT Coffees.ID, Coffees.[Name], Coffees.RoasterID, Roasters.[Name], Coffees.[Type],Coffees.Rating, Coffees.Comment, Coffees.Description, Coffees.Roast, Coffees.IsExtraBold, Coffees.IsFavorite
From Coffees Inner Join Roasters on Coffees.RoasterID = Roasters.ID Where ")
If itgRoaster <> 0 Then
sb.Append($"RoasterID = {itgRoaster} ")
bolNeedAnd = True
End If
If strRoast <> "" Then
If bolNeedAnd Then
sb.Append($"AND Roast = '{strRoast}' ")
Else
sb.Append($"Roast = '{strRoast}' ")
End If
bolNeedAnd = True
End If
If strType <> "" Then
If bolNeedAnd Then
sb.Append($"AND Type = '{strType}' ")
Else
sb.Append($"Type = '{strType}' ")
End If
bolNeedAnd = True
End If
If strRating <> "" Then
If bolNeedAnd Then
sb.Append($"AND Rating = '{strRating}' ")
Else
sb.Append($"Rating = '{strRating}' ")
End If
bolNeedAnd = True
End If
If bolBold Then
If bolNeedAnd Then
sb.Append("AND IsExtraBold = 1 ")
Else
sb.Append("IsExtraBold = 1 ")
End If
bolNeedAnd = True
End If
If bolFavorite Then
If bolNeedAnd Then
sb.Append("AND IsFavorite = 1 ")
Else
sb.Append("IsFavorite = 1 ")
End If
End If
sb.Append("Order By Coffees.[Name];")
Debug.Print(sb.ToString)
Dim cmd As New OleDbCommand With {
.Connection = cn,
.CommandType = CommandType.Text,
.CommandText = sb.ToString}

Related

Variable that depends on another variable VBA

I have an interesting question about referencing another variable based on another variable in an array.
Below is my code:
Dim company, price, sdev, mean, random
Dim companies
companies = Array("mm", "tgt", "boog")
mm_price = 0
mm_mean = 0
mm_sdev = 0
tgt_price = 0
tgt_mean = 0
tgt_sdev = 0
boog_price = 0
boog_mean = 0
boog_sdev = 0
For i = 1 To 3
company = companies(i)
mean = company & "_mean"
sdev = company & "_sdev"
Next i
Now, the issue occurs when I attempt to define the "mean" and "sdev" variables, as they will not use the "0" value, but instead give it the string name "mm_mean" etc. mm_mean = 0, therefore, I want mean = 0 when i = 1. Clear?
Thanks, and let me know. It is a rather strange question, and the code is cut from many different functions, so if it doesn't make sense as to why I am doing this, my apologies. I tried to make it as simple as possible so it wouldn't confuse the answerer.
This is a good example of when custom data-structures (aka "record types", "structs") should be used to group related data together. In VBA the syntax is Type:
Type Company
Name As String
Price As Currency
Mean As Double
SDev As Double
End Type
Public Sub CalculateCompanyInfo()
Dim companies(3) As Company
companies(0).Name = "mm"
companies(0).Price = 123
companies(1).Name = "tgt"
companies(1).Price = 456
companies(2).Name = "boog"
companies(2).Price = 789
For i As Integer = 0 to UBound(companies)
companies(i).Mean = ...
companies(i).SDev = ...
Next i
End Sub
Where ... means whatever custom calculation you need to do to get that value.

find files then parse the results before displaying the results

I'm working on an app for our customer service division. The user will enter a print number, and a mfg date. the app will then do a get files returning results of all files found with that drawing number (multiple revisions). the files are named in this format drawingnumber_rev_pages_6-digit date.pdf.
Once I have the list of that drawing number, I then take a right(string) of 10 characters to strip the 6-digit date off, and do a Date.ParseExact to compare to the user input mfg date. I was grabbing anything prior to that date, and showing them in a listbox. the criteria has now changed, and they want me to only show the file that would pertain to that build date. The problem is, I need the first rev prior to that date. and I don't fully understand the (function) portion of my getfiles statement. So I don't know what to search for to even lookup google results. as I search for getfiles, function isn't mentioned, if I look up orderby... function isn't mentioned, is it linq I need to search under? how would you propose I approach this?
original version example
new version, I can filter result and find everything before date ... but what I want is the highest revision before the mfg. date. return a single result.
current version
thank you all- here's my sample code.
Try
Dim results = Directory.GetFiles(fp, f).
OrderByDescending(Function(x) New FileInfo(x).FullName)
For Each result In results
Dim dp As String = ""
Dim d As Date
dp = Strings.Right(result, 10)
dp = Strings.Left(dp, 6)
d = Date.ParseExact(dp, "MMddyy", New Globalization.CultureInfo("en-us"))
Debug.Print(dp)
Debug.Print(d)
Dim udt As String = ""
Dim ud As Date 'user date
udt = Trim(Replace(txtMfgDate.Text, "/", ""))
If udt.ToString.Length = 0 Then lstFound.Items.Add(result)
ud = Date.ParseExact(udt, "MMddyy", New Globalization.CultureInfo("en-us"))
If d.Date <= ud.Date Then
lstFound.Items.Add(result)
End If
Debug.Print(d)
Debug.Print(ud)
If result <> Nothing Then
End If
Next
Catch x As Exception
MessageBox.Show("Error finding file: " & f)
End Try
LINQ can do everything you need with the right conditions. I also tried to clean up some of the code in other ways. I switched the LINQ expression to query comprehension to so I could use Let.
Try
Dim enusCulture = New Globalization.CultureInfo("en-us") ' consider CultureInfo.CurrentCulture instead?
Dim udt As String = Trim(Replace(txtMfgDate.Text, "/", ""))
Dim ud As Date
If udt.Length > 0 Then ud = Date.ParseExact(udt, "MMddyy", enusCulture) ' user mfg date
' 0071911_a_1_072115.pdf
Dim results = From filename In Directory.GetFiles(fp, f)
Let filedate = Date.ParseExact(filename.Substring(filename.Length - 10, 6), "MMddyy", enusCulture)
Where udt.Length = 0 OrElse filedate.Date < ud.Date
Order By filedate Descending
If udt.Length> 0 Then
results = results.Take(1)
End If
lstFound.Items.AddRange(results)
Catch x As Exception
MessageBox.Show("Error finding file: " & f)
End Try
The Function(x) syntax is a lambda expression, and it is part of LINQ method syntax for the OrderByDescending function, if you want to research that.

Want to speed up my vb.net For Each loop

I am making a website and I have a for each loop that is taking 2.5 minutes to run through.
I am wondering if anyone knows how i can speed it up. Both of the if statements inside of the loop have to remain there.
Protected Sub WebDataGrid1_InitializeRow(sender As Object, e As Infragistics.Web.UI.GridControls.RowEventArgs) Handles WebDataGrid1.InitializeRow
Dim SqlString As String = ""
Dim TerrMapTable As New DataTable
Dim Terr As String = ""
Dim RgnMgr As String = ""
Dim Reg As String = ""
Dim TerrMap As String = ""
For Each GridRecord As GridRecord In WebDataGrid1.Rows
Terr = GridRecord.Items.FindItemByKey("ABAN8").Value
RgnMgr = GridRecord.Items.FindItemByKey("RgnMgr").Value
If Terr < 9000 Then
Terr = "T" & Terr
Else
Terr = "TA1" & Right(Terr, 2)
End If
SqlString = "Select distinct Terr, Region from TerrReg WHERE Terr='" & Terr & "'"
TerrMapTable = OleFun.GetMyDataTableString(SqlString, 4)
If TerrMapTable.Rows.Count <> 0 Then
Reg = TerrMapTable(0)(1)
TerrMap = "<b>PIC</b>"
GridRecord.Items.FindItemByKey("TerrMap").Text = TerrMap
Else
End If
Next
End Sub
In the event handler for InitializeRow, which I would expect to be called for a single row, you are looping through all of the rows. As a result, I suspect that you have turned an operation that should be linear into the number of rows (with the event firing once for each row, and you operate only on that row) into an operation that is quadratic in the number of rows (for each row, you loop through each row).
Based on the event name, I don't think you should have the For Each loop at all. It looks like you should be getting the appropriate row from the event arguments and operating on that row only.
This could be better suited for code review but here are my thought.
Use string building to concatenate strings
Don't concatenate sql! Use parameters.
Get all the unique Terr and do one query.
Database queries must be your bottleneck.
Terr seems like a mix of number and string. Try option strict on.

Referencing an entire data set instead of a single Cell

I am creating a login form for users to log into using a database. I was wondering if there is a way in which i could get the program to search the entire table instead of a certain item. Here is my code so far.
Dim UserInputtedUsername As String
Dim UserInputtedPassword As String
UserInputtedUsername = txtAdminUsername.Text
UserInputtedPassword = txtAdminPassword.Text
sqlrunnerQuery = "SELECT * FROM tblLogin"
daRunners = New OleDb.OleDbDataAdapter(sqlrunnerQuery, RunnerConnection)
daRunners.Fill(dsRunner, "Login")
If UserInputtedUsername = dsadminlogin.Tables("Login").Rows(0).Item(2) And UserInputtedPassword = dsadminlogin.Tables("Login").Rows(0).Item(3) Then
Form1.Show()
ElseIf MsgBox("You have entered incorrect details") Then
End If
End Sub
Instead if searching the (in-memory) DataSet for your user serach the database in the first place. Therefore you have to use a WHERE in the sql query(with guessed column names):
sqlrunnerQuery = "SELECT * FROM tblLogin WHERE UserName=#UserName AND PassWord=#PassWord"
Note that i've used sql-parameters to prevent sql-injection. You add them in this way:
daRunners = New OleDb.OleDbDataAdapter(sqlrunnerQuery, RunnerConnection)
daRunners.SelectCommand.Parameters.AddWithValue("#UserName", txtAdminUsername.Text)
daRunners.SelectCommand.Parameters.AddWithValue("#PassWord", txtAdminPassword.Text)
Now the table is empty if there is no such user.
If dsadminlogin.Tables("Login").Rows.Count = 0 Then
MsgBox("You have entered incorrect details")
End If
For the sake of completeteness, you can search a complete DataTable with DataTable.Select. But i prefer LINQ-To-DataSet. Here's a simple example:
Dim grishamBooks = From bookRow in tblBooks
Where bookRow.Field(Of String)("Author") = "John Grisham"
Dim weHaveGrisham = grishamBooks.Any()

Linq Dynamic Error with Dynamic Where clause

I have a four dropdownlists that are used to filter/order a gridview.
If any of the first 3 dropdowns have a selected value other than 0 the the WhereFlag is set to True. The fourth dropdown is used to dictate which column the grid is ordered by.
My code for databinding the gridview uses System.Linq.Dynamic and is shown below...
Dim dc As New DiaryDataContext
If WhereFlag = False Then
'This section works...
Dim AList = dc.D_AreaSubAreas _
.OrderBy(ddl_SortBy.SelectedValue)
Dim dl As List(Of D_AreaSubArea)
dl = AList.ToList
dl.Insert(0, New D_AreaSubArea With {.Ref = 0,
.Area = "",
.SubArea = "",
.Allocation = "Allocation...",
.Redundant = False})
gv_AreaSubArea.DataSource = dl
gv_AreaSubArea.DataBind()
'Gridview successfully binds
'If ddl_SortBy value is changed... Gridview binds OK.
Else
'This section gives error...
Dim WhereBuild As New StringBuilder
If ddl_AreaFilter.SelectedIndex <> 0 Then
WhereBuild.Append("Area = '" & ddl_AreaFilter.SelectedValue & "'")
AndFlag = True
End If
If ddl_SubAreaFilter.SelectedIndex <> 0 Then
If AndFlag = True Then
WhereBuild.Append(" AND ")
End If
WhereBuild.Append("SubArea = '" & ddl_SubAreaFilter.SelectedValue & "'")
AndFlag = True
End If
If ddl_AllocFilter.SelectedIndex <> 0 Then
If AndFlag = True Then
WhereBuild.Append(" AND ")
End If
WhereBuild.Append("Allocation = '" & ddl_AllocFilter.SelectedValue & "'")
End If
'ERROR HERE
Dim AList = dc.D_AreaSubAreas _
.Where(WhereBuild.ToString) _
.OrderBy(ddl_SortBy.SelectedValue)
'END ERROR
Dim dl As List(Of D_AreaSubArea)
dl = AList.ToList
dl.Insert(0, New D_AreaSubArea With {.Ref = 0,
.Area = "",
.SubArea = "",
.Allocation = "Allocation...",
.Redundant = False})
gv_AreaSubArea.DataSource = dl
gv_AreaSubArea.DataBind()
End If
The error I get is with the dynamic where clause. The error I get is
ParseException is unhandled by user code. Character Literal must contain exactly one character
It points to the query AList in the Else fork. The only difference between it and the query in the If fork is the addition of the Where clause... but I have been unable to deduce what is wrong with my code. AHA.
The error points to the place where the LINQ query is evaluated and, as the message says, the problem is that a character is expected but several characters were provided.
Check whenever ddl_AreaFilter.SelectedValue, ddl_SubAreaFilter.SelectedValue or ddl_AllocFilter.SelectedValue actually contain a character or a string. If they contain more than one character, you should replace ' with \" when building the where condition, for instance:
WhereBuild.Append("Area = """ & ddl_AreaFilter.SelectedValue & """")
EDIT
You must make sure that the type of the value contained in each SelectedValue string match the corresponding database type. For instance, if the database column is a numeric type, the string content will be casted to a numeric type.
When you specify the value in the comparison using quotes, you are indicating that the type on the right side of the comparison is either character or string (depending on whenever you use single or double quotes, respectively).
So, what are the Area, SubArea and Allocation types in the database?
Single character: the value in your query should be around single quotes: Area = 'value'
Strings (for instance, varchar):you must use double quotes: Area = "value"
Other: then you should use no quotes: Area = value