Using a query in Form On-Open event -- MS Access - vba

So I have a couple forms. In the parent form, I collect information about a User and include a UserID field. Additionally in the parent form, there are several buttons that a data-enterer can click dictating several different categories.
When the data-enterer clicks on a category button, the engine opens a child UserCategory form. I've included logic in the On-Click event of the button (as well as the On-Open event of the child form) such that the UserID value is passed from the parent form record, and the CategoryID value is determined by the button clicked and is also passed to the child form. The combination of UserID and CategoryID is unique for the underlying UserCategory table in question and represents a key of the table, however, the primary key is another field -- UserCategoryID (which has a 1:1 relationship between the combination of UserID and CategoryID).
A requirement, then, is that if the form receives a combination of UserID and CategoryID that is already in the table, then the form should reference that record. However, the form will go ahead and create a new record when its given values that are already represented. I've encountered this problem before when creating related forms, and the problem arose because the table wasn't respecting the 1:1 relationship between the FK and the PK of the related table. In all cases, I was able to fix the problem by enforcing a 1:1 relationship using the Relationships Database Tool. However, in this scenario, a user can belong to multiple categories and a category can have multiple users -- in other words, the UserID field is 1:M and the CategoryID field is 1:M, but their combo is 1:1. I don't know if there's a way to enforce this relationship.
One potential solution I've thought of is to build a query in the On-Open event of a form. It will use the UserID and CategoryID to select the UserCategoryID from the table, which will then be fed into the field on the form. I've included my initial thoughts on the design of the query. Please excuse the syntax -- VBA beginner.
Any help is appreciated
EDIT -- Logic on the passer side:
Private Sub FLower_Click()
Dim catType As Integer
catType = 2
DoCmd.OpenForm "frm_RespondentUseCat", OpenArgs:=2
End Sub
Logic on the Passee side
Private Sub Form_Open(Cancel As Integer)
Dim catType As Integer
catType = Forms.frm_RespondentUseCat!OpenArgs
Dim userID As Integer
userID = Forms!Respondent.RespondentID.Value
Dim strSQL As String
strSQL = "SELECT * From RespondentUseCategories WHERE RespondentID = " & userID & " AND UseCategoryID = " & catType & ";"
Dim results As DAO.Recordset
Set results = CurrentDb.OpenRecordset(strSQL)
If results.RecordCount > 0 Then
Set Forms("frm_RespondentUseCat").Recordset = results
Else
Me.UseCategoryID.Value = catType
Me.RespondentID.Value = userID
End If
End Sub
Essentially the passer (i.e. the parent form with the button) will simply pass the category type reflecting what button was clicked. On the passee side, the form will execute some code on the event that it is opened. First it will collect the category type, then it will attempt to read the userID (RespondentID) value from the parent form (Respondent). Then it will use those two values to get the relevant record from the UserCategory table (RespondentUseCategories). If there is any data here, then we use that as the recordset for the form, and if not, we populate the userID and categoryID with the collected values and let the UserCateogryID autopopulated itself. This seems like the correct approach, however, when I tried the test-case where the record exists (e.g. an already encountered combo of User & Category IDS), the child form remains unpopulated. I'm guessing theres a bug in the VBA.

create a dual key on the UserCategory table using UserID and CategoryID as shown here:
https://www.access-programmers.co.uk/forums/threads/how-to-put-two-primary-keys-in-one-table.225908/
Now Access will throw an error if anyone finds a way to try and add a non unique combinations of UserID and CategoryID. Next using the designer I expect you will get something like:
note textboxes have been replaced with comboboxes and the UsersForm is all header because the subform shows the details. The Userform record source is Users & the subform record source is UserCategory. The form and subform are linked by UserID only. At this point access throws an error if someone trys to enter a non unique combination of user and category, but I don't want to be able to enter incorrect data so I came up with the following:
under the data properties of the UserCategorysForm set both allow dataentry and allow additions to no so the data enterer must use the buttons to add records then set the buttons to add a user/category combination if it doesn't exist and delete that combination if it does.
Private Sub cmbUserName_AfterUpdate()
Me.Filter = "UserID = " & Me.cmbUserName.Value
Me.FilterOn = True
End Sub
'TODO ADD CODE TO CHANGE BUTTON CAPTION BASED ON WHETHER BUTTON WILL ADD OR REMOVE CATEGORY
Private Sub cmdCommand_Click()
AddRemoveRecordFromUserCategoryTable 1
End Sub
Private Sub cmdScience_Click()
AddRemoveRecordFromUserCategoryTable 2
End Sub
Private Sub AddRemoveRecordFromUserCategoryTable(ID As Integer)
DoCmd.SetWarnings False
If IsNull(DLookup("UserCategoryID", "UserCategory", "UserID = " & Me.cmbUserName.Value & " AND CategoryID = " & ID)) Then
DoCmd.RunSQL ("INSERT INTO UserCategory (UserID, CategoryID) VALUES(" & Me.cmbUserName.Value & ", " & ID & ")")
Else
DoCmd.RunSQL ("DELETE * FROM UserCategory WHERE UserID = " & Me.cmbUserName.Value & " AND CategoryID = " & ID)
End If
DoCmd.SetWarnings True
Me.Refresh
End Sub
now the form checks to see if the user/category combination is already in the table before adding it and instead removes the record preventing duplicate combinations from being added. hitting command removes the record (not shown). hitting science adds a record.

Related

Hide field based on date field on access form

I would like to hide a text field based on a date field on an access form. The use will enter a date in the field when they leave, but blank if they are current. I display two dates for years of service (current and after they leave).
Private Sub Form_Current()
If IsNull(DateEnd) Then
Me.YearsOfSeriviceEND.Visible = False
Else
Me.YearsOfSeriviceEND.Visible = True
End If
This does not hide the field, YearsOfSeriviceEND. I'm not sure if this is not working because the field is a date?
Dennis

3-level Hierarchical SQL Query

I'm very well aware of basic SQL queries, however I've never worked with a query that deals with hierarchical data.
I'm using the Telerik TreeView control to display data related to a school board with the following structure:
--School "ABC"
----Class "Grade 1"
----Class "Grade 2"
------Student #1
------Student #2
--School "DEF"
----Class "Grade 1"
------Student #3
----Class "Grade 3"
The TreeView is in a tri-state checkbox mode, so each element can be checked off. So if a single child element is checked then the parent element will be checked as well, and when checking a parent element all the child elements will be checked.
After checking off some schools, classes, and students, the following screen displays information about the students in a graph which currently uses the school IDs (if multiple are checked) to select all students of those schools.
Here's where it gets tricky. Using the above structure as an example, I select the class "Grade 1" from the school "ABC" and class "Grade 3" from the school "DEF" which in turn will select the students #1 & #2 AND the schools "ABC" & "DEF".
Selected schools: ABC, DEF
Selected classes: Grade 1, Grade 3
Selected Students: #1, #2
As mentioned before, my current SQL query is based solely on the school ID and I know that I can't simply add two other conditions in the where clause that look like this:
AND ClassID IN ('Grade 1', 'Grade 3') --Note there is no primary key for classes, and I can't change that in my position..
AND StudentID IN (1,2)
as this will also select student #3 from the other class title "Grade 1"
So my question is, how to I combine the School ID(s), Class name(s), and student ID(s) into one condition that will solve this issue.
Edit:
Here's a structure of the tables.
If i understand you correctly you have the selected school, class and student. So would this work for you:
where (school = "ABC" and
class = "Grade1")
OR
(school = "DEF" and
class = "Grade3")
I hope i understood your requirements correctly.

Multiple Outer Join Condition LLBLGen

I have the following LLBLGen code that retrieves articles by Category. Essentially it is selecting from the article table where the articles are not marked for deletion and joining on the ArticleTopicCategory table to retrieve specific categories (where category = 'string')
ArticleCollection articles = new ArticleCollection();
IPredicateExpression articlesFilter = new PredicateExpression();
articlesFilter.Add(ArticleFields.IsFlaggedForDeletion != true);
PrefetchPath prefetchTopic = new PrefetchPath(EntityType.ArticleEntity);
prefetchTopic.Add(ArticleEntity.PrefetchPathTopic);
prefetchTopic.Add(ArticleEntity.PrefetchPathArticleTopicCategories).SubPath.Add(ArticleTopicCategoryEntity.PrefetchPathTopicCategory);
articles.GetMulti(articlesFilter, prefetchTopic);
I have added another table named SuppressedArticle which is a 1 to many and contains Id, OrganizationId, and ArticleId. The theory is that since articles are syndicated to multiple websites, if "Website A" did not want to publish "Article A" they could suppress it, i.e insert a record into the SuppressedArticle table.
On the article admin screen, i'd like to add a link button to suppress/unsuppress the article, by adding a left join with the two conditions like:
left join SuppressedArticle on (Article.Id = SuppressedArticle.articleId and SuppressedArticle.organizationId='CC177558-85CC-45CC-B4E6-805BDD1EECCC')
I tried adding the multiple join like so, but I cast/conversion error:
"Cannot implicitly convert type 'SD.LLBLGen.Pro.ORMSupportClasses.FieldCompareValuePredicate' to 'SD.LLBLGen.Pro.ORMSupportClasses.IPredicateExpression'. An explicit conversion exists (are you missing a cast?)"
IRelationCollection relations = new RelationCollection();
relations.Add(ArticleEntity.Relations.SuppressedArticleEntityUsingArticleId, JoinHint.Left).CustomFilter = new FieldCompareValuePredicate(SuppressedArticleFields.OrganizationId, ComparisonOperator.Equal, this.CurrentIdentity.OrganizationId);
Any help would be greatly appreciated!
CustomFilter is of type IPredicateExpression, you create a predicate (of type IPredicate) and assign it to that property, which of course doesn't work :)
do:
IRelationCollection relations = new RelationCollection();
relations.Add(ArticleEntity.Relations.SuppressedArticleEntityUsingArticleId, JoinHint.Left)
.CustomFilter = new PredicateExpression(SuppressedArticleFields.OrganizationId == this.CurrentIdentity.OrganizationId);

DataGridView and DataSource: Any way to display fields linked to by foreign keys easily?

I have a three-tier Windows Forms DB application in VB.NET. I'm using VS 2005.
I'd like to display records in a table in a DataGridView. I can already display the records as is by binding the DataSource to the business class that talks to the DB class:
Dim assetList as List(Of Asset)
assetList = DB_Asset.GetAssetListOrderByID_Asset
AssetDataGridView.DataSource = assetList
"Asset" is my business class, and "DB_Asset" is my DB class that queries the DB to return assetList.
Now, Asset has members something like this:
Private m_ID_Asset As Integer
Private m_CategoryID As Integer
Private m_CustodianID As Integer
Private m_ManufacturerID As Integer
Private m_SignedOutToID As Integer
Private m_DefaultLocationID As Integer
Private m_CurrentLocationID As Integer
Private m_DateAcquired As Date
Private m_DateEntered As Date
Private m_EnteredByID As Integer
m _ ID _ Asset contains the primary key for the Asset table in the DB, and everything else of the form m_XXXXXXXXXID contains a foreign key to another table in the DB.
So basically what I get now is rows with a whole lot of numbers. It's exactly what's in the Assets table:
ID_Asset CategoryID CustodianID ManufacturerID SignedOutToID
1 17 23 14 5
What I'd like to know is if there's an easy way to display the text fields that I've linked to with all of those foreign keys:
ID_Asset CategoryName CustodianName Manufacturer SignedOutTo
1 Soda - Diet John Coca-Cola Fred
Anyone with experience here have any tricks?
Thanks in advance!
If you have a List(Of Asset) then you basically just change your query DB_Asset.GetAssetListOrderByID_Asset so you are doing your required joins in there and assigning the values out of that property to assetList.
You would want to add something like m_CustomerName As String to your Asset class, along with whatever else you'll be retrieving so you have a place to put the data.
If you can't change your Asset class you probably want to wrap something around it that gets all those strings for you before you bind.

Linqtosql - joined rows

In Linqtosql how do I show items from multiple rows in a single field.
eg I have a 3 table setup for tagging(entity, tag, entitytag) all linked via foreign keys.
For each entity I would like to return the name in one field and then all relevant tags in 2nd field.
eg Item1, tag1; tag2; tag3
Item2, tag4, tag5....
VB statements preferred.
Thanks
Geoff
Okay, not sure if this is the most efficient way but it works.
Dim dc As New DataContext
Dim query = From i In dc.Items _
Let tags = (From t In dc.ItemTags _
Where t.ItemID = i.ID _
Select t.Tag.Name).ToArray _
Select i.ItemName, Tags = String.Join(" | ", tags)
With this answer I am assuming you have your tables setup similar to the following, names are not great, just for illustration:
AnEntity: Id, Name
ATag: Id, TagName
EntityTag: EntityId (FK to AnEntity.Id), TagId (FK to ATag.Id)
You might try this:
var entityTags = from ent in theEntities
from enttags in ent.EntityTags
group enttags by enttags.AnEntity into entityGroup
select new { TheEntity = entityGroup.Key, TheTags =
from t in entityGroup
select t.ATag.TagName };
I have not been able to actually test this, I'll give it a shot this afternoon and edit it if need be. What is happening here is a SelectMany. The 'from ent in dc.AnEntities' gets all of the entity records, then the next 'from enttags in ent.EntityTags' gets all the entity tag records for each entity. The group by does pretty much that, groups the EntityTag records by AnEntity. Put them in an anonymous type and you are good to go.
EDITED:
Okay, changed the code above, it works now. Before you would get a list of the EntityTag objects, now you get the Entiy object and a list of strings (tags) for that entity.