Microsoft Access 2010 - Dynamic query - sql

I have an access web database that several users will need to log in to. The database contains a table of products.
The challenge is, every user needs to only see a subset of these products and never see the whole list.
At the moment i have some code to modify an existing query based on the logged in user's details. As they log in, some tempvars are created and these are used to modify the query criteria.
This works well when the first user logs in, but the moment the next user logs in, the query is modified again and the product list refreshes and now his products are shown and not the first users! Im thinking i need to dynamically create a permanent query for each user on log in?
Or is a better way to accomplish what im trying ? im quite new to access and struggling. Can anyone assist please?
Here is my code so far:
Button on login form has the following code that collects the user's details
Private Sub cmdLoginMine_Click()
Dim ID as long, strEmpName as string,strZondsc as string,strgrpdsc as string
ID = DLookup("ID", "Employees", "Login='" & Me.txtUser.Value & "'")
strEmpName = DLookup("FullName", "Employees", "Login='" & Me.txtUser.Value & "'")
strgrpdsc = DLookup("MyGrpdscs", "Employees", "Login='" & Me.txtUser.Value & "'")
strzondsc = DLookup("MyZondscs", "Employees", "Login='" & Me.txtUser.Value & "'")
TempVars.Add "tmpEmployeeID", ID
TempVars.Add "tmpEmployeeName", txtUser.Value
I then call a function that modifies the existing query, populating it with this users details for the criteria
qryEdit strgrpdsc, strzondsc, ID
Sub qryEdit(strgrpdsc As String, strzondsc As String, ID As Long)
Dim qdf As DAO.QueryDef
Dim qdfOLD As String
Set qdf = CurrentDb.QueryDefs("InventoryQryforDS")
With qdf
.SQL = "SELECT Products.ProductCode, Products.ProductName, Products.GRPDSC, Categories.Category, Inventory.Available " & _
"FROM (Categories INNER JOIN Products ON Categories.ID = Products.CategoryID) INNER JOIN Inventory ON Products.ID = Inventory.ProductID " & _
"WHERE Products.GRPDSC in (" & strgrpdsc & ") and Categories.Category in (" & strzondsc & ") and products.ownersid =" & ID & _
" ORDER BY Products.ProductCode"
End With
Set qdf = Nothing
End Sub
The results of the query are shown on a form, which is what is currently requerying and showing the wrong data.
Thanks
EDIT - THe data is shown on a form, linked to one of the new style navigation buttons as shown.The recordsource property of the form is the query that's populated as described above.

I have a few suggestions/corrections to your approach to solving this issue.
When using DLookup, make sure they are enclosed within Nz() function, if the value you are looking for is not found you might be facing troubles of assigning a Null value to a Data Type that cannot handle Null. Anything other than Variant type will suffer.
You seem to do not one but four DLookup's on the table. This could be very expensive. This could be minimized by using a simple RecordSet Object.
I would not use TempVars much as they could be tricky to understand and implement.
Why are you editing the Query? This could again be a bit expensive in terms of memory. How are you showing the list of Products? In DataSheet or ComboBox or LsitBox? You could yet again change the RecordSource or RowSource of the Objects rather than editing the Query itself.
Finally, your users should all have a separate copy of the Front End file. Not one copy used by 20-30 people. If the files is restricted to be used by one person, then the code should work regardless of being modified. As the user will have access to the right data at all times.

Related

Access: dynamically query tables

I built an Access database to contain information regarding parts that we use to create schematics. There is one table that contain "basic" information, like "unique part ID" ("TUPID"), links to datasheets and so on - and the "Partition" further information is stored.
Furthermore there are several tables (this is "Partition") that contain information on the part itself: one table for resistors, one for connectors, one for power-ICs and so on. Each table has many fields different from other tables, but there are fields that exist on each table, eg. "Manufacturer", "Symbol", "Package" and "Height".
Now I have a split form ("10_Change_BaseInformation") that shows the "basic information", so when I select one row in the database-part of the form, the data is loaded into textboxes and can be edited. Additionally I want to see the information from the "Partition"-table in this form, so I wrote this:
Private Sub Form_Click()
Dim SelectedPartition As String: SelectedPartition = Forms![10_Change_BaseInformation]![Text25]
'Field "Text25" contains the TUPID
Dim SQLStatement As String
SQLStatement = "SELECT " & SelectedPartition & ".TUPID, " & SelectedPartition & ".[Mfg]" & vbCr & _
"FROM " & SelectedPartition & vbCr & _
"WHERE (((" & SelectedPartition & ".TUPID)=[Forms]![10_Change_BaseInformation]![TUPID]));"
DoCmd.RunSQL SQLStatement
' SQLStatement = "SELECT Resistor.TUPID, Resistor.[Hersteller] FROM ResistorWHERE (((Resistor.TUPID)=[Forms]![10_Change_BaseInformation]![TUPID]));"
End Sub
First of all, I get runtime-error "2342", but I can't make any sense of that; so: how do I have to modify my code, to get a valid result?
Second, how can I get the values from the query to the form?
Thanks in advance for your help!
I can't speak to the reason of your error, as we would need more information such as the types of your fields in your table.
I'm guessing that your Resistor.TUPID field is a string.
If so, you need to modify your code as below:
WHERE (((" & SelectedPartition & ".TUPID)='" & [Forms]![10_Change_BaseInformation]![TUPID] & "'));
If it is of Type number then
WHERE (((" & SelectedPartition & ".TUPID)=" & [Forms]![10_Change_BaseInformation]![TUPID] & "));
As to the form...
Generally speaking, you can design a form without a record source, but with controls that are named the same as your select query fields.
When designing each Control on the form, you have to explicitly specify the Control Source (i.e. The field names that are returned from your select query), otherwise they will be Unbound and never update automatically based on Record Source.
Then assign your SQL query string to the Recordsource of the form.
MyForm.Recordsource = MySQLString
Then run the Requery method of the form.
MyForm.Requery

SQL Syntax error running action Query through VBA

I am trying to build this query through VBA instead of building it in Access and running a docmd.openquery. That seemed to me like the easier route, but I wanted to work on my SQL. Obviously that didn't work as intended if I am here lol.
So, I am trying to take the Date values of 14 text boxes on our JobTicket form and insert them into another table, Tbl_Schedule. This table is not a part of the Query that is the record source for the JobTicket form. I am worried that attempting to add this table in will overload the Query, as it is already very full. When I try to quickly navigate to the last field in that Query the text writes on top of itself, and then Access goes not responding while it clears up the text and loads the last couple fields. Adding another 56 fields to that seems like a recipe for disaster. I will post the SQL I have written below.
DoCmd.RunSQL "INSERT INTO Tbl_Schedule (Date_Scheduled1, Date_Scheduled2, Date_Scheduled3, Date_Scheduled4, Date_Scheduled5, Date_Scheduled6, Date_Scheduled7, " & _
"(Date_Scheduled8, Date_Scheduled9, Date_Scheduled10, Date_Scheduled11, Date_Scheduled12, Date_Scheduled13, Date_Scheduled14)" & _
"VALUES (#" & [Forms]![Frm_JobTicket]![Txt_DateScheduled1_JobTicket] & "#,#" & [Forms]![Frm_JobTicket]![Txt_DateScheduled2_JobTicket] & "#, " & _
"(#" & [Forms]![Frm_JobTicket]![Txt_DateScheduled3_JobTicket] & "#,#" & [Forms]![Frm_JobTicket]![Txt_DateScheduled4_JobTicket] & "#, " & _
"(#" & [Forms]![Frm_JobTicket]![Txt_DateScheduled5_JobTicket] & "#,#" & [Forms]![Frm_JobTicket]![Txt_DateScheduled6_JobTicket] & "#, " & _
"(#" & [Forms]![Frm_JobTicket]![Txt_DateScheduled7_JobTicket] & "#,#" & [Forms]![Frm_JobTicket]![Txt_DateScheduled8_JobTicket] & "#, " & _
"(#" & [Forms]![Frm_JobTicket]![Txt_DateScheduled9_JobTicket] & "#,#" & [Forms]![Frm_JobTicket]![Txt_DateScheduled10_JobTicket] & "#, " & _
"(#" & [Forms]![Frm_JobTicket]![Txt_DateScheduled11_JobTicket] & "#,#" & [Forms]![Frm_JobTicket]![Txt_DateScheduled12_JobTicket] & "#, " & _
"(#" & [Forms]![Frm_JobTicket]![Txt_DateScheduled13_JobTicket] & "#,#" & [Forms]![Frm_JobTicket]![Txt_DateScheduled14_JobTicket] & "#)"
Table being inserted into: Tbl_Schedule
Fields being inserted into: Date_Scheduled1 -to- Date_Scheduled14
Getting data from text boxes: Txt_DateScheduled1_JobTicket -to- Txt_DateScheduled14_JobTicket on Frm_JobTicket
Any other questions that would assist you in assisting me please feel free to ask! Thanks in advance!
Dynamic SQL has its uses, but this is not one of them.
Using DAO methods makes your code so much simpler and easier to read and debug.
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim frm As Access.Form
' for readability
Set frm = Forms!Frm_JobTicket
' open table for adding record(s)
Set db = CurrentDb
Set rs = db.OpenRecordset("Tbl_Schedule", dbOpenDynaset, dbAppendOnly)
rs.AddNew
rs!Date_Scheduled1 = frm!Txt_DateScheduled1_JobTicket
rs!Date_Scheduled2 = frm!Txt_DateScheduled2_JobTicket
' etc.
rs.Update
rs.Close
With enumerated field names like these, you can also use a loop:
Dim i As Long
rs.AddNew
For i = 1 To 14
rs("Date_Scheduled" & i) = frm("Txt_DateScheduled" & i & "_JobTicket")
Next i
rs.Update
This is a good opportunity to consider normalizing your data so that part of your problem is removed entirely. Instead of having DateScheduled1_JobTicket, DateScheduled2_JobTicket etc., it might be better to have another table which fills vertically instead of horizontally, perhaps with fields like ID, Item, JobTicketNumber, ScheduledDate.
Then, fill this table with a row for each item/sku/product, and date. You'll have 14 rows for scheduled tickets for each item/sku/product instead of 14 columns, and this will also solve your future problem of adding 56 fields. The benefit is that you can present the job ticket schedule rows by using continuous forms (in a list). Even better, you can put this continuous form with dates as a subform on your item/sku/product main form, which will then show as a neat list of scheduled tickets that will automatically change as you scroll through item/sku/products.
If you don't use continuous forms, you can still use an unbound approach as you're using now. One benefit is that it will be much easier when you need to add future JobTicket numbers, since you can just add more rows instead of adding fields and having to do additional design work.
If you want to view data in the flattened way that you built your table, you can use a Crosstab query to present it as you have in your table, but the underlying data will be much better stored in a normalized format.
Note that you don't need to concatenate a string as you did above; just leave the Forms!Form!Control reference expression directly in the query and you have a nice parameterized query that will execute just fine, so long as there are dates in those controls (text box, drop down etc).
ex.
Insert Into (MyDateField) Values (Forms!MyForm!MyDateControl);
No dynamic SQL needed.

Is there a simple code to allow certain user(s) access to see command buttons?

Okay, I am having difficulties coding security behind my user form. Let me give you guys the rundown. I created this make table "tblPermissionTypes" that basically has two field in there "ID" & "EmployeeType_ID". The ID field represents Security level of access 0 through 2, and EmployeeType_ID is the title: 0 = Requestor, 1 = Admin, and 2 = Printer.
With that being said I have another table "tblEmployees" with the same field "EmployeeType_ID", I manually set the 0s, 1s, & 2s. This table also contains all employees UserNames
Finally, I have another table "tblPermission" that contains three fields "EmployeeType_ID", "FormName", and "HasAccess"
My end result is being whenever this tblPermission has a Checkbox under the field HasAccess I want to grant access based on the EmployeeType_ID field to communicate back to the table "tblEmployees", but in this case I want them to only be able to see a button that contains that certain form.
Private Sub cmdClick_Click()
Dim strSQL As String
Dim permission As String
If permission = ("fOSUserName") = True Then
Run strSQL
strSQL = "SELECT * FROM tblEmployees WHERE "
strSQL = strSQL & "tblEmployees.Five2 =" & ("fOSUserName") & """, = False
Then
MsgBox "You do not have permission!", vbExclamation
Else'
cmdButton.Visible True
End If
NOTE: fOSUserName, is a function I created basically the same thing as
Environ("UserName")
Debug.Print strSQL
Function calls should not be within quote marks. You construct an SQL statement but then don't properly use it. You declare and use variable permission but don't set the variable - it is an empty string. Need form name as a search criteria. Couple of other syntax errors but they will go away with this suggested code. Run this code in form Open event to disable button and don't even give unauthorized users opportunity to click. Don't annoy them with a popup message that emphasizes their lowly status in hierarchy.
You need to build a query that joins tblEmployees to tblPermission so that UserName, FormName, HasAccess fields are all available then reference that query in search for permission.
A DLookup could serve here.
Me.buttonNameHere.Visible = Nz(DLookup("HasAccess", "queryNameHere", _
"UserName='" & fOSUserName & "' AND FormName='FormNameHere'"), 0)

Passing a query a parameter [Access 2016]

To make a longer story shorter:
I'm an Access noob, doing a quick-and-dirty conversion of a massive Excel spreadsheet into an Access database. Part of the requirements are to mimic some of the functionality of Excel, specifically, pulling data from a certain table and doing some basic calculations on it (sums, averages, etc.).
I've written a chain of queries to pull the data, count/sum it, etc., and have been testing them by using a manually-entered Parameter (i.e., the kind where the input box pops up and asks you to type a response). Now that I'm ready to drop these queries into a (sub)form, though, I have no idea how to automatically pass that parameter from a box in the form into the subform into the query.
Every query I've written uses a manually-entered Parameter named "MATCHNAME," which holds the name of an individual. In manual testing, if I enter this parameter on one query, all the queries it calls also get that value. So, I think I just need to figure out how to tell the top query what MATCHNAME actually is, and that'll take care of it.
Problem is, I don't know how to do that in Access. If it was any other programming language, I'd do something like "queryXYZ(MATCHNAME);", but I don't think I can do that in Access. Plus, since the values queryXYZ returns are all calculated, I'm not sure how to add an extra MATCHNAME field, nor how to actually make sure that gets read by the queries, nor how to make sure it gets passed down the chain. I've even tried creating a Parameter in design view, then trying to set up Link Master Fields, but the Parameter doesn't appear in that window.
I'd also like to re-run these queries whenever a new record is pulled up, but I'm not sure how to do that either--i.e., the numbers should be current for whatever record I'm looking at.
And, before we go there--I feel like a Relationship is out of the question, as the data itself is auto-generated, and is in rough enough shape to where I can't guarantee that any given key is wholly unique, and large enough (20k+) that, outside of writing a magical script, I can't assign a numerical key. However, I don't know much about Relationships in Access, so please prove me wrong.
(Is this all making sense?)
Do you have any suggestions for me--for how to make a subform read a field on the main form to run its queries on? Alternately, is there an easier way to do this, i.e., to bed SQL calls inside a form?
Thanks very much for your help...
You can use SQL as the recordsource of the subform in the property tab and use the afterupdate event of your matchname field to change yourform.recordsource = "Select * from table where filteredfieldname = & me.matchname & ";" . You can also use sql as the control source of form fields. To pass criteria to filter the subform using the whole table as the recordsource, add an event procedure to your field's after update event like this
`In the declarataions at the top
Global mtchnmfltr as string
Private Sub MATCHNAME_AfterUpdate()
'use the same procedure for Private Sub yourmainform_Current()
mtchnmfltr = "[yourfilterfield] = " & Chr(34) & me.matchname & Chr(34)
'if matchname is not text then just = "[yourfilterfield] = " & me.matchname
with me.subformname.form
.filter = mtchnmfltr
.filteron = true
end with
'Build your sql as a string for your sum avg fields etc. using mtchnmfltr in the where clause
me.yoursumfield.controlsource = "Select...where " & mtchnmfltr & ";"
'etc.
end sub
Or you could throw Matchname into a sql recordsource of the subform and add the function fields to the subform on the same on current and after update events
if me.newrecord = true then
me.dirty = false
end if
me.subform.form.recordsource = "Select Table.Matchname, sum(yourfield) as sumalias, _
(etc.) from yourtable where table.matchname = " & chr(34) & me.matchname & _
chr(34) & Group By table.matchname"
If you are storing your sums etc in a table you need to do it a bit different, since your controls controlsource are bound to fields.
dim strsqlsumfld as string
dim rs as dao.recordset
strsqlsumfld= "Select SUM.....AS sumfldalias where " & mtchnmfltr & ";"
set rs = currentdb.openrecordset(strsqlsumfld)
me.yoursumfield = rs("sumfldalias")
rs.close

Referencing Variable for Functions

Every two weeks there will be a new table with new data coming in to me, I need to update that onto a master table. I want to automate this process.
I would like to declare a variable that a user can input the date the data is from, and based on that date update to the appropriate field on the master table. I have no clue how to make SQL functions use variables to locate tables with the syntax, not even sure if this can be done. Any help would be appreciated.
As I am inexperienced, I am making a VBA Macro and embedding the SQL code from the access query I made.
Sub UpdateFieldX()
Dim SQL As String
SQL = "UPDATE [15-Jun-16] " & _
"RIGHT JOIN MasterSPI " & _
"ON [15-Jun-16].[SR#] = MasterSPI.[SR#] " & _
"SET MasterSPI.[30-Jun-16] = [15-Jun-16].[SPI]; " _
DoCmd.RunSQL SQL
End Sub
I've done something similar to this and used an Update Query and Insert Query.
It sounds like you're trying to just update any existing records that may need updating and then adding new records into the table.
For an update, I'd try creating a query in SQL view and type something like:
UPDATE MasterSPI SET MasterSPI.SRnumber = [newtablename]![SRnumber] etc.
and do that for each field in your table.
For inserting you could do something like:
INSERT INTO MasterSPI SELECT * FROM newtablename
That may help you get started.
I know many of you are advanced users but I found an answer that fit my quick needs. As a notice and disclaimer for other people, it will be in no way secure, it is more for personal use such that I do not need to spend an hour doing append and updates to several master tables repetitively.
To do this, I made a global string variable called name, then I made another variable called strung that translated it into a readable string for referencing. I then made an SQL string that references that strung variable. This allows the user to submit an input for the global variable, which translates into a readable string for the RunSQL method.
Here's a snippet of the parts that were most relevant, I hope it helps whoever comes next with the same scenario.
**
Public name As String 'global variable declared so that the new field to be created can be used elsewhere
name = InputBox("Name of Month", "InputBox") 'variable that takes user input and it will be name for new fields to be added
Dim SQLa As String 'creates a string variable such that it will be read as an SQL line by the RunSQL method
strung = "[" & name & "]" 'sets strung as global varible name, which is user input on the specific table being used to update
'the following lines runs a query such that it updates the master table with new data and does the grouping by SR number
SQLa = "UPDATE " & strung & " " & _
"RIGHT JOIN MasterSPI " & _
"ON " & strung & "." & "[SR#] = MasterSPI.[SR#] " & _
"SET MasterSPI." & strung & " = " & strung & "." & "[SPI]; " _
DoCmd.RunSQL SQLa 'executes the SQL code in VBA **