MS Access extracting elements from multi-valued field based on condition - sql

I get a report where there is a multi-valued field that houses user roles and the respective user information. There are a total of 6 roles and each row of data could have multiple people operating under the same role. I am using MS Access 2013 to do my data manipulation.
ID Data Users
1 Data1 Supervisor (MICROSOFT OUTLOOK; microsoftoutlook#example.com 9999999999) Admin Assistant (WATER BOTTLE; waterbottle#example.com, 9999999999) Analyst (GREEN BLUE; greenblue#example.com; 999999999)
2 Data2 Supervisor (COMPUTER MONITOR; computermonitor#example.com; 9999999999) Admin Assistant (MICROSOFT EXCEL; microsoftexcel#example.com, 9999999999) Analyst (GREEN BLUE; greenblue#example.com; 999999999); Analyst (ORANGE PURPLE; orangepurple#example.com; 999999991)
3 Data3 Supervisor (GREEN BLUE; greenblue#example.com; 9999999999) Admin (MICROSOFT ACCESS; microsoftaccess#example.com, 9999999999) Analyst (ORANGE PURPLE; orangepurple#example.com; 999999999); Analyst (YELLOW BLACK; yellowblack#example.com; 999999991)
The above is a truncated and substituted data set. In ID = 2 and 3, there are 2 analysts. There could be a case where there are 2 "Admin Assistants". The formatting for the Users is exactly as listed. Individual users could be listed across multiple rows as well and under different roles sometimes.
I don't necessarily need to split the data apart, but I do need pick out certain roles based on the Data column. If Data = [Certain Condition], then pull in a certain user role. For example, if Data = "Completed", I need to pull all the "Supervisors" for that row. If Data = "In progress", I need to pull all the "Analysts" for that row. There are other conditionals too for determining which role and users to pull. I need to pull both the user role and the users associated with that role for that row. I would like to create 2 new columns for "Ownership Role" and "Current Ownership". "Ownership Role" is the User Role and "Current Ownership" should contain all the users associated with that role for that row.
Please let me know if you have any questions or need any clarification. I appreciate you taking the time to read this.
UPDATE
My desired results are below.
ID Data Ownership Role Current Ownership Users
1 Completed Supervisor MICROSOFT OUTLOOK Supervisor (MICROSOFT OUTLOOK; microsoftoutlook#example.com 9999999999) Admin Assistant (WATER BOTTLE; waterbottle#example.com, 9999999999) Analyst (GREEN BLUE; greenblue#example.com; 999999999)
2 In Progress Analyst GREEN BLUE, ORANGE PURPLE Supervisor (COMPUTER MONITOR; computermonitor#example.com; 9999999999) Admin Assistant (MICROSOFT EXCEL; microsoftexcel#example.com, 9999999999) Analyst (GREEN BLUE; greenblue#example.com; 999999999); Analyst (ORANGE PURPLE; orangepurple#example.com; 999999991)
3 Initiated Admin MICROSOFT ACCESS Supervisor (GREEN BLUE; greenblue#example.com; 9999999999) Admin (MICROSOFT ACCESS; microsoftaccess#example.com, 9999999999) Analyst (ORANGE PURPLE; orangepurple#example.com; 999999999); Analyst (YELLOW BLACK; yellowblack#example.com; 999999991)
UPDATE 2019-05-10
Due to my failure to properly explain the question and set up a sample data set, I am posting the cases based on June7's answer and a sample row of data.
Case "Initiated"
strRole = "Main Admin Assistant"
Case "Drafted"
strRole = "Financial Analyst"
Case "Rated"
strRole = "Contractor Rep"
Case "Reviewed"
strRole = "Assessing Official"
Case "Finalized"
strRole = "Reviewing Official"
Sample Data - this is all in 1 cell in Excel and each element is on a different line in that cell. Immediately after the close parenthesis, there is NO space from what I can tell so the next user role begins IMMEDIATELY.
Supervisor (ERGO KB; ergokb#example.com; (999) 999-9999)Team Lead (WIDE SCREEN; widescreen#example.com; 9999999999)Team Rep (CELL PHONE; cellphone#example.com; 999-999-9999)Team Rep (CLICK PEN; clickpen#example.com; (999) 999-9999)Main Admin Assistant (WIRED MOUSE; wiredmouse#example.com; 999-999-9999)Main Admin Assistant (PHONE CHARGER; phonecharger#example.com; 9999999999)Financial Analyst (WATER BOTTLE; waterbottle#example.com; (999) 999-9999)Financial Analyst (CLEAR TAPE; cleartape#example.com; 999-999-9999)Human Resources (POST IT NOTE; postitnote#example.com; 999-999-9999)
For each status below, I want the associated User Role.
Status User Role
Initiated Main Admin Assistant
Drafted Financial Analyst
Rated Team Rep
Reviewed Financial Analyst
Finalized Human Resources
Completed Completed
Real Desired Results
Status User Role Users
Initiated Main Admin Assistant WIRED MOUSE, PHONE CHARGER
Drafted Financial Analyst WATER BOTTLE, CLEAR TAPE
Rated Team Rep CELL PHONE, CLICK PEN
Reviewed Financial Analyst WATER BOTTLE, CLEAR TAPE
Finalized Human Resources POST IT NOTE
Completed Completed Completed

Consistency in structure is critical when parsing strings. For instance, there is a semi-colon after ) between the last two elements of IDs 2 and 3 but no semi-colon after the others. That extra semi-colon complicates programming logic. Since comments say that semi-colon is a typo as well as no space following ), code reflects those corrections in structure.
Here is a function to get you started. Place this procedure in a general module and it can be called from query or textbox.
Function GetUsers(strData As String, strUsers As String) As String
Dim aryS As Variant, x As Integer, strRole As String, strNames As String
aryS = Split(strUsers, ")")
Select Case strData
Case "Initiated"
strRole = "Main Admin Assistant"
Case "Drafted"
strRole = "Financial Analyst"
Case "Rated"
strRole = "Team Rep"
Case "Reviewed"
strRole = "Financial Analyst"
Case "Finalized"
strRole = "Human Resources"
End Select
For x = 0 To UBound(aryS) - 1
If strRole = Left(aryS(x), InStr(aryS(x), "(") - 2) Then
strNames = strNames & Mid(aryS(x), InStr(aryS(x), "(") + 1, InStr(aryS(x), ";") - 1 - InStr(aryS(x), "(")) & ", "
End If
Next
If strNames <> "" Then GetUsers = Left(strNames, Len(strNames) - 2)
End Function
Instead of determining Role by calculation with Data, suggest a table that defines these associations. Include that table in query then pass the Role to function instead of Data and the Case block should not be needed.
Revised code to deal with inconsistent phone number structure that sometimes has parens causing previous code to fail as well as the possibility of similar role names (I probably should have gone this way to begin with):
Function GetUsers(strData As String, strUsers As String) As String
Dim strRole As String, strNames As String
Select Case strData
Case "Initiated"
strRole = "Main Admin Assistant ("
Case "Drafted"
strRole = "Financial Analyst ("
Case "Rated"
strRole = "Team Rep ("
Case "Reviewed"
strRole = "Financial Analyst ("
Case "Finalized"
strRole = "Human Resources ("
Case "Completed"
strRole = "Financial Analyst Jr ("
End Select
Do While InStr(strUsers, strRole) > 0
strUsers = Mid(strUsers, InStr(strUsers, strRole))
strNames = strNames & Mid(strUsers, InStr(strUsers, "(") + 1, InStr(strUsers, ";") - Len(strRole) - 1) & ", "
strUsers = Mid(strUsers, 2)
Loop
If strNames <> "" Then GetUsers = Left(strNames, Len(strNames) - 2)
End Function

Related

Search reference in text (Create Loop in function)

I have a question I've been working with view for a while now and in a table I have a text formatted this way ... name of the book, name of the author, number of pages or name of the book, name of the edition, year of the edition, name of the author, number of pages. I want to make sure that when I move my mouse over the reference ([name of the book] or [name of the book, name of the edition, year of the edition]) the description is displayed after returning my api. The way to find the reference with only the name of the book works but I don't know how to search on a single parameter or 3 in my function.
getBiblio(biblio) {
this.resultbiblio = biblio.split(/\s*,|$\s*/, 1)
axios
.get('../api/biblio/' + this.resultbiblio )
.then(response => (this.biblio = response.data))
},
biblio = Les Fables, Jean de la Fontaine, 1600
Now let's say that my data is biblio: L'avare, édition de poche, 2002, Molière, 1300 I would like to test if there is a reference to L'avare, if the answer is no then I cut the biblio data to form the sentence L'avare, édition de poche, 2002 and I test to see if there is a reference.

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

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.

Searching on pubmed using biopython

I am trying to input over 200 entries into pubmed in order to record the number of articles published by an author and to refine the search by including his/her mentor and institution. I have tried to do this using biopython and xlrd (the code is below), but I am consistently getting 0 results for all three formats of inquiries (1. by name, 2. by name and institution name, and 3. by name and mentor's name). Are there steps of troubleshooting that I can do, or should I use a different format when using the keywords indicated below to search on pubmed?
Example output of the input queries;search_term is a linked list with lists of the input queries.
print(*search_term[8:15], sep='\n')
[text:'Andrew Bland', 'Weill Cornell Medical College', text:'David Cutler MD']
[text:'Andy Price', 'University of Alabama at Birmingham School of Medicine', text:'Jason Warem, PhD']
[text:'Bah Chamin', 'University of Texas Southwestern Medical School', text:'Dr. Timothy Hillar']
[text:'Eduo Cera', 'University of Colorado School of Medicine', text:'Dr. Tim']
Code used to generate the input queries above and to search on Pubmed:
Entrez.email = "mollyzhaoe#college.harvard.edu"
for search_term in search_terms[8:55]:
handle = Entrez.egquery(term="{0} AND ((2010[Date - Publication] : 2017[Date - Publication])) ".format(search_term[0]))
handle_1 = Entrez.egquery(term = "{0} AND ((2010[Date - Publication] : 2017[Date - Publication])) AND {1}".format(search_term[0], search_term[2]))
handle_2 = Entrez.egquery(term = "{0} AND ((2010[Date - Publication] : 2017[Date - Publication])) AND {1}".format(search_term[0], search_term[1]))
record = Entrez.read(handle)
record_1 = Entrez.read(handle_1)
record_2 = Entrez.read(handle_2)
pubmed_count = ['','','']
for row in record["eGQueryResult"]:
if row["DbName"] == "pubmed":
pubmed_count[0] = row["Count"]
for row in record_1["eGQueryResult"]:
if row["DbName"] == "pubmed":
pubmed_count[1] = row["Count"]
for row in record_2["eGQueryResult"]:
if row["DbName"] == "pubmed":
pubmed_count[2] = row["Count"]
Check your indentation, it is difficult to know which part belongs to which loop.
If you want to troubleshoot, try printing your egquery, e.g.
print("{0} AND ((2010[Date - Publication] : 2017[Date - Publication])) ".format(search_term[0]))
and paste the output to pubmed and see what you get. Perhaps modify it a bit and see which search term causes the problems.
Your input format is a little bit hard to guess. Print the query and make sure you are getting the right search values.
For the author names, try to get rid of the academic titles, PubMed might confused them with the initials, e.g. House MD, might be Mark David House.

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.

select instance where instance OR associations match fuzzy text

This is hard to explain, so I'll start with what I've got.
User has_many Dependents
Users and Dependents have names.
I want a scope on User that will return all of the users whose names, OR whose dependents names match a certain fuzzy text.
It seems to not return all of the expected results. This is my current scope:
scope :by_fuzzy_text, lambda { |text|
text = "%#{text}%"
joins(:dependents).where('name ILIKE ? OR dependents.name ILIKE ?', text, text).uniq
}
The issue is that it's returning unexpected results. It seems to only work on the first 11 Users in my database, despite there being over 100. So no matter how perfectly the scope matches, it will only return the results who are one of the first 11 users in the database. I do NOT have this issue if I'm not using Joins.
Hopefully that makes sense. The uniq on the end is because if the user has multiple dependents, the user is returned multiple times, once for each dependent.
Example:
User1 = "Sam Smith"
-Dependent1.1 = "Ralph Smith"
-Dependent1.2 = "Alex Smith"
User2 = "April Shower"
-Dependent2.1 = "Zach Shower"
-Dependen2.2 = "Sally Smith"
User.by_fuzzy_text('w') => [User2]
(April Shower, Zach Shower)
User.by_fuzzy_text('z') => [User2]
(Zach Shower)
User.by_fuzzy_text('x') => [User1]
(Alex Smith)
User.by_fuzzy_text('smith') => [User1, User2]
(Sam Smith, Ralph Smith, Alex Smith, Sally Smith)
I needed to do a LEFT OUTER JOINS to include the Users who did not have any dependents.
joins('LEFT OUTER JOIN dependents ON users.id = dependents.user_id').where('name ILIKE ? OR name ILIKE ?', text, text).uniq`