ASC/DESC sorting in MS Access - vba

I have a table like this:
Name Country Grade
Lily Germany A
Anna USA C
Leon Slovenia -
Mia Italy
Carlos Mexico B
And have two buttons:
1. ASC button
2. DESC button
I am trying that the buttons sort the table in asc/desc order but the problem is that i have A,B,C letters but are actually grades. With my code:
Private Sub cboAufS_Click()
If Me.cboFilData = "KK" Then
Me.Text1163.SetFocus
DoCmd.RunCommand acCmdSortAscending
End If
End Sub
Private Sub cboAbS_Click()
If Me.cboFilData = "KK" Then
Me.Text1163.SetFocus
DoCmd.RunCommand acCmdSortDescending
End If
End Sub
This code gives me the wrong results, for ASC i get : " ", -, A, B, C and need " ", - C, B, A, and for DESC i get C, B, A, -, " " and i need A, B, C, -, " "
Can someone please help me?

Calculate a field in query to apply sorting on and use that query as form or report RecordSource.
Switch(Grade="A",5, Grade="B",4, Grade="C",3, Grade="-",2, True,1) AS SortOrd
In a form, use code in button events like:
Me.OrderBy = "SortOrd ASC"
or
Me.OrderBy = "SortOrd DESC"
In a simple report, similar code can be used to dynamically set sort order. Code for complex report that uses Sorting & Grouping utility in design will be more complicated.

You can use a query:
SELECT
Table1.Name,
Table1.Country,
Table1.Grade
FROM
Table1
ORDER BY
Abs(Nz([Grade]) = "") DESC,
Abs([Grade] = "-") DESC,
Table1.Grade DESC;
Replace DESC with ASC for the reverse sorting.

Related

MS Access Cascading Combo box against a single table, then store the resultant ID

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

Double sort in Excel or SQL

I have a weird table-sorting issue that I cannot figure out in Excel or SQL.
Here's a table sorted by column 2, "Letter".
Name
Letter
Dan
A
Moe
A
Ted
B
Dan
C
Joe
F
Noe
F
What I need is the table sorted by Letter BUT... if there are any duplicates in the "Name" column, I need those grouped--which would break the sorting in column 2. So below, even though the table is sorted by Letter, I want the two Dans together:
Name
Letter
Dan
A
Dan
C
Moe
A
Ted
B
Joe
F
Noe
F
Thanks for any help!
I would try something like this.
SELECT t.name, t.letter
FROM table t
JOIN (SELECT table.name, MIN(table.letter) AS min_letter_by_name
FROM table GROUP BY table.name)
ON t.name = table.name
ORDER BY min_letter_by_name, name, letter
You didn't really say where in the results you wanted the Dans but let's use a window function to count the number of same names and sort by that initially (descending, meaning the dans will sort nearer the top)
SELECT *
FROM t
ORDER BY COUNT(*) OVER(PARTITION BY name) DESC, letter
If you have eg two Fred and you want them to sort together and not mix up with the dans, consider
SELECT *
FROM t
ORDER BY CASE WHEN COUNT(*) OVER(PARTITION BY name) = 1 THEN NULL ELSE name END DESC, letter
This effectively puts unique names "in one basket" that is then sorted by letter and for "non unique names" they clump together by the name and then sort by the letter
This Power Query solution (available in Windows Excel 2010+ and Office 365) seems to do what you want on your database.
Please read the comments.
I group by Name and then sort by letter (as well as sort each subGroup by letter).
M Code
let
Source = Excel.CurrentWorkbook(){[Name="Table7"]}[Content],
#"Changed Type" = Table.TransformColumnTypes(Source,{{"Name", type text}, {"Letter", type text}}),
//Group by name
//Sort each subgroup and extract the initial letter
//Generate a Table with each subtable properly sorted
#"Grouped Rows" = Table.Group(#"Changed Type", {"Name"}, {
{"All", each _, type table [Name=nullable text, Letter=nullable text]},
{"SortSubGroup", each Table.Sort(_,"Letter")},
{"letter1", each List.Sort([Letter]){0}, type text}
}),
//Sort by initial letter and then by Name
//Remove the unneeded columns and expand the subTables
#"Sorted Rows" = Table.Sort(#"Grouped Rows",{{"letter1", Order.Ascending}, {"Name", Order.Ascending}}),
#"Removed Columns" = Table.RemoveColumns(#"Sorted Rows",{"Name", "All", "letter1"}),
#"Expanded SortSubGroup" = Table.ExpandTableColumn(#"Removed Columns", "SortSubGroup", {"Name", "Letter"}, {"Name", "Letter"})
in
#"Expanded SortSubGroup"
I think you just want two keys in your order by clause:
order by name, letter
EDIT:
If you want to sort by name based on the lowest letter, then you would use window functions:
order by min(letter) over (partition by name),
name, letter

Run query from FORM with variable

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

Grouping joined lookup values in Access [duplicate]

This question already has an answer here:
Combine values from related rows into a single concatenated string value
(1 answer)
Closed 8 years ago.
My database includes several lookup tables (shown as pulldown menus on the UI form).
For example,
customer_data - customer demographic info.
lookup_car - stores car descriptions (Pinto, Vega, Reliant Robin, Mustang, Corvette)
junction_car_customer - joins a customer with one or more cars
Customer Jeremy Clarkson (cust_id: 1) owns three cars. The dropdown for his record shows:
Pinto (car_id=100)
Reliant Robin (car_id=101)
Vega (car_id=102)
The junction_car_customer data looks like this:
cust_id car_id
1 100
1 101
1 102
I'm trying to return a row showing the customer name and the models owned (as a semi-colon delimited string).
Here's my query:
SELECT
cd.cust_id,
cd.name_first,
cd.name_last,
jcc.car_id,
lc.car_desc
FROM
((customer_data AS cd)
LEFT JOIN ju_cust_car AS jcc ON jcc.cust_id = cd.cust_id)
LEFT JOIN lookup_cars AS lc ON lc.car_id = jcc.car_id
ORDER BY
cd.name_last
This returns:
cust_id name_first name_last car_id car_desc
1 Jeremy Clarkson 100 Pinto
1 Jeremy Clarkson 101 Reliant Robin
1 Jeremy Clarkson 102 Vega
What I'd like is:
cust_id name_first name_last car_desc
1 Jeremy Clarkson Pinto;Reliant Robin;Vega
Is there an efficient way of returning the above result?
As HansUp says, you need to use a custom VBA function. If the data is fairly static, you can speed things up by caching the results. So...
1) In the VB editor, add a reference to the 'Microsoft Scripting Runtime' (we'll be needing the Dictionary class from this library).
2) Create a new standard module, and add code to it like the following:
Option Explicit
Private mCache As New Scripting.Dictionary
Sub ClearCarDescCache(Optional cust_id)
If IsMissing(cust_id) Then
mCache.RemoveAll
Else
mCache.Remove CInt(cust_id)
End If
End Sub
Function GetCarDescList(cust_id) As String
If mCache.Exists(cust_id) Then
GetCarDescList = mCache(cust_id)
Exit Function
End If
Dim RS As DAO.Recordset, S As String
Set RS = CurrentDb.OpenRecordset( _
" SELECT car_desc " + _
" FROM junction_car_customer INNER JOIN lookup_car " + _
" ON junction_car_customer.car_id = lookup_car.car_id " + _
" WHERE cust_id = " & cust_id & _
" ORDER BY car_desc", dbOpenForwardOnly)
While Not RS.EOF
If Len(S) = 0 Then
S = RS(0)
Else
S = S + ";" & RS(0)
End If
RS.MoveNext
Wend
mCache.Add cust_id, S
GetCarDescList = S
End Function
3) The main query can now look like this:
SELECT cust_id, name_first, name_last, GetCarDescList(cust_id) AS car_desc
FROM customer_data
ORDER BY name_last
4) Add explicit calls to ClearCarDescCache as appropriate.

Group By for Combobox Filter Sort

I've created a basic Combobox Filter Sort that sorts through Company Regions for my company (Acronyns mostly) we refer to these as AOR's or Area of Reference. After defining your AOR, it limits the next combo box to show only Countries in that specific AOR, hense the Filter Sort. But, my problem is - when it displays the countries after selecting an AOR - It displays ALL RECORDS in that specific country, instead of just 1 country listing.
Basically, It isn't grouping my countries - and when I select "Totals" which normally gives me only unique results, this doesnt work.
My question, How can I re-write this code to include a Group By?
My Code:
Private Sub cboRegion_AfterUpdate()
' Region -> Country
Dim sManagerSource As String
sManagerSource = "SELECT [FullEmail].[AORID], [FullEmail].[ID], [FullEmail].[Country] " & _
"FROM FullEmail " & _
"WHERE [AORID] = " & Me.cboRegion.Value
Me.cboCountry.RowSource = sManagerSource
Me.cboCountry.Requery
End Sub
My SQL statement looks like this (It's got Group By in it, but it doesn't GROUP)
SELECT FullEmail.AORID, FullEmail.ID, FullEmail.Country
FROM FullEmail
GROUP BY FullEmail.AORID, FullEmail.ID, FullEmail.Country
HAVING (((FullEmail.AORID)=1));
Thanks in advance for reading through!
The previous answer is correct, except with a having clause you need to use aggregates... so just use where.
SELECT Country
FROM FullEmail
WHERE AORID=1
GROUP BY Country;
That should fix you up. When you use HAVING it looks at what you've selected, since you didn't select AORID, it didn't know what to do.