Before I ask this question I must admit that I am new to SQL but here it goes:
I have 3 tables: Crown Facility, tblSubProjects, and lnkSubProjectFacility.
I use lnkSubProjectFacility to assign facilities to sub-projects and vice-verse. When the user selects a sub-project from a combo box (cboAddFacSubProject) in a form, the AfterUpdate event in the combo box causes the 2 listboxes to populate with the associated records in the lnkSubProjectFacility table records.
The LEFT listbox (lstAddSubProjectFacilities) displays all facilities associated with the sub-project and the RIGHT (lstAddSubProFac) displays all available remaining facilities that the user can assign, with a command button, and remove a available facility from the right listbox and moving it to the left (thus creating a new record in the lnkSubProjectFacility table).
Everything works fine except I cannot seem to get the right listbox to populate with the correct available facilities in the combo box's AfterUpdate event with the VBA code below:
Private Sub cboAddFacSubProject_AfterUpdate()
Dim strSQL As String, strSQL2 As String
'String SQL statment variable for the LEFT listbox - to display all ASSIGNED facilities
strSQL = "SELECT [CROWN Facility].FACILITY_ID, " & _
"[CROWN Facility].FACILITY_NAME, " & _
"lnkSubProjectFacility.SUBPROJECT_ID " & _
"FROM [CROWN Facility] INNER JOIN lnkSubProjectFacility " & _
"ON [CROWN Facility].FACILITY_ID = lnkSubProjectFacility.FACILITY_ID " & _
"WHERE lnkSubProjectFacility.SUBPROJECT_ID =" & Me.cboAddFacSubProject & " " & _
"ORDER BY [CROWN Facility].FACILITY_NAME"
'String SQL statment variable for the RIGHT listbox - to display all AVAILABLE facilities
strSQL2 = "SELECT [CROWN Facility].FACILITY_ID, " & _
"[CROWN Facility].FACILITY_NAME, " & _
"lnkSubProjectFacility.SUBPROJECT_ID " & _
"FROM [CROWN Facility] LEFT JOIN lnkSubProjectFacility " & _
"ON [CROWN Facility].FACILITY_ID = lnkSubProjectFacility.FACILITY_ID " & _
"WHERE lnkSubProjectFacility.SUBPROJECT_ID <>" & Me.cboAddFacSubProject & " " & _
"ORDER BY [CROWN Facility].FACILITY_NAME"
'RowSource for the LEFT listbox - to display all assigned facilities
lstAddSubProjectFacilities.RowSource = strSQL
'RowSource for the RIGHT listbox - to display all available facilities
lstAddSubProFac.RowSource = strSQL2
'This is just updating a label showing the count of items in the left listbox
lblListCt.Caption = lstAddSubProjectFacilities.ListCount & " Facilities Selected"
End Sub
After executing this code, everything is fine in the left listbox, with all assigned records for that sub-project showing correctly. However, despite the right listbox excluding all facilities shown in the left, it also excludes ANY facilities that were assigned to any other sub-projects in that table.
In addition for searching for hours for this question, I have experimented with the strSQL2 variable by trying to add IS NULL at the end (which of course does not work) and many other things such as changing the JOIN types. Most recently, I changed WHERE to AND which returns nothing.
I am sure there is a fairly simple solution to this but I would be very appreciative of any assistance to get me there!
Note: I am using Access 2010 but I do not think that makes any difference.
EDIT: Here is the structure for the lnkSubProjectFacility table:
SUBPROJECT_ID FACILITY_ID
7 20000003
7 20000025
7 20000027
8 20010302
8 20021781
9 20040035
9 20044392
10 20045465
17 10000282
17 10000452
17 10000844
21 20000005
21 20000019
21 20000026
CROWN Facility table structure:
FACILITY_ID FACILITY_NAME
20000003 Barnes
20000025 Bio-Medical Applications
20000027 Barnes Center
20010302 Atlantic
20021781 Anthonys Hospital
20040035 Black Hawk
20044392 Ames
20045465 Arnold
10000282 BETHANY
10000452 ANDOVER
10000844 Ankeny
20000005 Columbia
20000019 Baptist
20000026 Childrens Hospital
tblSubProjects table structure:
SUBPROJECT_ID SUBPROJECT
7 Service Project1
7 Service Project1
7 Service Project1
8 Service Project2
8 Service Project2
9 Service Project3
9 Service Project3
10 Service Project4
17 CatheterReduction1
17 CatheterReduction1
17 CatheterReduction1
21 Patient Access3
21 Patient Access3
21 Patient Access3
I am going to assume that lnkSubProjectFacility is meant to relate Facility to tblSubProjects. lnkSubProjectFacility seems to be the only table I would change as far as schema
lnkSubProjectFacility
SPF_Id, SubProject_Id, Facility_Id
1 7 20000003
2 7 20000025
3 7 20000027
4 8 20010302
. . .
. . .
. . .
try to normalize the data as much as possible that way it is easier to write queries.
if you are trying to write a query that gets all facilities that have not been assigned a subproject:
select [CROWN FACILITY].FACILITY_ID, [CROWN FACILITY].FACILITY_NAME
from [CROWN FACILITY] as CF
where not exists (select *
from lnkSubProjectFacility as lSPF
where CF.FACILITY_ID == lSPF.FACILITY_ID)
i believe that query should work, hard to tell when I cannot test it.
sometimes a join is not the answer. a join can look a lot nicer, but sometimes a subquery is better
Related
Using MS Access, I have a table called "Locations" and a table called "Routes". I want to create a form that will basically present the routes table to the user (i.e select "from" and select "to") and will filter the "to" locations based on what the user has selected for the "from" location, i.e. a cascading combo box. The corresponding "Route.ID" will be stored in a resultant table on completion of the form.
Location has the following columns -
ID
Name
Notes
1
London
Great Britain
2
Manchester
Great Britain
3
Alabama
USA
Routes has the following columns -
ID
From_LID
To_LID
KM
Notes
1
1
2
450
Using the M1 route
2
2
1
450
Using the M1 route
3
1
2
485
Using the inside routes
4
2
1
485
Using the inside routes
5
1
3
5450
Too far to consider
6
3
1
5450
Too far to consider
I want to create a form with a cascading combo box - "ComboFrom", "ComboTo". Where "ComboFrom" will search the table for a list of distinct IDs, and using a JOIN with the Locations table, I can display the location names. "ComboTo" is dependant on the ID value in "ComboFrom" and will therefore only return a list of locations where it matches the From_LocationID of the routes table.
I have the first part more or less done. ComboFrom has the below query:
SELECT DISTINCT Location.Location_ID, Location.[Location Name], Location.Description
FROM Location INNER JOIN Route ON Location.Location_ID = Route.From_LID
ORDER BY Location.[Location Name];
ComboTo has the below query:
SELECT Location.Location_ID, Location.[Location Name], Location.Description, Route.From_LID
FROM Location INNER JOIN Route ON Location.Location_ID = Route.To_LID
WHERE (((Route.From_LID)=[Forms]![fmrRoute1]![From_LID]))
ORDER BY Location.[Location Name];
The piece of code in the "Where" clause in the ComboTo field basically gets the input of the ID from ComboFrom and outputs the correct IDs from the corresponding To_LID list. I then add vba against the update events for ComboFrom to requery ComboTo field.
What I am having trouble figuring out is how I can get the corresponding route ID to display correctly. i.e if I choose "1" for ComboFrom, and "3" for ComboTo, the Route ID should show "5" and not "7" (where "7" is a new record in the Routes table). Do I have to have a separate query to search for ComboFrom and ComboTo and return the resultant Routes ID? If so, how do I do this and attach it to the field in the form that will automatically update every time the ComboFrom and ComboTo fields change?
Is there a better way to do what I am trying to do?
Thanks in advance for any assistance!
This looks like cascading combo boxes into filtering a form from those two unbound combo boxes.
You can google those two ideas.
The self join made this tricky. The key was ignoring the relationship between Location.ID and From_LID. That relationship is in the database but ignored (deleted) in the query.
this gives us the sql we will need for the second combo box record source.
SELECT Routes.From_LID, Routes.To_LID, First(Locations.LocationName) AS FirstOfLocationName
FROM Locations INNER JOIN Routes ON Locations.ID = Routes.To_LID
GROUP BY Routes.From_LID, Routes.To_LID
HAVING (((Routes.From_LID)=2))
ORDER BY First(Locations.LocationName)
'just need to replace the 2
Private Sub cmbFrom_AfterUpdate()
'cascade combobox
Dim strRowSource As String
strRowSource = "SELECT Routes.From_LID, Routes.To_LID, First(Locations.LocationName) AS FirstOfLocationName" & _
" FROM Locations INNER JOIN Routes ON Locations.ID = Routes.To_LID " & _
"GROUP BY Routes.From_LID, Routes.To_LID " & _
"HAVING (((Routes.From_LID) = " & Me.cmbFrom & "))" & _
"ORDER BY First(Locations.LocationName)"
Debug.Print strRowSource
Me.cmbTo.RowSource = strRowSource
Me.cmbTo.Visible = True
'a look and feel choice
End Sub
[![enter image description here][2]][2]
Private Sub cmbTo_AfterUpdate()
'filter form 'most of the time you want to create a search form where you filter to the records you want
Me.Filter = "From_LID = " & Me.cmbFrom & " AND To_LID = " & Me.cmbTo
Me.FilterOn = True
Me.Detail.Visible = True
End Sub
I have a table tblCosts which i display on an msaccess front end which enables users to add new entries as well as update existing ones. The table is structured as below.
ExpenseType Month Year Cost
Hardware June 2017 $500
Software July 2017 $300
Hardware Sept 2017 $150
I have an update and insert queries which work fine when run manually.
However I am having trouble differentiating the condition when to fire the query on the form. For example, if the record exists in the table, it should run the update query, if record does not exist, it should run the insert query.
For example if someone puts in
- Hardware Sept 2017 $120 it should update the 3rd entry from 150 to 120 but if someone puts in
- Furniture Sept 2017 $350 it should recognize that Furniture is not part of the DB and run the insert query. I have the update and insert queries but need help in identifying the condition when to run them.
The Update query I'm using is:
Update tblCosts
set tblCosts.Cost=[Forms]![frmCost]![txtCost]
where tblCosts.ExpenseType = [Forms]![frmCost]![txtExpType]
and tblCosts.Month = [Forms]![frmCost]![txtMonth]
and tblCosts.Year = [Forms]![frmCost]![txtYear]
The Insert query I'm using is:
Insert into tblCosts (ExpenseType , Month, Year, Cost)
Select [Forms]![frmCost]![txtExpType] as Exp1,
[Forms]![frmCost]![txtMonth] as Exp2,
[Forms]![frmCost]![txtYear] as Exp 3,
[Forms]![frmCost]![txtCost] as Exp 4
Need code (VBA or macro) behind a form that determines which action query to run. In VBA something like:
If DCount("*", "tablename", "ExpenseType='" & Me.cbxExpense & "' AND [Month]='" & Me.tbxMonth & "' AND [Year]=" & Me.tbxYear) = 0 Then
CurrentDb.Execute "INSERT INTO tablename (Expense, [Month], [Year], Cost) VALUES ('" & Me.cbxExpense & "', '" & Me.tbxMonth & "', " & Me.tbxYear & ", " & Me.tbxCost & ")"
Else
CurrentDb.Execute "UPDATE tablename SET Cost=" & Me.tbxCost & " WHERE Expense='" & Me.cbxExpense & "' AND [Month]='" & Me.tbxMonth & ", [Year]=" & Me.tbxYear
End If
Probably also want some validation code to make sure all four controls have data before executing queries.
The real trick is figuring out what event to put code into - the Cost AfterUpdate will work as long as the other fields have data entered first, otherwise the validation will fail and user will have to re-enter cost.
Could have code that doesn't make each control available until previous value is entered.
Month and Year are reserved words and should not use reserved words as names for anything.
Would be better to save month numbers instead of month names for sorting purposes.
Why updating a value which really should be a calculated aggregation of transaction records?
Task:
Append/edit the currently working code below to return only one row per patient, the maximum value of d1_10.xtransfer (datatype int) with the restriction that this row's d1_10.dstartdate <= glob_End_Date.
Caveats:
There are similar questions on StackOverflow and its sister sites. None that I have found have successfully helped with a resolution to this issue.
This is a medical EHR database, I can share code, but any discussion of results has to be general and exclude patient information.
I am replacing the SQL query within a pre-existing Excel spreadsheet to do something different. Excel pulls information from our database with an ODBC connection. Our database is using Ingres SQL which accepts most, but not all, of your typical SQL code varieties. It's possible that a piece of code will generally work in other flavors of SQL but not with the combo of Ingres and Excel. I've got the spreadsheet working and returning results, now it's about making some fixes by writing SQL code that works in this software.
Thus far:
With the currently working code below (no maximum d1_10.xtransfer restrictions) we return all rows with d1_10.dstartdate in the user selected date range and with the user selected d1_10.xinstitute. We want just the latest one. That is, the patient's row with either the maximum d1_10.dstartdate within the date range, or the maximum d1_10.xtransfer (index that counts up as they are added) within the date range.
Currently working code:
"SELECT " & _
"d1.xpid ""XPID"", " & _
"d0_v1.name_family ""NAME_FAMILY"", " & _
"d0_v1.name_given1 ""NAME_GIVEN1"", " & _
"d0_v1.name_given2 ""NAME_GIVEN2"", " & _
"d1.sex ""SEX"", " & _
"d1.birthdate ""DOB"", " & _
"d0_v1.hsp_pid, " & _
"c58.brief_name, " & _
"c73.cname, " & _
"date_trunc('day',d1_10.dstartdate) ""DSTARTDATE"", " & _
"date_trunc('day',d1_17.ddeath) ""DDEATH"" " & _
"FROM d1 " & _
"JOIN d0_v1 ON d1.xpid = d0_v1.xpid " & _
"JOIN d1_2 ON d1.xpid = d1_2.xpid " & _
"JOIN c58 ON d1_2.xmodality = c58.xcmodality " & _
"JOIN d1_10 ON d1.xpid = d1_10.xpid " & _
"JOIN c73 ON d1_10.xinstitute = c73.xcsite " & _
"JOIN d1_17 ON d1.xpid = d1_17.xpid " & _
"WHERE " & _
"d1_10.xinstitute = " & institute_index & " AND " & _
"d1_10.dstartdate >= '" & glob_Start_Date & " 00:00:00' and " & _
"d1_10.dstartdate <= '" & glob_End_Date & " 23:59:59' "
The closest I have gotten with code that runs from the excel spreadsheet is with this additional line in the WHERE clause:
d1_10.xtransfer = (SELECT MAX(d1_10.xtransfer) FROM d1_10 GROUP BY xpid)
With this additional line we now return only one row from each patient that has a d1_10.xtransfer within the date range. But if they have a row where d1_10.xtransfer is more recent than the date range, then they don't show up in the results at all.
With this line the code is taking MAX(d1_10.xtransfer) for each xpid before it applies the date restriction. By my logic we want it to do so after instead, but I have been unable to come up with code that runs that gets it any closer than this.
Thanks in advance. I'll keep this question updated with additional info below this page break.
Additional Info:
Per PaulM:
Yes, xpid is a patient ID index number, unique to each patient.
Added/edited line in WHERE clause to: "d1_10.xtransfer = (SELECT MAX(xtransfer) FROM d1_10 d1_10_b WHERE d1_10.xpid = d1_10_b.xpid AND d1_10_b.dstartdate <= '" & glob_End_Date & " 23:59:59') "
Patient Bob has transfers on both the 14th and 17th of June that fit the rest of the criteria.
When inputting a date range with an end date of Jun 17+, the spreadsheet correctly returns a row for Bob with his Jun 17 transfer.
When inputting a date range with an end date of Jun 14,15 or 16, the spreadsheet incorrectly does not return a row for Bob.
It seems as though it still takes the maximum xtransfer before restricting by date.
Per PaulM's comment:
I ran the subselect for a specific patient as follows:
Input:
SELECT MAX(xtransfer) FROM d1_10 d1_10_b WHERE d1_10_b.xpid = '2258' AND d1_10_b.dstartdate <= '20-apr-2016 23:59:59'
It outputted a value of MAX(xtransfer) = '48233'. This is correct.
So, when run in Visual SQL as its own statement, setting d1_10_b.xpid equal to a specific patient, it correctly pulls the maximum xtransfer from the date range. (There was a more recent xtransfer outside of the date range, and it still correctly displayed the maximum xtransfer from within the date range.)
I then tried running this exact same subselect in the where close for the spreadsheet. That is, I manually selected the same date range (which is being passed through as a variable correctly and successfully) but I subbed out d1_10.xpid = d1_10_b.xpid for d1_10_b.xpid = '2258'. This did not work. The spreadsheet did not show a row for this patient, seemingly because it still applies the MAX() function before it restricts by the date range in the subquery. And yet, the subquery works when run by itself.
Much appreciation for any further suggestions.
You need to add the date restriction in the subselect as well as the main query. Also I suspect the group by is wrong. By adding a group by you're making the subselect a list of patients xtransfer values with the largest value for each xpid (identifies a patient?). However that means if the row you're interested in from the main query happens to have an xtransfer value that matches the largest one belonging to a different xpid you're getting a false match.
What you really need is to add a join on xpid from the subselect back up to the main query. To do that you'll need a different correlation name e.g.
d1_10.xtransfer = (SELECT MAX(xtransfer)
FROM d1_10 d1_10_b
WHERE d1_10.xpid = d1_10_b.xpid
AND d1_10_b.dstartdate > = ... {as above} )
I need to calculate weighted price index between 2 retailers (weighted on retailer 1 turnover). I need to calculate this index using different Item compositions (different top n).
Table1 contains data on area-week-item level.
The basic approach of weighting is:
SUM(price1/price2 * turnover)/SUM(turnover)
I calculate it with the following Query statement:
SELECT week, SUM(price1/price2 * turnover)/SUM(turnover) AS [PriceIndex]
FROM Table1
GROUP BY week;
According to the business needs I usually need to calculate this index with different set of products - only private label, only vegetables or - the most simple example - for only TOP n products (based on turnover in Q1'16).
I want to make a simple form in Access using VBA with several parameters for my query.
Form example
I have Dictionary tables with item-code descriptions (to select only beer category) and TOP 500 ranking table. I've made a query, that works - it JOINS top 500 table (it consists just code column with needed codes. I can manually write number of TOP codes (hilighted the string with ***).
SELECT week, SUM(price1/price2 * turnover)/SUM(turnover) AS [PriceIndex]
FROM Table1 AS F INNER JOIN TopTable AS T ON F.code=T.code
WHERE F.code IN (
***SELECT TOP 500 code
FROM TopTable)
GROUP BY week;
Now the main question. How to make the illustrated form?
I've made the beginning:
Private Sub Command_Click()
Dim top_num As Long
Dim SQL As String
top_num = Top_number.Value
sSQL = _
"SELECT week, SUM(price1/price2 * turnover)/SUM(turnover) AS [PriceIndex]" & _
"FROM Table1 AS F INNER JOIN TopTable AS T ON F.code=T.code" & _
"WHERE F.code IN ( " & _
"***SELECT TOP top_num code" & _
"FROM TopTable)" & _
"GROUP BY week;"
' ????
End Sub
I need just to execute SQL statement with form variable top_num.
So if you want to display the results of you query in your form, you have to do it in a subform.
create the Subform
create a new form, name it Subform1 or anything else you like. click on the square which is top left of the form and bring the property window. Under "Format", "default display", select "Datasheet".
Your query returns 2 columns, so you need to add 2 textboxes on your form
You have to bound your textboxes to the column names returned by your query: select a textbox and bring the properties window, under "data" tab the first field is "control source", there you specify your query column names : week for one textbox, PriceIndex for the other
Your Subform is ready, save and close it.
Add the subform to the form
Open the main form in design mode and add a SubForm control to it. When you create the control the wizard ask you which will be the data source for it, select SubForm1
Adapt the code
Private Sub Command_Click()
Dim top_num As Long
Dim SQL As String
top_num = Top_number.Value
sSQL = _
"SELECT week, SUM(price1/price2 * turnover)/SUM(turnover) AS [PriceIndex]" & _
"FROM Table1 AS F INNER JOIN TopTable AS T ON F.code=T.code" & _
"WHERE F.code IN ( " & _
"***SELECT TOP " & top_num & " code" & _
"FROM TopTable)" & _
"GROUP BY week;"
' Add This :
Me!subform1.Form.RecordSource = sSQL
End Sub
You're done
Assume the following table:
ID COMPANY SUBSIDIARY NR_LIVES INSURANCE_LINE FACTOR_CALC
1 COMPANY_X SUB_1 860 LIFE YES
2 COMPANY_X SUB_1 860 DISABILITY YES
3 COMPANY_X SUB_1 860 MEDICAL YES
4 COMPANY_X SUB_2 46 LIFE YES
5 COMPANY_X SUB_2 689 MEDICAL YES
6 COMPANY_X SUB_3 852 LIFE YES
I need an SQL string that returns to me the value 2401.
This is done by making the sum of the highest NR_Of_Lives per subsidiary where FACTOR_CALC = Yes.
I probably would know how to do it loading everything in a recordset and then using VBA, but I would appreciate it if it were possible in one SQL command.
UPDATE:
The current query:
sSQL_Select = "SELECT SUM(NR_LIVES) FROM (SELECT SUBSIDIARY, MAX(NR_LIVES) FROM T_WILMA WHERE PARENT=" & lParent & " AND ACC_YEAR=" & lAcc_Year & _
" AND FACTOR_CALCULATION=TRUE GROUP BY SUBSIDIARY);"
throws an error: Too few parameters, expected 1.
The subquery on its own works as expected.
Thanks to replies so far, but I haven't succeeded to make it work so far.
You can determine the maximum per subsidiary in a subquery. The outer query can then sum the maximums.
select sum(MaxLives)
from (
select company
, subsidiary
, max(nr_lives) as MaxLives
from YourTable
where factor_calc = 'yes'
group by
company
, subsidiary
) as SubQueryAlias
I'll suggest you include some aliasing to see whether that helps unconfuse the db engine.
sSQL_Select = "SELECT SUM(sub.MaxOfNR_LIVES) AS NR_LIVES" & vbcrlf & _
"FROM (" & vbCrLf & _
"SELECT SUBSIDIARY, MAX(NR_LIVES) AS MaxOfNR_LIVES" & vbCrLf & _
"FROM T_WILMA WHERE PARENT=" & lParent & _
" AND ACC_YEAR=" & lAcc_Year & _
" AND FACTOR_CALCULATION=TRUE GROUP BY SUBSIDIARY) AS sub;"
Debug.Print sSQL_Select
SELECT SUM(NR_LIVES)
from(
SELECT SUBSIDIARY,MAX(NR_LIVES) as NR_LIVES
from <Table>
where FACTOR_CALC='YES'
group by SUBSIDIARY)a
You need to let the system know which NR_LIVES it's trying to add up. On your table (and taking out the extra stuff in the WHERE that is not in your example, this returns 2401
SELECT Sum(MAXNR_LIVES) AS Expr1
FROM (SELECT SUBSIDIARY, MAX(NR_LIVES) AS MAXNR_LIVES FROM T_WILMA
WHERE FACTOR_CALC=TRUE GROUP BY SUBSIDIARY);