Sometimes you find that you can't get just the information you want using just a Totals query. For example, in a table with Manufacturer, Month, Sales and Year, you may want to view the highest sales by month and year, but also see which manufacturer had the highest sales. A sub query can allow you to include summarised information from the same or another table, without having to create another query first.
Let's run through a sequence of four queries to illustrate.
The first query has the SQL shown in Listing 1, and returns 1128 rows. In the sample database it is qryAll.
SELECT Manufacturers.Manufacturer, UK_CarSales.Month, UK_CarSales.Sales, UK_CarSales.Year FROM UK_CarSales INNER JOIN Manufacturers ON UK_CarSales.ManufacturerID = Manufacturers.ManufacturerID;
The second query has just 3 fields and returns 24 rows. The SQL is shown in Listing 2.
SELECT UK_CarSales.Month, Max(UK_CarSales.Sales) AS Max_Sales, UK_CarSales.Year FROM UK_CarSales GROUP BY UK_CarSales.Month, UK_CarSales.Year;
So far, so good. However, when you add the fourth field to this query you end up with 1128 rows again.
SELECT Manufacturers.Manufacturer, UK_CarSales.Month, Max(UK_CarSales.Sales) AS Max_Sales, UK_CarSales.Year FROM UK_CarSales INNER JOIN Manufacturers ON UK_CarSales.ManufacturerID = Manufacturers.ManufacturerID GROUP BY Manufacturers.Manufacturer, UK_CarSales.Month, UK_CarSales.Year;
This is no good to us because the intention is to summarise the months, only listing the manufacturer with the highest sales for the month. One option is to use two queries: the first to generate the top manufacturer for the month, joined to a second query that completes the display. This doesn't always work -- sometimes the dynamics of the query change, and you find yourself trying to undo another unintended consequence. Fortunately, there is an option: the subquery.
A subquery (or a subselect) is a way to tie the result set from one query to another. It gives you the flexibility to add descriptive fields such as Manufacturer without affecting the number of rows. There are some variants of this technique, with different strengths and weaknesses. In the first option (Listing 5), the criteria for the Sales field is another query that selects the maximum sales, filtered by month and year. Because UK_Sales is used both in the main query and the criteria, you need to create aliases for the table to distinguish them, or you will get an error when you try to run the query. First, let's look at the final display and then we'll create the subquery in steps.
You can create the components of a subquery using the query design grid, but you need to use SQL to finish the job.
Create a new query and add UK_CarSales and Manufacturers to the query window. Join the tables on ManufacturerID.
Right-click UK_CarSales, select Properties, and change the Alias name to U. Close the Properties window. Repeat the process for Manufacturers, but alias the table to M.
Add the Manufacturer, Month, Sales and Year fields to the grid.
Create another new query and this time, add UK_CarSales to the query window twice.
Alias the first table as U and the second table as S, and close the Properties window.
Double-click Sales, Month and Year in table S to place the fields on the grid. Change the query to a Totals query by clicking the large ∑ on the toolbar, then change the summary function for Sales from Group By to Max. Change the summary functions for the other two fields to Where, and in the criteria row type [U].[Year] for the Year field, and [U].[Month] for the Month field.
View > SQL View, then select all of the text and copy it. Switch back to the first query, and go into SQL view as well. You should see:
SELECT M.Manufacturer, U.Month, U.Sales, U.Year FROM UK_CarSales AS U INNER JOIN Manufacturers AS M ON U.ManufacturerID = M.ManufacturerID;
Remove the semi colon, press Enter to create a new line and type
WHERE U.Sales)=(
Now paste the SQL that you copied from the second query. You will see this SQL:
SELECT M.Manufacturer, U.Month, U.Sales, U.Year FROM UK_CarSales AS U INNER JOIN Manufacturers AS M ON U.ManufacturerID = M.ManufacturerID WHERE U.Sales=(SELECT Max(Sales) AS MaxOfSales FROM UK_CarSales AS S, UK_CarSales AS U WHERE (([S].[Month] = [U].[Month])) AND (([S].[Year] = [U].[Year])));
This needs some cleaning up before it will work properly. The alias for Max(Sales) can be removed, and you need to delete the comma and UK_CarSales AS U in the second FROM statement (both sections are highlighted). The reason for this is that we want to filter the second query on the first, and removing the second alias makes that unambiguous.
Finally, remove some of the excess parentheses (Access gets a touch enthusiastic around WHERE clauses). You can also remove the square brackets around the table and field names, because there are no gaps in the names. The final result should look like this:
SELECT U.Manufacturer, U.Month, U.Sales, U.Year FROM UK_CarSales AS U WHERE U.Sales=(SELECT Max(Sales) FROM UK_CarSales AS S WHERE S.Month = U.Month AND S.Year = U.Year);
Run the query, and you should get the result displayed previously.
Often a standard query doesn't do the job. Either you need two or more queries in sequence, or you find that running sequential queries still gives unwanted results. When that happens, reach for a subquery. You will need to work with SQL to finish the job, but you will reduce the number of queries in your database and your reports will behave better.