Calculate Average with SQL as the source of the subform

S

scratchtrax

I'm trying to calculate the average price for products and display the
average on the subform.
strSELECT = "SELECT tblITEMS.ITEMNAMEDESCRIPTION, tblITEMS.UNITPRICE, _
(SELECT AVG([UNITPRICE])FROM tblITEMS) FROM tblITEMS "

strOrderBy = "ORDER BY tblITEMS.ITEMNAMEDESCRIPTION;"

strFullSQL = strSELECT & strWHERE & strGroup & strOrderBy

[subfrmAVGTBL].[Form].[RecordSource] = "SELECT * FROM tblITEMS WHERE FALSE"

Me.subfrmAVGTBL.Form.RecordSource = strFullSQL

Using the above code in a cmd button to get the averages and display them in
a subform, doesn't work (obviously). But it does populate the subform with
the items and their prices. Am I close here, or do I have a long ways to go?
 
K

Ken Sheridan

The query in itself will return the a row for each item including the column
returned by the subquery giving the average of all unit prices. You just
need to bind a control to the computed column in the subform and format the
control as currency. It would help if you name the column, e.g.

SELECT AVG(UNITPRICE)FROM tblITEMS) AS AveragePrice

As no variables are involved here, however, why not simply set the
subform's RecordSource to the query in its properties sheet:

SELECT ITEMNAMEDESCRIPTION, UNITPRICE,
(SELECT AVG(UNITPRICE)
FROM tblITEMS)
AS AveragePrice
FROM tblITEMS
ORDER BY ITEMNAMEDESCRIPTION;

You don't say whether the subform is linked to its parent form, restricting
its rows to those related to the parent form's current record, though the
query suggests not. If it were then you'd need to restrict the subquery
also by correlating it with outer query. Say, for instance, you wanted to
show the products within a category selected in the parent form to which the
subform is linked on CategoryID:

SELECT ITEMNAMEDESCRIPTION, UNITPRICE,
(SELECT AVG(UNITPRICE)
FROM tblITEMS AS TI2
WHERE TI2.CategoryID = TI1.CategoryID)
AS AveragePrice
FROM tblITEMS AS TI1
ORDER BY ITEMNAMEDESCRIPTION;

Ken Sheridan
Stafford, England
 
K

Ken Sheridan

In the hypothetical example in my last post in the event of the subform being
linked to the parent form on CategoryID the CategoryID column would also have
to be included in the outer query's SELECT clause of course to enable the
subform to be linked to its parent:

SELECT ITEMNAMEDESCRIPTION, UNITPRICE, CategoryID
(SELECT AVG(UNITPRICE)
FROM tblITEMS)
AS AveragePrice
FROM tblITEMS
ORDER BY ITEMNAMEDESCRIPTION;

Ken Sheridan
Stafford, England
 
S

scratchtrax

Thank you for the response. Yes, I am attempting to construct the SQL
statement
that will define the record source of the subform. What you gave me helped
and I thank you. Is the "As AveragePrice" in the statement below refering to
a table column or an unbound text box?

Its the SQL syntax that I don't really get. I was previously able to use
SQL as I am attempting to here with the help of members of this community.
Its a powerful and dynamic way to construct the select statement in a form
with users input. I am rarely able to replicate this as I don't have the SQL
syntax down very well at all. Sometimes I'm able to make a query with the
tools and then switch to SQL view and then copy and paste. However, I'm not
sure how to do that and get statistics.

So, with your input I thought of a different way where I'm going to try to
use an orchestra of queries. Thanks again.
 
K

Ken Sheridan

AveragePrice is the name of a column in the query's 'result table' and will
show the values returned by the subquery. If the SQL statement is used as
the RecordSource of the subform AveragePrice will be a field in the form's
underlying recordset and can be used as the ControlSource of a text box.

Ken Sheridan
Stafford, England
 
S

scratchtrax

Thanks again Ken.
After I set the subform to continuous and added the the appropriate text box
controls to the subform, I used the following code in a button.
strSELECT = "SELECT DISTINCTROW tblITEMS.ITEMNAMEDESCRIPTION, " & _
"Avg(tblITEMS.UNITPRICE) AS [Avg Of UNITPRICE], Min(tblITEMS.UNITPRICE) " & _
"AS [Min Of UNITPRICE], Max(tblITEMS.UNITPRICE) AS [Max Of UNITPRICE], " & _
"Count(*) AS [Count Of tblITEMS] FROM tblPROJECTS INNER JOIN tblITEMS " & _
"ON tblPROJECTS.PROJECTID = tblITEMS.PROJECTID "
strWHERE = "WHERE ((([tblPROJECTS]![BIDDATE]) >= DateAdd(" & "'m'" & ", -13,
Now()))) GROUP BY tblITEMS.ITEMNAMEDESCRIPTION;"

strFullSQL = strSELECT & strWHERE
[subfrmAVGTBL].[Form].[RecordSource] = "SELECT * FROM tblITEMS WHERE FALSE"
Me.subfrmAVGTBL.Form.RecordSource = strFullSQL

Although I didn't have to do it this way to set the source of the subform, I
haven't used this in a while. It was Tom Wickerath that first introduced me
to using SQL to set the source of a subform but I haven't done this while
performing calculations. This seems to work ok, now I'm trying to add to
this, the average of the lowest priced item per project. This is
troublesome.
Thanks for your patience. Thanks for your input.
 
K

Ken Sheridan

To get the average of the lowest prices per project you'll need to use a
subquery within a subquery, the inner subquery identifying the MIN Unitprice
per project and the outer query returning the AVG of these values. The inner
subquery would be correlated with the outermost query on ProjectID, so the
query would look like this:

SELECT
itemnamedescription,
AVG(unitprice) AS [avg of unitprice],
MIN(unitprice) AS [min of unitprice],
MAX(unitprice) AS [max of unitprice],
COUNT(*) AS [count of tblItems],
(SELECT AVG(lowestprice)
FROM (SELECT MIN(unitprice) AS lowestprice
FROM tblItems AS TI2
GROUP BY projectid
WHERE TI2.projectid TI1.projectid))
AS [avg of lowest prices]
FROM tblProjects INNER JOIN tblItems AS TI1
ON tblProjects.projectid = TI1.projectid
WHERE [biddate] >= DATEADD("m", -13, DATE())
GROUP BY itemnamedescription;

Note that I've followed the convention of using lower case for column names,
proper case (preceded by the 'tbl' tag in your case) for table names and
upper case for SQL keywords and function names. This makes no difference to
the operation of the query of course, but I do find this convention makes SQL
more readable than when upper case is used for table and column names also.

Another thing to note is the use of the DATE function rather than the NOW
function. The latter includes the current time of day in its return value,
so any biddate on current date would be excluded if its time of day is eelier
than the current time of day, including those where a date value only has
been entered, as these have a time of day of zero, i.e. midnight at the start
of the day.

I've not been able to test this of course (but I think I've got the logic
right) so I'd suggest you first paste the above into a new query in SQL view
and open it to see if it gives what you are looking for. If so you then have
four options as regards your subform:

1. Save the query and enter its name as the subform's RecordSource property
in its properties sheet.

2. Enter the SQL of the (unsaved) query as the subform's RecordSource
property in its properties sheet.

3. If there's a real reason why you have to set the subform's RecordSource
property via a button, assign the name of the (saved) query to the property
in the button's Click event procedure.

4. Assign the SQL to the subform's RecordSource property with code as you
are currently doing.

Ken Sheridan
Stafford, England

scratchtrax said:
Thanks again Ken.
After I set the subform to continuous and added the the appropriate text box
controls to the subform, I used the following code in a button.
strSELECT = "SELECT DISTINCTROW tblITEMS.ITEMNAMEDESCRIPTION, " & _
"Avg(tblITEMS.UNITPRICE) AS [Avg Of UNITPRICE], Min(tblITEMS.UNITPRICE) " & _
"AS [Min Of UNITPRICE], Max(tblITEMS.UNITPRICE) AS [Max Of UNITPRICE], " & _
"Count(*) AS [Count Of tblITEMS] FROM tblPROJECTS INNER JOIN tblITEMS " & _
"ON tblPROJECTS.PROJECTID = tblITEMS.PROJECTID "
strWHERE = "WHERE ((([tblPROJECTS]![BIDDATE]) >= DateAdd(" & "'m'" & ", -13,
Now()))) GROUP BY tblITEMS.ITEMNAMEDESCRIPTION;"

strFullSQL = strSELECT & strWHERE
[subfrmAVGTBL].[Form].[RecordSource] = "SELECT * FROM tblITEMS WHERE FALSE"
Me.subfrmAVGTBL.Form.RecordSource = strFullSQL

Although I didn't have to do it this way to set the source of the subform, I
haven't used this in a while. It was Tom Wickerath that first introduced me
to using SQL to set the source of a subform but I haven't done this while
performing calculations. This seems to work ok, now I'm trying to add to
this, the average of the lowest priced item per project. This is
troublesome.
Thanks for your patience. Thanks for your input.
--
http://njgin.aclink.org


Ken Sheridan said:
AveragePrice is the name of a column in the query's 'result table' and will
show the values returned by the subquery. If the SQL statement is used as
the RecordSource of the subform AveragePrice will be a field in the form's
underlying recordset and can be used as the ControlSource of a text box.

Ken Sheridan
Stafford, England
 
S

scratchtrax

Wow Ken!

Thank you so much for your continued response.

I tried to load it into a button and execute but I got an error. I will
review this more closely, but I wanted to acknowledge and thank you for your
input as soon as possible. Thank you.

I get an error that reads false and then in another pop-it it reads,
"...Jet...could not find the query or table named 'False'. Check name and
spelling and try again."
When I look at the code I see "SELECT AVG(lowestprice)" Where/how does
lowestprice exist? Not sure what it means but I do not have a query or table
named 'lowestprice' so I wonder if that's not it...? Maybe I could should
replace this with 'tblITEMS' ? Not sure. I'll try a couple of things in
the meantime. Thanks again for the input.
--
http://njgin.aclink.org


Ken Sheridan said:
To get the average of the lowest prices per project you'll need to use a
subquery within a subquery, the inner subquery identifying the MIN Unitprice
per project and the outer query returning the AVG of these values. The inner
subquery would be correlated with the outermost query on ProjectID, so the
query would look like this:

SELECT
itemnamedescription,
AVG(unitprice) AS [avg of unitprice],
MIN(unitprice) AS [min of unitprice],
MAX(unitprice) AS [max of unitprice],
COUNT(*) AS [count of tblItems],
(SELECT AVG(lowestprice)
FROM (SELECT MIN(unitprice) AS lowestprice
FROM tblItems AS TI2
GROUP BY projectid
WHERE TI2.projectid TI1.projectid))
AS [avg of lowest prices]
FROM tblProjects INNER JOIN tblItems AS TI1
ON tblProjects.projectid = TI1.projectid
WHERE [biddate] >= DATEADD("m", -13, DATE())
GROUP BY itemnamedescription;

Note that I've followed the convention of using lower case for column names,
proper case (preceded by the 'tbl' tag in your case) for table names and
upper case for SQL keywords and function names. This makes no difference to
the operation of the query of course, but I do find this convention makes SQL
more readable than when upper case is used for table and column names also.

Another thing to note is the use of the DATE function rather than the NOW
function. The latter includes the current time of day in its return value,
so any biddate on current date would be excluded if its time of day is eelier
than the current time of day, including those where a date value only has
been entered, as these have a time of day of zero, i.e. midnight at the start
of the day.

I've not been able to test this of course (but I think I've got the logic
right) so I'd suggest you first paste the above into a new query in SQL view
and open it to see if it gives what you are looking for. If so you then have
four options as regards your subform:

1. Save the query and enter its name as the subform's RecordSource property
in its properties sheet.

2. Enter the SQL of the (unsaved) query as the subform's RecordSource
property in its properties sheet.

3. If there's a real reason why you have to set the subform's RecordSource
property via a button, assign the name of the (saved) query to the property
in the button's Click event procedure.

4. Assign the SQL to the subform's RecordSource property with code as you
are currently doing.

Ken Sheridan
Stafford, England

scratchtrax said:
Thanks again Ken.
After I set the subform to continuous and added the the appropriate text box
controls to the subform, I used the following code in a button.
strSELECT = "SELECT DISTINCTROW tblITEMS.ITEMNAMEDESCRIPTION, " & _
"Avg(tblITEMS.UNITPRICE) AS [Avg Of UNITPRICE], Min(tblITEMS.UNITPRICE) " & _
"AS [Min Of UNITPRICE], Max(tblITEMS.UNITPRICE) AS [Max Of UNITPRICE], " & _
"Count(*) AS [Count Of tblITEMS] FROM tblPROJECTS INNER JOIN tblITEMS " & _
"ON tblPROJECTS.PROJECTID = tblITEMS.PROJECTID "
strWHERE = "WHERE ((([tblPROJECTS]![BIDDATE]) >= DateAdd(" & "'m'" & ", -13,
Now()))) GROUP BY tblITEMS.ITEMNAMEDESCRIPTION;"

strFullSQL = strSELECT & strWHERE
[subfrmAVGTBL].[Form].[RecordSource] = "SELECT * FROM tblITEMS WHERE FALSE"
Me.subfrmAVGTBL.Form.RecordSource = strFullSQL

Although I didn't have to do it this way to set the source of the subform, I
haven't used this in a while. It was Tom Wickerath that first introduced me
to using SQL to set the source of a subform but I haven't done this while
performing calculations. This seems to work ok, now I'm trying to add to
this, the average of the lowest priced item per project. This is
troublesome.
Thanks for your patience. Thanks for your input.
--
http://njgin.aclink.org


Ken Sheridan said:
AveragePrice is the name of a column in the query's 'result table' and will
show the values returned by the subquery. If the SQL statement is used as
the RecordSource of the subform AveragePrice will be a field in the form's
underlying recordset and can be used as the ControlSource of a text box.

Ken Sheridan
Stafford, England

:

Thank you for the response. Yes, I am attempting to construct the SQL
statement
that will define the record source of the subform. What you gave me helped
and I thank you. Is the "As AveragePrice" in the statement below refering to
a table column or an unbound text box?

Its the SQL syntax that I don't really get. I was previously able to use
SQL as I am attempting to here with the help of members of this community.
Its a powerful and dynamic way to construct the select statement in a form
with users input. I am rarely able to replicate this as I don't have the SQL
syntax down very well at all. Sometimes I'm able to make a query with the
tools and then switch to SQL view and then copy and paste. However, I'm not
sure how to do that and get statistics.

So, with your input I thought of a different way where I'm going to try to
use an orchestra of queries. Thanks again.
--
http://njgin.aclink.org


:

In the hypothetical example in my last post in the event of the subform being
linked to the parent form on CategoryID the CategoryID column would also have
to be included in the outer query's SELECT clause of course to enable the
subform to be linked to its parent:

SELECT ITEMNAMEDESCRIPTION, UNITPRICE, CategoryID
(SELECT AVG(UNITPRICE)
FROM tblITEMS)
AS AveragePrice
FROM tblITEMS
ORDER BY ITEMNAMEDESCRIPTION;

Ken Sheridan
Stafford, England
 
K

Ken Sheridan

I've just noticed that I correlated the innermost subquery with the outer
query unnecessarily. As the subquery is not now correlated there is no need
for the aliases TI1 and TI2. Also I assume the subquery would also need to
be restricted on biddate, so would need to join the tables in the same way as
the outer query. So the query should have been:

SELECT
itemnamedescription,
AVG(unitprice) AS [avg of unitprice],
MIN(unitprice) AS [min of unitprice],
MAX(unitprice) AS [max of unitprice],
COUNT(*) AS [count of tblItems],
(SELECT AVG(lowestprice)
FROM (SELECT MIN(unitprice) AS lowestprice
FROM tblProjects INNER JOIN tblItems
ON tblProjects.projectid = tblItems.projectid
WHERE biddate >= DATEADD("m", -13, DATE())
GROUP BY tblprojects.projectid))
AS [avg of lowest prices]
FROM tblProjects INNER JOIN tblItems
ON tblProjects.projectid = tblItems.projectid
WHERE biddate >= DATEADD("m", -13, DATE())
GROUP BY itemnamedescription;

To answer your question the query itself creates the 'lowestprice' column;
its not some existing object in the database. If you take a look at the
innermost subquery you'll see that MIN(unitprice) is returned as a column
named lowestprice, so the column contains the lowest unit prices grouped by
project for the current project returned by the outermost query. The average
of the values in this column is then returned by the first subquery with
AVG(lowestprice) and the column returned by this subquery is given the name
[avg of lowest prices]. As this is the average of the lowest prices for each
project the column will contain the same value in every row returned by the
query of course.

As regards the errors you will se there is no mention of 'False' in the
above query. False is actually a Boolean value, the opposite of True. I
suspect that a reference to a Boolean False has somehow crept into the query,
possibly as a result of your having opened it in query design view and then
amended it. When using subqueries its best to do everything in SQL view and
save it as such without ever going into design view.

On the basis of the table and column names in the original string expression
you posted you should be able to paste the above SQL into a blank query and
open it. I have in fact reconstructed your tables and tried it, and it opens
without any error. One thing I'm not quite clear about, however, is whether
by 'the average of the lowest priced item per project' you mean the average
the average of the lowest prices for any item per project, or the average of
the lowest price for the item returned in the current row of the query's
result table. I'd assumed the former and this is what the above query does.
If it is in fact the latter that's required I can't see how that could be
done in a single query because, having tried it, as far as I can see the
innermost subquery can't be correlated with the outer query. It would
probably need two (or maybe even three) queries joined in a single overall
query.

Ken Sheridan
Stafford, England

scratchtrax said:
Wow Ken!

Thank you so much for your continued response.

I tried to load it into a button and execute but I got an error. I will
review this more closely, but I wanted to acknowledge and thank you for your
input as soon as possible. Thank you.

I get an error that reads false and then in another pop-it it reads,
"...Jet...could not find the query or table named 'False'. Check name and
spelling and try again."
When I look at the code I see "SELECT AVG(lowestprice)" Where/how does
lowestprice exist? Not sure what it means but I do not have a query or table
named 'lowestprice' so I wonder if that's not it...? Maybe I could should
replace this with 'tblITEMS' ? Not sure. I'll try a couple of things in
the meantime. Thanks again for the input.
--
http://njgin.aclink.org


Ken Sheridan said:
To get the average of the lowest prices per project you'll need to use a
subquery within a subquery, the inner subquery identifying the MIN Unitprice
per project and the outer query returning the AVG of these values. The inner
subquery would be correlated with the outermost query on ProjectID, so the
query would look like this:

SELECT
itemnamedescription,
AVG(unitprice) AS [avg of unitprice],
MIN(unitprice) AS [min of unitprice],
MAX(unitprice) AS [max of unitprice],
COUNT(*) AS [count of tblItems],
(SELECT AVG(lowestprice)
FROM (SELECT MIN(unitprice) AS lowestprice
FROM tblItems AS TI2
GROUP BY projectid
WHERE TI2.projectid TI1.projectid))
AS [avg of lowest prices]
FROM tblProjects INNER JOIN tblItems AS TI1
ON tblProjects.projectid = TI1.projectid
WHERE [biddate] >= DATEADD("m", -13, DATE())
GROUP BY itemnamedescription;

Note that I've followed the convention of using lower case for column names,
proper case (preceded by the 'tbl' tag in your case) for table names and
upper case for SQL keywords and function names. This makes no difference to
the operation of the query of course, but I do find this convention makes SQL
more readable than when upper case is used for table and column names also.

Another thing to note is the use of the DATE function rather than the NOW
function. The latter includes the current time of day in its return value,
so any biddate on current date would be excluded if its time of day is eelier
than the current time of day, including those where a date value only has
been entered, as these have a time of day of zero, i.e. midnight at the start
of the day.

I've not been able to test this of course (but I think I've got the logic
right) so I'd suggest you first paste the above into a new query in SQL view
and open it to see if it gives what you are looking for. If so you then have
four options as regards your subform:

1. Save the query and enter its name as the subform's RecordSource property
in its properties sheet.

2. Enter the SQL of the (unsaved) query as the subform's RecordSource
property in its properties sheet.

3. If there's a real reason why you have to set the subform's RecordSource
property via a button, assign the name of the (saved) query to the property
in the button's Click event procedure.

4. Assign the SQL to the subform's RecordSource property with code as you
are currently doing.

Ken Sheridan
Stafford, England

scratchtrax said:
Thanks again Ken.
After I set the subform to continuous and added the the appropriate text box
controls to the subform, I used the following code in a button.
strSELECT = "SELECT DISTINCTROW tblITEMS.ITEMNAMEDESCRIPTION, " & _
"Avg(tblITEMS.UNITPRICE) AS [Avg Of UNITPRICE], Min(tblITEMS.UNITPRICE) " & _
"AS [Min Of UNITPRICE], Max(tblITEMS.UNITPRICE) AS [Max Of UNITPRICE], " & _
"Count(*) AS [Count Of tblITEMS] FROM tblPROJECTS INNER JOIN tblITEMS " & _
"ON tblPROJECTS.PROJECTID = tblITEMS.PROJECTID "
strWHERE = "WHERE ((([tblPROJECTS]![BIDDATE]) >= DateAdd(" & "'m'" & ", -13,
Now()))) GROUP BY tblITEMS.ITEMNAMEDESCRIPTION;"

strFullSQL = strSELECT & strWHERE
[subfrmAVGTBL].[Form].[RecordSource] = "SELECT * FROM tblITEMS WHERE FALSE"
Me.subfrmAVGTBL.Form.RecordSource = strFullSQL

Although I didn't have to do it this way to set the source of the subform, I
haven't used this in a while. It was Tom Wickerath that first introduced me
to using SQL to set the source of a subform but I haven't done this while
performing calculations. This seems to work ok, now I'm trying to add to
this, the average of the lowest priced item per project. This is
troublesome.
Thanks for your patience. Thanks for your input.
--
http://njgin.aclink.org


:

AveragePrice is the name of a column in the query's 'result table' and will
show the values returned by the subquery. If the SQL statement is used as
the RecordSource of the subform AveragePrice will be a field in the form's
underlying recordset and can be used as the ControlSource of a text box.

Ken Sheridan
Stafford, England

:

Thank you for the response. Yes, I am attempting to construct the SQL
statement
that will define the record source of the subform. What you gave me helped
and I thank you. Is the "As AveragePrice" in the statement below refering to
a table column or an unbound text box?

Its the SQL syntax that I don't really get. I was previously able to use
SQL as I am attempting to here with the help of members of this community.
Its a powerful and dynamic way to construct the select statement in a form
with users input. I am rarely able to replicate this as I don't have the SQL
syntax down very well at all. Sometimes I'm able to make a query with the
tools and then switch to SQL view and then copy and paste. However, I'm not
sure how to do that and get statistics.

So, with your input I thought of a different way where I'm going to try to
use an orchestra of queries. Thanks again.
--
http://njgin.aclink.org


:

In the hypothetical example in my last post in the event of the subform being
linked to the parent form on CategoryID the CategoryID column would also have
to be included in the outer query's SELECT clause of course to enable the
subform to be linked to its parent:

SELECT ITEMNAMEDESCRIPTION, UNITPRICE, CategoryID
(SELECT AVG(UNITPRICE)
FROM tblITEMS)
AS AveragePrice
FROM tblITEMS
ORDER BY ITEMNAMEDESCRIPTION;

Ken Sheridan
Stafford, England
 
S

scratchtrax

Ken I am very grateful for your continued correspondence, thank you Sir.

Query worked perfectly, it's not exactly the result I am working toward but
with your help I believe that I will be getting there soon. With the
statement you've provided it appears the returned value is the average of all
the prices for all the items. I am hoping to construct a statement that
returns the 13 month average of the item with the lowest price per project.
The lowest price for each item per project averaged over the last 13 months.
This will give an average of the lowest prices for each item.

I think that you were correct with your exiting statement last time,
serveral queries joined together into a single overall query.

You were also correct regarding the design vs sql view and query construction.

So, how can I build these queries if going into design view will probably
yield errors and my SQL logic and understanding is limited?

Best Regards,

-Matthew

--
http://njgin.aclink.org


Ken Sheridan said:
I've just noticed that I correlated the innermost subquery with the outer
query unnecessarily. As the subquery is not now correlated there is no need
for the aliases TI1 and TI2. Also I assume the subquery would also need to
be restricted on biddate, so would need to join the tables in the same way as
the outer query. So the query should have been:

SELECT
itemnamedescription,
AVG(unitprice) AS [avg of unitprice],
MIN(unitprice) AS [min of unitprice],
MAX(unitprice) AS [max of unitprice],
COUNT(*) AS [count of tblItems],
(SELECT AVG(lowestprice)
FROM (SELECT MIN(unitprice) AS lowestprice
FROM tblProjects INNER JOIN tblItems
ON tblProjects.projectid = tblItems.projectid
WHERE biddate >= DATEADD("m", -13, DATE())
GROUP BY tblprojects.projectid))
AS [avg of lowest prices]
FROM tblProjects INNER JOIN tblItems
ON tblProjects.projectid = tblItems.projectid
WHERE biddate >= DATEADD("m", -13, DATE())
GROUP BY itemnamedescription;

To answer your question the query itself creates the 'lowestprice' column;
its not some existing object in the database. If you take a look at the
innermost subquery you'll see that MIN(unitprice) is returned as a column
named lowestprice, so the column contains the lowest unit prices grouped by
project for the current project returned by the outermost query. The average
of the values in this column is then returned by the first subquery with
AVG(lowestprice) and the column returned by this subquery is given the name
[avg of lowest prices]. As this is the average of the lowest prices for each
project the column will contain the same value in every row returned by the
query of course.

As regards the errors you will se there is no mention of 'False' in the
above query. False is actually a Boolean value, the opposite of True. I
suspect that a reference to a Boolean False has somehow crept into the query,
possibly as a result of your having opened it in query design view and then
amended it. When using subqueries its best to do everything in SQL view and
save it as such without ever going into design view.

On the basis of the table and column names in the original string expression
you posted you should be able to paste the above SQL into a blank query and
open it. I have in fact reconstructed your tables and tried it, and it opens
without any error. One thing I'm not quite clear about, however, is whether
by 'the average of the lowest priced item per project' you mean the average
the average of the lowest prices for any item per project, or the average of
the lowest price for the item returned in the current row of the query's
result table. I'd assumed the former and this is what the above query does.
If it is in fact the latter that's required I can't see how that could be
done in a single query because, having tried it, as far as I can see the
innermost subquery can't be correlated with the outer query. It would
probably need two (or maybe even three) queries joined in a single overall
query.

Ken Sheridan
Stafford, England

scratchtrax said:
Wow Ken!

Thank you so much for your continued response.

I tried to load it into a button and execute but I got an error. I will
review this more closely, but I wanted to acknowledge and thank you for your
input as soon as possible. Thank you.

I get an error that reads false and then in another pop-it it reads,
"...Jet...could not find the query or table named 'False'. Check name and
spelling and try again."
When I look at the code I see "SELECT AVG(lowestprice)" Where/how does
lowestprice exist? Not sure what it means but I do not have a query or table
named 'lowestprice' so I wonder if that's not it...? Maybe I could should
replace this with 'tblITEMS' ? Not sure. I'll try a couple of things in
the meantime. Thanks again for the input.
--
http://njgin.aclink.org


Ken Sheridan said:
To get the average of the lowest prices per project you'll need to use a
subquery within a subquery, the inner subquery identifying the MIN Unitprice
per project and the outer query returning the AVG of these values. The inner
subquery would be correlated with the outermost query on ProjectID, so the
query would look like this:

SELECT
itemnamedescription,
AVG(unitprice) AS [avg of unitprice],
MIN(unitprice) AS [min of unitprice],
MAX(unitprice) AS [max of unitprice],
COUNT(*) AS [count of tblItems],
(SELECT AVG(lowestprice)
FROM (SELECT MIN(unitprice) AS lowestprice
FROM tblItems AS TI2
GROUP BY projectid
WHERE TI2.projectid TI1.projectid))
AS [avg of lowest prices]
FROM tblProjects INNER JOIN tblItems AS TI1
ON tblProjects.projectid = TI1.projectid
WHERE [biddate] >= DATEADD("m", -13, DATE())
GROUP BY itemnamedescription;

Note that I've followed the convention of using lower case for column names,
proper case (preceded by the 'tbl' tag in your case) for table names and
upper case for SQL keywords and function names. This makes no difference to
the operation of the query of course, but I do find this convention makes SQL
more readable than when upper case is used for table and column names also.

Another thing to note is the use of the DATE function rather than the NOW
function. The latter includes the current time of day in its return value,
so any biddate on current date would be excluded if its time of day is eelier
than the current time of day, including those where a date value only has
been entered, as these have a time of day of zero, i.e. midnight at the start
of the day.

I've not been able to test this of course (but I think I've got the logic
right) so I'd suggest you first paste the above into a new query in SQL view
and open it to see if it gives what you are looking for. If so you then have
four options as regards your subform:

1. Save the query and enter its name as the subform's RecordSource property
in its properties sheet.

2. Enter the SQL of the (unsaved) query as the subform's RecordSource
property in its properties sheet.

3. If there's a real reason why you have to set the subform's RecordSource
property via a button, assign the name of the (saved) query to the property
in the button's Click event procedure.

4. Assign the SQL to the subform's RecordSource property with code as you
are currently doing.

Ken Sheridan
Stafford, England

:

Thanks again Ken.
After I set the subform to continuous and added the the appropriate text box
controls to the subform, I used the following code in a button.
strSELECT = "SELECT DISTINCTROW tblITEMS.ITEMNAMEDESCRIPTION, " & _
"Avg(tblITEMS.UNITPRICE) AS [Avg Of UNITPRICE], Min(tblITEMS.UNITPRICE) " & _
"AS [Min Of UNITPRICE], Max(tblITEMS.UNITPRICE) AS [Max Of UNITPRICE], " & _
"Count(*) AS [Count Of tblITEMS] FROM tblPROJECTS INNER JOIN tblITEMS " & _
"ON tblPROJECTS.PROJECTID = tblITEMS.PROJECTID "
strWHERE = "WHERE ((([tblPROJECTS]![BIDDATE]) >= DateAdd(" & "'m'" & ", -13,
Now()))) GROUP BY tblITEMS.ITEMNAMEDESCRIPTION;"

strFullSQL = strSELECT & strWHERE
[subfrmAVGTBL].[Form].[RecordSource] = "SELECT * FROM tblITEMS WHERE FALSE"
Me.subfrmAVGTBL.Form.RecordSource = strFullSQL

Although I didn't have to do it this way to set the source of the subform, I
haven't used this in a while. It was Tom Wickerath that first introduced me
to using SQL to set the source of a subform but I haven't done this while
performing calculations. This seems to work ok, now I'm trying to add to
this, the average of the lowest priced item per project. This is
troublesome.
Thanks for your patience. Thanks for your input.
--
http://njgin.aclink.org


:

AveragePrice is the name of a column in the query's 'result table' and will
show the values returned by the subquery. If the SQL statement is used as
the RecordSource of the subform AveragePrice will be a field in the form's
underlying recordset and can be used as the ControlSource of a text box.

Ken Sheridan
Stafford, England

:

Thank you for the response. Yes, I am attempting to construct the SQL
statement
that will define the record source of the subform. What you gave me helped
and I thank you. Is the "As AveragePrice" in the statement below refering to
a table column or an unbound text box?

Its the SQL syntax that I don't really get. I was previously able to use
SQL as I am attempting to here with the help of members of this community.
Its a powerful and dynamic way to construct the select statement in a form
with users input. I am rarely able to replicate this as I don't have the SQL
syntax down very well at all. Sometimes I'm able to make a query with the
tools and then switch to SQL view and then copy and paste. However, I'm not
sure how to do that and get statistics.

So, with your input I thought of a different way where I'm going to try to
use an orchestra of queries. Thanks again.
--
http://njgin.aclink.org


:

In the hypothetical example in my last post in the event of the subform being
linked to the parent form on CategoryID the CategoryID column would also have
to be included in the outer query's SELECT clause of course to enable the
subform to be linked to its parent:

SELECT ITEMNAMEDESCRIPTION, UNITPRICE, CategoryID
(SELECT AVG(UNITPRICE)
FROM tblITEMS)
AS AveragePrice
FROM tblITEMS
ORDER BY ITEMNAMEDESCRIPTION;

Ken Sheridan
Stafford, England
 
K

Ken Sheridan

Matthew:

Switching to design view in itself won't produce any errors, but it will
present things in a way where it can be difficult to perceive the logic which
is apparent in SQL and therefore make it all too easy to change the query so
that it does produce an error inadvertently. If you want to create complex
queries using subqueries it is really necessary to learn SQL. It might look
a little scary at first, but its actually quite straightforward and logical
once you get your teeth around it. The hard part is getting a mental picture
of the logical operations involved in squirreling the information you want
out of the data, but that's something you have to do however you design a
query..

I'm having difficulty grasping what you mean by 'lowest price for each item
per project averaged over the last 13 months'. There are two aggregation
operations involved here so this means there are two sets involved.
Consequently there has to be a set of a set, i.e. there must be multiple sets
of values from which a single set of lowest values is derived, from which in
turn an average value is derived. In fact as the end result also seems to be
a set of average values, being the averages for 'each' item', it sounds as
though the end result is in fact a set of sets of sets. I think you'd have
to provide an example, as I'm just not clear in my mind what sets we are
dealing with.

Although I'm not clear on the specifics here, I can nevertheless answer your
question in the abstract. Essentially what you would do in query design view
in this sort of scenario is to design a number of simple aggregating queries
to produce each set in the chain; firstly one to produce the set of lowest
prices over, grouping the query by something or other, then another based on
the first to produce the average of those prices grouping the query by
something else. The second grouping would most probably be a subset of the
first, e.g. you might group the first by project and item and return the
lowest (MIN) prices. Then in the then second you might group by item and
return the average (AVG) prices. You'd only need to restrict the first to
the last 13 months. This doesn't sound quite like what you describe as it
returns the average of the lowest prices per item per project (i.e. it allows
for multiple prices for the same item in one project), which is not the same
as 'average of the item with the lowest price per project'.

Ken Sheridan
Stafford, England

scratchtrax said:
Ken I am very grateful for your continued correspondence, thank you Sir.

Query worked perfectly, it's not exactly the result I am working toward but
with your help I believe that I will be getting there soon. With the
statement you've provided it appears the returned value is the average of all
the prices for all the items. I am hoping to construct a statement that
returns the 13 month average of the item with the lowest price per project.
The lowest price for each item per project averaged over the last 13 months.
This will give an average of the lowest prices for each item.

I think that you were correct with your exiting statement last time,
serveral queries joined together into a single overall query.

You were also correct regarding the design vs sql view and query construction.

So, how can I build these queries if going into design view will probably
yield errors and my SQL logic and understanding is limited?

Best Regards,

-Matthew

--
http://njgin.aclink.org


Ken Sheridan said:
I've just noticed that I correlated the innermost subquery with the outer
query unnecessarily. As the subquery is not now correlated there is no need
for the aliases TI1 and TI2. Also I assume the subquery would also need to
be restricted on biddate, so would need to join the tables in the same way as
the outer query. So the query should have been:

SELECT
itemnamedescription,
AVG(unitprice) AS [avg of unitprice],
MIN(unitprice) AS [min of unitprice],
MAX(unitprice) AS [max of unitprice],
COUNT(*) AS [count of tblItems],
(SELECT AVG(lowestprice)
FROM (SELECT MIN(unitprice) AS lowestprice
FROM tblProjects INNER JOIN tblItems
ON tblProjects.projectid = tblItems.projectid
WHERE biddate >= DATEADD("m", -13, DATE())
GROUP BY tblprojects.projectid))
AS [avg of lowest prices]
FROM tblProjects INNER JOIN tblItems
ON tblProjects.projectid = tblItems.projectid
WHERE biddate >= DATEADD("m", -13, DATE())
GROUP BY itemnamedescription;

To answer your question the query itself creates the 'lowestprice' column;
its not some existing object in the database. If you take a look at the
innermost subquery you'll see that MIN(unitprice) is returned as a column
named lowestprice, so the column contains the lowest unit prices grouped by
project for the current project returned by the outermost query. The average
of the values in this column is then returned by the first subquery with
AVG(lowestprice) and the column returned by this subquery is given the name
[avg of lowest prices]. As this is the average of the lowest prices for each
project the column will contain the same value in every row returned by the
query of course.

As regards the errors you will se there is no mention of 'False' in the
above query. False is actually a Boolean value, the opposite of True. I
suspect that a reference to a Boolean False has somehow crept into the query,
possibly as a result of your having opened it in query design view and then
amended it. When using subqueries its best to do everything in SQL view and
save it as such without ever going into design view.

On the basis of the table and column names in the original string expression
you posted you should be able to paste the above SQL into a blank query and
open it. I have in fact reconstructed your tables and tried it, and it opens
without any error. One thing I'm not quite clear about, however, is whether
by 'the average of the lowest priced item per project' you mean the average
the average of the lowest prices for any item per project, or the average of
the lowest price for the item returned in the current row of the query's
result table. I'd assumed the former and this is what the above query does.
If it is in fact the latter that's required I can't see how that could be
done in a single query because, having tried it, as far as I can see the
innermost subquery can't be correlated with the outer query. It would
probably need two (or maybe even three) queries joined in a single overall
query.

Ken Sheridan
Stafford, England

scratchtrax said:
Wow Ken!

Thank you so much for your continued response.

I tried to load it into a button and execute but I got an error. I will
review this more closely, but I wanted to acknowledge and thank you for your
input as soon as possible. Thank you.

I get an error that reads false and then in another pop-it it reads,
"...Jet...could not find the query or table named 'False'. Check name and
spelling and try again."
When I look at the code I see "SELECT AVG(lowestprice)" Where/how does
lowestprice exist? Not sure what it means but I do not have a query or table
named 'lowestprice' so I wonder if that's not it...? Maybe I could should
replace this with 'tblITEMS' ? Not sure. I'll try a couple of things in
the meantime. Thanks again for the input.
--
http://njgin.aclink.org


:

To get the average of the lowest prices per project you'll need to use a
subquery within a subquery, the inner subquery identifying the MIN Unitprice
per project and the outer query returning the AVG of these values. The inner
subquery would be correlated with the outermost query on ProjectID, so the
query would look like this:

SELECT
itemnamedescription,
AVG(unitprice) AS [avg of unitprice],
MIN(unitprice) AS [min of unitprice],
MAX(unitprice) AS [max of unitprice],
COUNT(*) AS [count of tblItems],
(SELECT AVG(lowestprice)
FROM (SELECT MIN(unitprice) AS lowestprice
FROM tblItems AS TI2
GROUP BY projectid
WHERE TI2.projectid TI1.projectid))
AS [avg of lowest prices]
FROM tblProjects INNER JOIN tblItems AS TI1
ON tblProjects.projectid = TI1.projectid
WHERE [biddate] >= DATEADD("m", -13, DATE())
GROUP BY itemnamedescription;

Note that I've followed the convention of using lower case for column names,
proper case (preceded by the 'tbl' tag in your case) for table names and
upper case for SQL keywords and function names. This makes no difference to
the operation of the query of course, but I do find this convention makes SQL
more readable than when upper case is used for table and column names also.

Another thing to note is the use of the DATE function rather than the NOW
function. The latter includes the current time of day in its return value,
so any biddate on current date would be excluded if its time of day is eelier
than the current time of day, including those where a date value only has
been entered, as these have a time of day of zero, i.e. midnight at the start
of the day.

I've not been able to test this of course (but I think I've got the logic
right) so I'd suggest you first paste the above into a new query in SQL view
and open it to see if it gives what you are looking for. If so you then have
four options as regards your subform:

1. Save the query and enter its name as the subform's RecordSource property
in its properties sheet.

2. Enter the SQL of the (unsaved) query as the subform's RecordSource
property in its properties sheet.

3. If there's a real reason why you have to set the subform's RecordSource
property via a button, assign the name of the (saved) query to the property
in the button's Click event procedure.

4. Assign the SQL to the subform's RecordSource property with code as you
are currently doing.

Ken Sheridan
Stafford, England

:

Thanks again Ken.
After I set the subform to continuous and added the the appropriate text box
controls to the subform, I used the following code in a button.
strSELECT = "SELECT DISTINCTROW tblITEMS.ITEMNAMEDESCRIPTION, " & _
"Avg(tblITEMS.UNITPRICE) AS [Avg Of UNITPRICE], Min(tblITEMS.UNITPRICE) " & _
"AS [Min Of UNITPRICE], Max(tblITEMS.UNITPRICE) AS [Max Of UNITPRICE], " & _
"Count(*) AS [Count Of tblITEMS] FROM tblPROJECTS INNER JOIN tblITEMS " & _
"ON tblPROJECTS.PROJECTID = tblITEMS.PROJECTID "
strWHERE = "WHERE ((([tblPROJECTS]![BIDDATE]) >= DateAdd(" & "'m'" & ", -13,
Now()))) GROUP BY tblITEMS.ITEMNAMEDESCRIPTION;"

strFullSQL = strSELECT & strWHERE
[subfrmAVGTBL].[Form].[RecordSource] = "SELECT * FROM tblITEMS WHERE FALSE"
Me.subfrmAVGTBL.Form.RecordSource = strFullSQL

Although I didn't have to do it this way to set the source of the subform, I
haven't used this in a while. It was Tom Wickerath that first introduced me
to using SQL to set the source of a subform but I haven't done this while
performing calculations. This seems to work ok, now I'm trying to add to
this, the average of the lowest priced item per project. This is
troublesome.
Thanks for your patience. Thanks for your input.
--
http://njgin.aclink.org


:

AveragePrice is the name of a column in the query's 'result table' and will
show the values returned by the subquery. If the SQL statement is used as
the RecordSource of the subform AveragePrice will be a field in the form's
underlying recordset and can be used as the ControlSource of a text box.

Ken Sheridan
Stafford, England

:

Thank you for the response. Yes, I am attempting to construct the SQL
statement
that will define the record source of the subform. What you gave me helped
and I thank you. Is the "As AveragePrice" in the statement below refering to
a table column or an unbound text box?

Its the SQL syntax that I don't really get. I was previously able to use
SQL as I am attempting to here with the help of members of this community.
Its a powerful and dynamic way to construct the select statement in a form
with users input. I am rarely able to replicate this as I don't have the SQL
syntax down very well at all. Sometimes I'm able to make a query with the
tools and then switch to SQL view and then copy and paste. However, I'm not
sure how to do that and get statistics.

So, with your input I thought of a different way where I'm going to try to
use an orchestra of queries. Thanks again.
--
http://njgin.aclink.org


:

In the hypothetical example in my last post in the event of the subform being
linked to the parent form on CategoryID the CategoryID column would also have
to be included in the outer query's SELECT clause of course to enable the
subform to be linked to its parent:

SELECT ITEMNAMEDESCRIPTION, UNITPRICE, CategoryID
(SELECT AVG(UNITPRICE)
FROM tblITEMS)
AS AveragePrice
FROM tblITEMS
ORDER BY ITEMNAMEDESCRIPTION;

Ken Sheridan
Stafford, England
 
S

scratchtrax

You have no idea how nice it is to get a thoughtful response (or maybe you do
:)

Thank you very much Ken!

Yes, I will continue to attempt to learn as much as I can about SQL, as I
would like to enjoy this powerful tool. However, it can be a bit scary, as
you noted.
I believe you are exactly right, in the abstract or not, regarding the
smaller grouping linked together. Although, I'm not sure how to link them
together. And your example is almost perfect albeit hypothetical. Here is
what I mean by 'the average of the lowest priced item per project over the
last 13 months'.

Proj #1: 01/12/2008
Price A | Price B | Price C
Item1 $7.00 | $8.00 | $10.00

Proj #2: 08/23/2007
Price A | Price D | Price E
Item1 $7.00 | $6.00 | $5.00

Take the lowest prices for Item 1 within the last 13 months of projects
(ie-$7.00, $5.00). Then average the lowest values (7 + 5 = 12, 12/2 = $6.00)
The average of the lowest price for Item 1 = $6.00

I will continue to attempt what you described previously as links in the
chain and produce the necessary 'sets'. However I am uncertain as to how I
will link them together.

Best Regards,
-Matthew

--
http://njgin.aclink.org


Ken Sheridan said:
Matthew:

Switching to design view in itself won't produce any errors, but it will
present things in a way where it can be difficult to perceive the logic which
is apparent in SQL and therefore make it all too easy to change the query so
that it does produce an error inadvertently. If you want to create complex
queries using subqueries it is really necessary to learn SQL. It might look
a little scary at first, but its actually quite straightforward and logical
once you get your teeth around it. The hard part is getting a mental picture
of the logical operations involved in squirreling the information you want
out of the data, but that's something you have to do however you design a
query..

I'm having difficulty grasping what you mean by 'lowest price for each item
per project averaged over the last 13 months'. There are two aggregation
operations involved here so this means there are two sets involved.
Consequently there has to be a set of a set, i.e. there must be multiple sets
of values from which a single set of lowest values is derived, from which in
turn an average value is derived. In fact as the end result also seems to be
a set of average values, being the averages for 'each' item', it sounds as
though the end result is in fact a set of sets of sets. I think you'd have
to provide an example, as I'm just not clear in my mind what sets we are
dealing with.

Although I'm not clear on the specifics here, I can nevertheless answer your
question in the abstract. Essentially what you would do in query design view
in this sort of scenario is to design a number of simple aggregating queries
to produce each set in the chain; firstly one to produce the set of lowest
prices over, grouping the query by something or other, then another based on
the first to produce the average of those prices grouping the query by
something else. The second grouping would most probably be a subset of the
first, e.g. you might group the first by project and item and return the
lowest (MIN) prices. Then in the then second you might group by item and
return the average (AVG) prices. You'd only need to restrict the first to
the last 13 months. This doesn't sound quite like what you describe as it
returns the average of the lowest prices per item per project (i.e. it allows
for multiple prices for the same item in one project), which is not the same
as 'average of the item with the lowest price per project'.

Ken Sheridan
Stafford, England

scratchtrax said:
Ken I am very grateful for your continued correspondence, thank you Sir.

Query worked perfectly, it's not exactly the result I am working toward but
with your help I believe that I will be getting there soon. With the
statement you've provided it appears the returned value is the average of all
the prices for all the items. I am hoping to construct a statement that
returns the 13 month average of the item with the lowest price per project.
The lowest price for each item per project averaged over the last 13 months.
This will give an average of the lowest prices for each item.

I think that you were correct with your exiting statement last time,
serveral queries joined together into a single overall query.

You were also correct regarding the design vs sql view and query construction.

So, how can I build these queries if going into design view will probably
yield errors and my SQL logic and understanding is limited?

Best Regards,

-Matthew

--
http://njgin.aclink.org


Ken Sheridan said:
I've just noticed that I correlated the innermost subquery with the outer
query unnecessarily. As the subquery is not now correlated there is no need
for the aliases TI1 and TI2. Also I assume the subquery would also need to
be restricted on biddate, so would need to join the tables in the same way as
the outer query. So the query should have been:

SELECT
itemnamedescription,
AVG(unitprice) AS [avg of unitprice],
MIN(unitprice) AS [min of unitprice],
MAX(unitprice) AS [max of unitprice],
COUNT(*) AS [count of tblItems],
(SELECT AVG(lowestprice)
FROM (SELECT MIN(unitprice) AS lowestprice
FROM tblProjects INNER JOIN tblItems
ON tblProjects.projectid = tblItems.projectid
WHERE biddate >= DATEADD("m", -13, DATE())
GROUP BY tblprojects.projectid))
AS [avg of lowest prices]
FROM tblProjects INNER JOIN tblItems
ON tblProjects.projectid = tblItems.projectid
WHERE biddate >= DATEADD("m", -13, DATE())
GROUP BY itemnamedescription;

To answer your question the query itself creates the 'lowestprice' column;
its not some existing object in the database. If you take a look at the
innermost subquery you'll see that MIN(unitprice) is returned as a column
named lowestprice, so the column contains the lowest unit prices grouped by
project for the current project returned by the outermost query. The average
of the values in this column is then returned by the first subquery with
AVG(lowestprice) and the column returned by this subquery is given the name
[avg of lowest prices]. As this is the average of the lowest prices for each
project the column will contain the same value in every row returned by the
query of course.

As regards the errors you will se there is no mention of 'False' in the
above query. False is actually a Boolean value, the opposite of True. I
suspect that a reference to a Boolean False has somehow crept into the query,
possibly as a result of your having opened it in query design view and then
amended it. When using subqueries its best to do everything in SQL view and
save it as such without ever going into design view.

On the basis of the table and column names in the original string expression
you posted you should be able to paste the above SQL into a blank query and
open it. I have in fact reconstructed your tables and tried it, and it opens
without any error. One thing I'm not quite clear about, however, is whether
by 'the average of the lowest priced item per project' you mean the average
the average of the lowest prices for any item per project, or the average of
the lowest price for the item returned in the current row of the query's
result table. I'd assumed the former and this is what the above query does.
If it is in fact the latter that's required I can't see how that could be
done in a single query because, having tried it, as far as I can see the
innermost subquery can't be correlated with the outer query. It would
probably need two (or maybe even three) queries joined in a single overall
query.

Ken Sheridan
Stafford, England

:

Wow Ken!

Thank you so much for your continued response.

I tried to load it into a button and execute but I got an error. I will
review this more closely, but I wanted to acknowledge and thank you for your
input as soon as possible. Thank you.

I get an error that reads false and then in another pop-it it reads,
"...Jet...could not find the query or table named 'False'. Check name and
spelling and try again."
When I look at the code I see "SELECT AVG(lowestprice)" Where/how does
lowestprice exist? Not sure what it means but I do not have a query or table
named 'lowestprice' so I wonder if that's not it...? Maybe I could should
replace this with 'tblITEMS' ? Not sure. I'll try a couple of things in
the meantime. Thanks again for the input.
--
http://njgin.aclink.org


:

To get the average of the lowest prices per project you'll need to use a
subquery within a subquery, the inner subquery identifying the MIN Unitprice
per project and the outer query returning the AVG of these values. The inner
subquery would be correlated with the outermost query on ProjectID, so the
query would look like this:

SELECT
itemnamedescription,
AVG(unitprice) AS [avg of unitprice],
MIN(unitprice) AS [min of unitprice],
MAX(unitprice) AS [max of unitprice],
COUNT(*) AS [count of tblItems],
(SELECT AVG(lowestprice)
FROM (SELECT MIN(unitprice) AS lowestprice
FROM tblItems AS TI2
GROUP BY projectid
WHERE TI2.projectid TI1.projectid))
AS [avg of lowest prices]
FROM tblProjects INNER JOIN tblItems AS TI1
ON tblProjects.projectid = TI1.projectid
WHERE [biddate] >= DATEADD("m", -13, DATE())
GROUP BY itemnamedescription;

Note that I've followed the convention of using lower case for column names,
proper case (preceded by the 'tbl' tag in your case) for table names and
upper case for SQL keywords and function names. This makes no difference to
the operation of the query of course, but I do find this convention makes SQL
more readable than when upper case is used for table and column names also.

Another thing to note is the use of the DATE function rather than the NOW
function. The latter includes the current time of day in its return value,
so any biddate on current date would be excluded if its time of day is eelier
than the current time of day, including those where a date value only has
been entered, as these have a time of day of zero, i.e. midnight at the start
of the day.

I've not been able to test this of course (but I think I've got the logic
right) so I'd suggest you first paste the above into a new query in SQL view
and open it to see if it gives what you are looking for. If so you then have
four options as regards your subform:

1. Save the query and enter its name as the subform's RecordSource property
in its properties sheet.

2. Enter the SQL of the (unsaved) query as the subform's RecordSource
property in its properties sheet.

3. If there's a real reason why you have to set the subform's RecordSource
property via a button, assign the name of the (saved) query to the property
in the button's Click event procedure.

4. Assign the SQL to the subform's RecordSource property with code as you
are currently doing.

Ken Sheridan
Stafford, England

:

Thanks again Ken.
After I set the subform to continuous and added the the appropriate text box
controls to the subform, I used the following code in a button.
strSELECT = "SELECT DISTINCTROW tblITEMS.ITEMNAMEDESCRIPTION, " & _
"Avg(tblITEMS.UNITPRICE) AS [Avg Of UNITPRICE], Min(tblITEMS.UNITPRICE) " & _
"AS [Min Of UNITPRICE], Max(tblITEMS.UNITPRICE) AS [Max Of UNITPRICE], " & _
"Count(*) AS [Count Of tblITEMS] FROM tblPROJECTS INNER JOIN tblITEMS " & _
"ON tblPROJECTS.PROJECTID = tblITEMS.PROJECTID "
strWHERE = "WHERE ((([tblPROJECTS]![BIDDATE]) >= DateAdd(" & "'m'" & ", -13,
Now()))) GROUP BY tblITEMS.ITEMNAMEDESCRIPTION;"

strFullSQL = strSELECT & strWHERE
[subfrmAVGTBL].[Form].[RecordSource] = "SELECT * FROM tblITEMS WHERE FALSE"
Me.subfrmAVGTBL.Form.RecordSource = strFullSQL

Although I didn't have to do it this way to set the source of the subform, I
haven't used this in a while. It was Tom Wickerath that first introduced me
to using SQL to set the source of a subform but I haven't done this while
performing calculations. This seems to work ok, now I'm trying to add to
this, the average of the lowest priced item per project. This is
troublesome.
Thanks for your patience. Thanks for your input.
--
http://njgin.aclink.org


:

AveragePrice is the name of a column in the query's 'result table' and will
show the values returned by the subquery. If the SQL statement is used as
the RecordSource of the subform AveragePrice will be a field in the form's
underlying recordset and can be used as the ControlSource of a text box.

Ken Sheridan
Stafford, England

:

Thank you for the response. Yes, I am attempting to construct the SQL
statement
that will define the record source of the subform. What you gave me helped
and I thank you. Is the "As AveragePrice" in the statement below refering to
a table column or an unbound text box?

Its the SQL syntax that I don't really get. I was previously able to use
SQL as I am attempting to here with the help of members of this community.
Its a powerful and dynamic way to construct the select statement in a form
with users input. I am rarely able to replicate this as I don't have the SQL
syntax down very well at all. Sometimes I'm able to make a query with the
tools and then switch to SQL view and then copy and paste. However, I'm not
sure how to do that and get statistics.

So, with your input I thought of a different way where I'm going to try to
use an orchestra of queries. Thanks again.
--
http://njgin.aclink.org


:

In the hypothetical example in my last post in the event of the subform being
linked to the parent form on CategoryID the CategoryID column would also have
to be included in the outer query's SELECT clause of course to enable the
subform to be linked to its parent:

SELECT ITEMNAMEDESCRIPTION, UNITPRICE, CategoryID
(SELECT AVG(UNITPRICE)
FROM tblITEMS)
AS AveragePrice
FROM tblITEMS
ORDER BY ITEMNAMEDESCRIPTION;

Ken Sheridan
Stafford, England
 
K

Ken Sheridan

Matthew:

I'm not sure about thoughtful; rambling and incoherent might be a better
description <G>. Whatever, I hope it makes some sense and is helpful.

To judge by your web page I think we may be in broadly similar lines of work
(or have been in my case as I'm retired now). I was a Principal Planning
Officer with in the Environmental Planning Unit of Staffordshire County
Council here:


http://www.staffordshire.gov.uk/environment


Back to business:

First start with a simple aggregating query which returns the lowest price
per item per project:

SELECT tblProjects.projectid, itemnamedescription,
MIN(unitprice) AS lowestprice
FROM tblProjects INNER JOIN tblItems
ON tblProjects.projectid = tblItems.projectid
WHERE biddate >= DATEADD("m", -13, DATE())
GROUP BY tblProjects.projectid, itemnamedescription

Save this as qryProjectItemMinCosts say. Then create another query which
averages the costs per item from the above:

SELECT itemnamedescription,
AVG(lowestprice) AS avglowestprice
FROM qryProjectItemMinCosts
GROUP BY itemnamedescription;

Both of the above can easily be created in design view. For the first
select Totals from the View menu, group by tblProjects.projectid and
itemnamedescription, and select Min for the unitprice column. Access will
give the computed column a name like MinOfunitprice, which you can either
keep or change that to lowestprice. The second is based on the first query
and is done in much the same way, grouping by itemnamedescription and select
Avg for the lowest price column (or for the default MinOfunitprice column if
you haven't changed its name in the first query.

If you wish you can then join the second query above to any other query (or
a table) which returns other data about items, joining them on the
itemnamedescription columns. You can then include the columns from the query
to which you join the second query above, along with the avglowestprice
column from the second query above in the result table of the final query.
Bear in mind, though that if one query returned rows for all items and the
other excludes some then you'd need to use a LEFT OUTER JOIN to include all
items. Say for instance you want to list all items and the average lowest
cost for each over the last 13 months, but not all items were included in
projects over that period then you could join the tblItems table to the
second query above, which I'll call qryProjectItemAvgMinCosts, using a LEFT
JOIN (the OUTER is optional and usually omitted) like so:

SELECT DISTINCT tblItems.itemnamedescription, avglowestprice
FROM tblItems LEFT JOIN qryProjectItemAvgMinCosts
ON tblItems.itemnamedescription =
qryProjectItemAvgMinCosts.itemnamedescription;

Note the use of the DISTINCT option here. This is because tblItems has
multiple rows per item so without selecting distinct rows the query would
return duplicate rows for each item. Any items not represented in the last
13 months will have a Null avglowestprice column. Again this query can
easily be created in design view.

BTW from a design point of view you should really have another table with
just the one itemnamedescription column and with each item entered as one
row. This can then be related to your tblItems table with referential
integrity enforced, thus preventing any invalid itemnamedescription names
being entered in tblItems. With this extra table tblItems in fact models a
many-to-many relationship between items and projects. It would actually be
better to call the new table tblItems and the current one tblProjectitems.

Ken Sheridan
Stafford, England

scratchtrax said:
You have no idea how nice it is to get a thoughtful response (or maybe you do
:)

Thank you very much Ken!

Yes, I will continue to attempt to learn as much as I can about SQL, as I
would like to enjoy this powerful tool. However, it can be a bit scary, as
you noted.
I believe you are exactly right, in the abstract or not, regarding the
smaller grouping linked together. Although, I'm not sure how to link them
together. And your example is almost perfect albeit hypothetical. Here is
what I mean by 'the average of the lowest priced item per project over the
last 13 months'.

Proj #1: 01/12/2008
Price A | Price B | Price C
Item1 $7.00 | $8.00 | $10.00

Proj #2: 08/23/2007
Price A | Price D | Price E
Item1 $7.00 | $6.00 | $5.00

Take the lowest prices for Item 1 within the last 13 months of projects
(ie-$7.00, $5.00). Then average the lowest values (7 + 5 = 12, 12/2 = $6.00)
The average of the lowest price for Item 1 = $6.00

I will continue to attempt what you described previously as links in the
chain and produce the necessary 'sets'. However I am uncertain as to how I
will link them together.

Best Regards,
-Matthew

--
http://njgin.aclink.org


Ken Sheridan said:
Matthew:

Switching to design view in itself won't produce any errors, but it will
present things in a way where it can be difficult to perceive the logic which
is apparent in SQL and therefore make it all too easy to change the query so
that it does produce an error inadvertently. If you want to create complex
queries using subqueries it is really necessary to learn SQL. It might look
a little scary at first, but its actually quite straightforward and logical
once you get your teeth around it. The hard part is getting a mental picture
of the logical operations involved in squirreling the information you want
out of the data, but that's something you have to do however you design a
query..

I'm having difficulty grasping what you mean by 'lowest price for each item
per project averaged over the last 13 months'. There are two aggregation
operations involved here so this means there are two sets involved.
Consequently there has to be a set of a set, i.e. there must be multiple sets
of values from which a single set of lowest values is derived, from which in
turn an average value is derived. In fact as the end result also seems to be
a set of average values, being the averages for 'each' item', it sounds as
though the end result is in fact a set of sets of sets. I think you'd have
to provide an example, as I'm just not clear in my mind what sets we are
dealing with.

Although I'm not clear on the specifics here, I can nevertheless answer your
question in the abstract. Essentially what you would do in query design view
in this sort of scenario is to design a number of simple aggregating queries
to produce each set in the chain; firstly one to produce the set of lowest
prices over, grouping the query by something or other, then another based on
the first to produce the average of those prices grouping the query by
something else. The second grouping would most probably be a subset of the
first, e.g. you might group the first by project and item and return the
lowest (MIN) prices. Then in the then second you might group by item and
return the average (AVG) prices. You'd only need to restrict the first to
the last 13 months. This doesn't sound quite like what you describe as it
returns the average of the lowest prices per item per project (i.e. it allows
for multiple prices for the same item in one project), which is not the same
as 'average of the item with the lowest price per project'.

Ken Sheridan
Stafford, England

scratchtrax said:
Ken I am very grateful for your continued correspondence, thank you Sir.

Query worked perfectly, it's not exactly the result I am working toward but
with your help I believe that I will be getting there soon. With the
statement you've provided it appears the returned value is the average of all
the prices for all the items. I am hoping to construct a statement that
returns the 13 month average of the item with the lowest price per project.
The lowest price for each item per project averaged over the last 13 months.
This will give an average of the lowest prices for each item.

I think that you were correct with your exiting statement last time,
serveral queries joined together into a single overall query.

You were also correct regarding the design vs sql view and query construction.

So, how can I build these queries if going into design view will probably
yield errors and my SQL logic and understanding is limited?

Best Regards,

-Matthew

--
http://njgin.aclink.org


:

I've just noticed that I correlated the innermost subquery with the outer
query unnecessarily. As the subquery is not now correlated there is no need
for the aliases TI1 and TI2. Also I assume the subquery would also need to
be restricted on biddate, so would need to join the tables in the same way as
the outer query. So the query should have been:

SELECT
itemnamedescription,
AVG(unitprice) AS [avg of unitprice],
MIN(unitprice) AS [min of unitprice],
MAX(unitprice) AS [max of unitprice],
COUNT(*) AS [count of tblItems],
(SELECT AVG(lowestprice)
FROM (SELECT MIN(unitprice) AS lowestprice
FROM tblProjects INNER JOIN tblItems
ON tblProjects.projectid = tblItems.projectid
WHERE biddate >= DATEADD("m", -13, DATE())
GROUP BY tblprojects.projectid))
AS [avg of lowest prices]
FROM tblProjects INNER JOIN tblItems
ON tblProjects.projectid = tblItems.projectid
WHERE biddate >= DATEADD("m", -13, DATE())
GROUP BY itemnamedescription;

To answer your question the query itself creates the 'lowestprice' column;
its not some existing object in the database. If you take a look at the
innermost subquery you'll see that MIN(unitprice) is returned as a column
named lowestprice, so the column contains the lowest unit prices grouped by
project for the current project returned by the outermost query. The average
of the values in this column is then returned by the first subquery with
AVG(lowestprice) and the column returned by this subquery is given the name
[avg of lowest prices]. As this is the average of the lowest prices for each
project the column will contain the same value in every row returned by the
query of course.

As regards the errors you will se there is no mention of 'False' in the
above query. False is actually a Boolean value, the opposite of True. I
suspect that a reference to a Boolean False has somehow crept into the query,
possibly as a result of your having opened it in query design view and then
amended it. When using subqueries its best to do everything in SQL view and
save it as such without ever going into design view.

On the basis of the table and column names in the original string expression
you posted you should be able to paste the above SQL into a blank query and
open it. I have in fact reconstructed your tables and tried it, and it opens
without any error. One thing I'm not quite clear about, however, is whether
by 'the average of the lowest priced item per project' you mean the average
the average of the lowest prices for any item per project, or the average of
the lowest price for the item returned in the current row of the query's
result table. I'd assumed the former and this is what the above query does.
If it is in fact the latter that's required I can't see how that could be
done in a single query because, having tried it, as far as I can see the
innermost subquery can't be correlated with the outer query. It would
probably need two (or maybe even three) queries joined in a single overall
query.

Ken Sheridan
Stafford, England

:

Wow Ken!

Thank you so much for your continued response.

I tried to load it into a button and execute but I got an error. I will
review this more closely, but I wanted to acknowledge and thank you for your
input as soon as possible. Thank you.

I get an error that reads false and then in another pop-it it reads,
"...Jet...could not find the query or table named 'False'. Check name and
spelling and try again."
When I look at the code I see "SELECT AVG(lowestprice)" Where/how does
lowestprice exist? Not sure what it means but I do not have a query or table
named 'lowestprice' so I wonder if that's not it...? Maybe I could should
replace this with 'tblITEMS' ? Not sure. I'll try a couple of things in
the meantime. Thanks again for the input.
--
http://njgin.aclink.org


:

To get the average of the lowest prices per project you'll need to use a
subquery within a subquery, the inner subquery identifying the MIN Unitprice
per project and the outer query returning the AVG of these values. The inner
subquery would be correlated with the outermost query on ProjectID, so the
query would look like this:

SELECT
itemnamedescription,
AVG(unitprice) AS [avg of unitprice],
MIN(unitprice) AS [min of unitprice],
MAX(unitprice) AS [max of unitprice],
COUNT(*) AS [count of tblItems],
(SELECT AVG(lowestprice)
FROM (SELECT MIN(unitprice) AS lowestprice
FROM tblItems AS TI2
GROUP BY projectid
WHERE TI2.projectid TI1.projectid))
AS [avg of lowest prices]
FROM tblProjects INNER JOIN tblItems AS TI1
ON tblProjects.projectid = TI1.projectid
WHERE [biddate] >= DATEADD("m", -13, DATE())
GROUP BY itemnamedescription;

Note that I've followed the convention of using lower case for column names,
proper case (preceded by the 'tbl' tag in your case) for table names and
upper case for SQL keywords and function names. This makes no difference to
the operation of the query of course, but I do find this convention makes SQL
more readable than when upper case is used for table and column names also.

Another thing to note is the use of the DATE function rather than the NOW
function. The latter includes the current time of day in its return value,
so any biddate on current date would be excluded if its time of day is eelier
than the current time of day, including those where a date value only has
been entered, as these have a time of day of zero, i.e. midnight at the start
of the day.

I've not been able to test this of course (but I think I've got the logic
right) so I'd suggest you first paste the above into a new query in SQL view
and open it to see if it gives what you are looking for. If so you then have
four options as regards your subform:

1. Save the query and enter its name as the subform's RecordSource property
in its properties sheet.

2. Enter the SQL of the (unsaved) query as the subform's RecordSource
property in its properties sheet.

3. If there's a real reason why you have to set the subform's RecordSource
property via a button, assign the name of the (saved) query to the property
in the button's Click event procedure.

4. Assign the SQL to the subform's RecordSource property with code as you
are currently doing.

Ken Sheridan
Stafford, England

:

Thanks again Ken.
After I set the subform to continuous and added the the appropriate text box
controls to the subform, I used the following code in a button.
strSELECT = "SELECT DISTINCTROW tblITEMS.ITEMNAMEDESCRIPTION, " & _
"Avg(tblITEMS.UNITPRICE) AS [Avg Of UNITPRICE], Min(tblITEMS.UNITPRICE) " & _
"AS [Min Of UNITPRICE], Max(tblITEMS.UNITPRICE) AS [Max Of UNITPRICE], " & _
"Count(*) AS [Count Of tblITEMS] FROM tblPROJECTS INNER JOIN tblITEMS " & _
"ON tblPROJECTS.PROJECTID = tblITEMS.PROJECTID "
strWHERE = "WHERE ((([tblPROJECTS]![BIDDATE]) >= DateAdd(" & "'m'" & ", -13,
Now()))) GROUP BY tblITEMS.ITEMNAMEDESCRIPTION;"

strFullSQL = strSELECT & strWHERE
[subfrmAVGTBL].[Form].[RecordSource] = "SELECT * FROM tblITEMS WHERE FALSE"
Me.subfrmAVGTBL.Form.RecordSource = strFullSQL

Although I didn't have to do it this way to set the source of the subform, I
haven't used this in a while. It was Tom Wickerath that first introduced me
to using SQL to set the source of a subform but I haven't done this while
performing calculations. This seems to work ok, now I'm trying to add to
this, the average of the lowest priced item per project. This is
troublesome.
Thanks for your patience. Thanks for your input.
--
http://njgin.aclink.org


:

AveragePrice is the name of a column in the query's 'result table' and will
show the values returned by the subquery. If the SQL statement is used as
the RecordSource of the subform AveragePrice will be a field in the form's
underlying recordset and can be used as the ControlSource of a text box.

Ken Sheridan
Stafford, England

:

Thank you for the response. Yes, I am attempting to construct the SQL
statement
that will define the record source of the subform. What you gave me helped
and I thank you. Is the "As AveragePrice" in the statement below refering to
a table column or an unbound text box?

Its the SQL syntax that I don't really get. I was previously able to use
SQL as I am attempting to here with the help of members of this community.
Its a powerful and dynamic way to construct the select statement in a form
 
S

scratchtrax

Hello Ken,

I just looked at your link, very nice. Thank you for sharing. I think
you're right, we do and have done similar things.

Looking over your last reply and I'm going to try to respond as I go
through it, so we'll see if this helps or hinders.

Well that worked like a charm, thank you. I will go through the design
mode method now. Wow this it great, THANK YOU KEN!!

I am going to continue on with this and I'll reply with the resulting
codes & queries. Although, I'm sure it will be exactly as you've generously
described.

I remain very grateful, thank you so much.

Best Regards,
-Matthew

--
http://njgin.aclink.org


Ken Sheridan said:
Matthew:

I'm not sure about thoughtful; rambling and incoherent might be a better
description <G>. Whatever, I hope it makes some sense and is helpful.

To judge by your web page I think we may be in broadly similar lines of work
(or have been in my case as I'm retired now). I was a Principal Planning
Officer with in the Environmental Planning Unit of Staffordshire County
Council here:


http://www.staffordshire.gov.uk/environment


Back to business:

First start with a simple aggregating query which returns the lowest price
per item per project:

SELECT tblProjects.projectid, itemnamedescription,
MIN(unitprice) AS lowestprice
FROM tblProjects INNER JOIN tblItems
ON tblProjects.projectid = tblItems.projectid
WHERE biddate >= DATEADD("m", -13, DATE())
GROUP BY tblProjects.projectid, itemnamedescription

Save this as qryProjectItemMinCosts say. Then create another query which
averages the costs per item from the above:

SELECT itemnamedescription,
AVG(lowestprice) AS avglowestprice
FROM qryProjectItemMinCosts
GROUP BY itemnamedescription;

Both of the above can easily be created in design view. For the first
select Totals from the View menu, group by tblProjects.projectid and
itemnamedescription, and select Min for the unitprice column. Access will
give the computed column a name like MinOfunitprice, which you can either
keep or change that to lowestprice. The second is based on the first query
and is done in much the same way, grouping by itemnamedescription and select
Avg for the lowest price column (or for the default MinOfunitprice column if
you haven't changed its name in the first query.

If you wish you can then join the second query above to any other query (or
a table) which returns other data about items, joining them on the
itemnamedescription columns. You can then include the columns from the query
to which you join the second query above, along with the avglowestprice
column from the second query above in the result table of the final query.
Bear in mind, though that if one query returned rows for all items and the
other excludes some then you'd need to use a LEFT OUTER JOIN to include all
items. Say for instance you want to list all items and the average lowest
cost for each over the last 13 months, but not all items were included in
projects over that period then you could join the tblItems table to the
second query above, which I'll call qryProjectItemAvgMinCosts, using a LEFT
JOIN (the OUTER is optional and usually omitted) like so:

SELECT DISTINCT tblItems.itemnamedescription, avglowestprice
FROM tblItems LEFT JOIN qryProjectItemAvgMinCosts
ON tblItems.itemnamedescription =
qryProjectItemAvgMinCosts.itemnamedescription;

Note the use of the DISTINCT option here. This is because tblItems has
multiple rows per item so without selecting distinct rows the query would
return duplicate rows for each item. Any items not represented in the last
13 months will have a Null avglowestprice column. Again this query can
easily be created in design view.

BTW from a design point of view you should really have another table with
just the one itemnamedescription column and with each item entered as one
row. This can then be related to your tblItems table with referential
integrity enforced, thus preventing any invalid itemnamedescription names
being entered in tblItems. With this extra table tblItems in fact models a
many-to-many relationship between items and projects. It would actually be
better to call the new table tblItems and the current one tblProjectitems.

Ken Sheridan
Stafford, England

scratchtrax said:
You have no idea how nice it is to get a thoughtful response (or maybe you do
:)

Thank you very much Ken!

Yes, I will continue to attempt to learn as much as I can about SQL, as I
would like to enjoy this powerful tool. However, it can be a bit scary, as
you noted.
I believe you are exactly right, in the abstract or not, regarding the
smaller grouping linked together. Although, I'm not sure how to link them
together. And your example is almost perfect albeit hypothetical. Here is
what I mean by 'the average of the lowest priced item per project over the
last 13 months'.

Proj #1: 01/12/2008
Price A | Price B | Price C
Item1 $7.00 | $8.00 | $10.00

Proj #2: 08/23/2007
Price A | Price D | Price E
Item1 $7.00 | $6.00 | $5.00

Take the lowest prices for Item 1 within the last 13 months of projects
(ie-$7.00, $5.00). Then average the lowest values (7 + 5 = 12, 12/2 = $6.00)
The average of the lowest price for Item 1 = $6.00

I will continue to attempt what you described previously as links in the
chain and produce the necessary 'sets'. However I am uncertain as to how I
will link them together.

Best Regards,
-Matthew

--
http://njgin.aclink.org


Ken Sheridan said:
Matthew:

Switching to design view in itself won't produce any errors, but it will
present things in a way where it can be difficult to perceive the logic which
is apparent in SQL and therefore make it all too easy to change the query so
that it does produce an error inadvertently. If you want to create complex
queries using subqueries it is really necessary to learn SQL. It might look
a little scary at first, but its actually quite straightforward and logical
once you get your teeth around it. The hard part is getting a mental picture
of the logical operations involved in squirreling the information you want
out of the data, but that's something you have to do however you design a
query..

I'm having difficulty grasping what you mean by 'lowest price for each item
per project averaged over the last 13 months'. There are two aggregation
operations involved here so this means there are two sets involved.
Consequently there has to be a set of a set, i.e. there must be multiple sets
of values from which a single set of lowest values is derived, from which in
turn an average value is derived. In fact as the end result also seems to be
a set of average values, being the averages for 'each' item', it sounds as
though the end result is in fact a set of sets of sets. I think you'd have
to provide an example, as I'm just not clear in my mind what sets we are
dealing with.

Although I'm not clear on the specifics here, I can nevertheless answer your
question in the abstract. Essentially what you would do in query design view
in this sort of scenario is to design a number of simple aggregating queries
to produce each set in the chain; firstly one to produce the set of lowest
prices over, grouping the query by something or other, then another based on
the first to produce the average of those prices grouping the query by
something else. The second grouping would most probably be a subset of the
first, e.g. you might group the first by project and item and return the
lowest (MIN) prices. Then in the then second you might group by item and
return the average (AVG) prices. You'd only need to restrict the first to
the last 13 months. This doesn't sound quite like what you describe as it
returns the average of the lowest prices per item per project (i.e. it allows
for multiple prices for the same item in one project), which is not the same
as 'average of the item with the lowest price per project'.

Ken Sheridan
Stafford, England

:

Ken I am very grateful for your continued correspondence, thank you Sir.

Query worked perfectly, it's not exactly the result I am working toward but
with your help I believe that I will be getting there soon. With the
statement you've provided it appears the returned value is the average of all
the prices for all the items. I am hoping to construct a statement that
returns the 13 month average of the item with the lowest price per project.
The lowest price for each item per project averaged over the last 13 months.
This will give an average of the lowest prices for each item.

I think that you were correct with your exiting statement last time,
serveral queries joined together into a single overall query.

You were also correct regarding the design vs sql view and query construction.

So, how can I build these queries if going into design view will probably
yield errors and my SQL logic and understanding is limited?

Best Regards,

-Matthew

--
http://njgin.aclink.org


:

I've just noticed that I correlated the innermost subquery with the outer
query unnecessarily. As the subquery is not now correlated there is no need
for the aliases TI1 and TI2. Also I assume the subquery would also need to
be restricted on biddate, so would need to join the tables in the same way as
the outer query. So the query should have been:

SELECT
itemnamedescription,
AVG(unitprice) AS [avg of unitprice],
MIN(unitprice) AS [min of unitprice],
MAX(unitprice) AS [max of unitprice],
COUNT(*) AS [count of tblItems],
(SELECT AVG(lowestprice)
FROM (SELECT MIN(unitprice) AS lowestprice
FROM tblProjects INNER JOIN tblItems
ON tblProjects.projectid = tblItems.projectid
WHERE biddate >= DATEADD("m", -13, DATE())
GROUP BY tblprojects.projectid))
AS [avg of lowest prices]
FROM tblProjects INNER JOIN tblItems
ON tblProjects.projectid = tblItems.projectid
WHERE biddate >= DATEADD("m", -13, DATE())
GROUP BY itemnamedescription;

To answer your question the query itself creates the 'lowestprice' column;
its not some existing object in the database. If you take a look at the
innermost subquery you'll see that MIN(unitprice) is returned as a column
named lowestprice, so the column contains the lowest unit prices grouped by
project for the current project returned by the outermost query. The average
of the values in this column is then returned by the first subquery with
AVG(lowestprice) and the column returned by this subquery is given the name
[avg of lowest prices]. As this is the average of the lowest prices for each
project the column will contain the same value in every row returned by the
query of course.

As regards the errors you will se there is no mention of 'False' in the
above query. False is actually a Boolean value, the opposite of True. I
suspect that a reference to a Boolean False has somehow crept into the query,
possibly as a result of your having opened it in query design view and then
amended it. When using subqueries its best to do everything in SQL view and
save it as such without ever going into design view.

On the basis of the table and column names in the original string expression
you posted you should be able to paste the above SQL into a blank query and
open it. I have in fact reconstructed your tables and tried it, and it opens
without any error. One thing I'm not quite clear about, however, is whether
by 'the average of the lowest priced item per project' you mean the average
the average of the lowest prices for any item per project, or the average of
the lowest price for the item returned in the current row of the query's
result table. I'd assumed the former and this is what the above query does.
If it is in fact the latter that's required I can't see how that could be
done in a single query because, having tried it, as far as I can see the
innermost subquery can't be correlated with the outer query. It would
probably need two (or maybe even three) queries joined in a single overall
query.

Ken Sheridan
Stafford, England

:

Wow Ken!

Thank you so much for your continued response.

I tried to load it into a button and execute but I got an error. I will
review this more closely, but I wanted to acknowledge and thank you for your
input as soon as possible. Thank you.

I get an error that reads false and then in another pop-it it reads,
"...Jet...could not find the query or table named 'False'. Check name and
spelling and try again."
When I look at the code I see "SELECT AVG(lowestprice)" Where/how does
lowestprice exist? Not sure what it means but I do not have a query or table
named 'lowestprice' so I wonder if that's not it...? Maybe I could should
replace this with 'tblITEMS' ? Not sure. I'll try a couple of things in
the meantime. Thanks again for the input.
--
http://njgin.aclink.org


:

To get the average of the lowest prices per project you'll need to use a
subquery within a subquery, the inner subquery identifying the MIN Unitprice
per project and the outer query returning the AVG of these values. The inner
subquery would be correlated with the outermost query on ProjectID, so the
query would look like this:

SELECT
itemnamedescription,
AVG(unitprice) AS [avg of unitprice],
MIN(unitprice) AS [min of unitprice],
MAX(unitprice) AS [max of unitprice],
COUNT(*) AS [count of tblItems],
(SELECT AVG(lowestprice)
FROM (SELECT MIN(unitprice) AS lowestprice
FROM tblItems AS TI2
GROUP BY projectid
WHERE TI2.projectid TI1.projectid))
AS [avg of lowest prices]
FROM tblProjects INNER JOIN tblItems AS TI1
ON tblProjects.projectid = TI1.projectid
WHERE [biddate] >= DATEADD("m", -13, DATE())
GROUP BY itemnamedescription;

Note that I've followed the convention of using lower case for column names,
proper case (preceded by the 'tbl' tag in your case) for table names and
upper case for SQL keywords and function names. This makes no difference to
 
S

scratchtrax

Well, the queries work very well. Now I'm trying to figure which textbox
controls (fields) to add on the subform and link the source via SQL. Earlier
in this thread I had to add these fields and set the form to continuous in
order for this to work. I will probably have to use the left outer join as
you described earlier, but first I need the appropriate textbox controls (or
fields) on the subform. I'll keep you posted.

--
http://njgin.aclink.org


scratchtrax said:
Hello Ken,

I just looked at your link, very nice. Thank you for sharing. I think
you're right, we do and have done similar things.

Looking over your last reply and I'm going to try to respond as I go
through it, so we'll see if this helps or hinders.

Well that worked like a charm, thank you. I will go through the design
mode method now. Wow this it great, THANK YOU KEN!!

I am going to continue on with this and I'll reply with the resulting
codes & queries. Although, I'm sure it will be exactly as you've generously
described.

I remain very grateful, thank you so much.

Best Regards,
-Matthew

--
http://njgin.aclink.org


Ken Sheridan said:
Matthew:

I'm not sure about thoughtful; rambling and incoherent might be a better
description <G>. Whatever, I hope it makes some sense and is helpful.

To judge by your web page I think we may be in broadly similar lines of work
(or have been in my case as I'm retired now). I was a Principal Planning
Officer with in the Environmental Planning Unit of Staffordshire County
Council here:


http://www.staffordshire.gov.uk/environment


Back to business:

First start with a simple aggregating query which returns the lowest price
per item per project:

SELECT tblProjects.projectid, itemnamedescription,
MIN(unitprice) AS lowestprice
FROM tblProjects INNER JOIN tblItems
ON tblProjects.projectid = tblItems.projectid
WHERE biddate >= DATEADD("m", -13, DATE())
GROUP BY tblProjects.projectid, itemnamedescription

Save this as qryProjectItemMinCosts say. Then create another query which
averages the costs per item from the above:

SELECT itemnamedescription,
AVG(lowestprice) AS avglowestprice
FROM qryProjectItemMinCosts
GROUP BY itemnamedescription;

Both of the above can easily be created in design view. For the first
select Totals from the View menu, group by tblProjects.projectid and
itemnamedescription, and select Min for the unitprice column. Access will
give the computed column a name like MinOfunitprice, which you can either
keep or change that to lowestprice. The second is based on the first query
and is done in much the same way, grouping by itemnamedescription and select
Avg for the lowest price column (or for the default MinOfunitprice column if
you haven't changed its name in the first query.

If you wish you can then join the second query above to any other query (or
a table) which returns other data about items, joining them on the
itemnamedescription columns. You can then include the columns from the query
to which you join the second query above, along with the avglowestprice
column from the second query above in the result table of the final query.
Bear in mind, though that if one query returned rows for all items and the
other excludes some then you'd need to use a LEFT OUTER JOIN to include all
items. Say for instance you want to list all items and the average lowest
cost for each over the last 13 months, but not all items were included in
projects over that period then you could join the tblItems table to the
second query above, which I'll call qryProjectItemAvgMinCosts, using a LEFT
JOIN (the OUTER is optional and usually omitted) like so:

SELECT DISTINCT tblItems.itemnamedescription, avglowestprice
FROM tblItems LEFT JOIN qryProjectItemAvgMinCosts
ON tblItems.itemnamedescription =
qryProjectItemAvgMinCosts.itemnamedescription;

Note the use of the DISTINCT option here. This is because tblItems has
multiple rows per item so without selecting distinct rows the query would
return duplicate rows for each item. Any items not represented in the last
13 months will have a Null avglowestprice column. Again this query can
easily be created in design view.

BTW from a design point of view you should really have another table with
just the one itemnamedescription column and with each item entered as one
row. This can then be related to your tblItems table with referential
integrity enforced, thus preventing any invalid itemnamedescription names
being entered in tblItems. With this extra table tblItems in fact models a
many-to-many relationship between items and projects. It would actually be
better to call the new table tblItems and the current one tblProjectitems.

Ken Sheridan
Stafford, England

scratchtrax said:
You have no idea how nice it is to get a thoughtful response (or maybe you do
:)

Thank you very much Ken!

Yes, I will continue to attempt to learn as much as I can about SQL, as I
would like to enjoy this powerful tool. However, it can be a bit scary, as
you noted.
I believe you are exactly right, in the abstract or not, regarding the
smaller grouping linked together. Although, I'm not sure how to link them
together. And your example is almost perfect albeit hypothetical. Here is
what I mean by 'the average of the lowest priced item per project over the
last 13 months'.

Proj #1: 01/12/2008
Price A | Price B | Price C
Item1 $7.00 | $8.00 | $10.00

Proj #2: 08/23/2007
Price A | Price D | Price E
Item1 $7.00 | $6.00 | $5.00

Take the lowest prices for Item 1 within the last 13 months of projects
(ie-$7.00, $5.00). Then average the lowest values (7 + 5 = 12, 12/2 = $6.00)
The average of the lowest price for Item 1 = $6.00

I will continue to attempt what you described previously as links in the
chain and produce the necessary 'sets'. However I am uncertain as to how I
will link them together.

Best Regards,
-Matthew

--
http://njgin.aclink.org


:

Matthew:

Switching to design view in itself won't produce any errors, but it will
present things in a way where it can be difficult to perceive the logic which
is apparent in SQL and therefore make it all too easy to change the query so
that it does produce an error inadvertently. If you want to create complex
queries using subqueries it is really necessary to learn SQL. It might look
a little scary at first, but its actually quite straightforward and logical
once you get your teeth around it. The hard part is getting a mental picture
of the logical operations involved in squirreling the information you want
out of the data, but that's something you have to do however you design a
query..

I'm having difficulty grasping what you mean by 'lowest price for each item
per project averaged over the last 13 months'. There are two aggregation
operations involved here so this means there are two sets involved.
Consequently there has to be a set of a set, i.e. there must be multiple sets
of values from which a single set of lowest values is derived, from which in
turn an average value is derived. In fact as the end result also seems to be
a set of average values, being the averages for 'each' item', it sounds as
though the end result is in fact a set of sets of sets. I think you'd have
to provide an example, as I'm just not clear in my mind what sets we are
dealing with.

Although I'm not clear on the specifics here, I can nevertheless answer your
question in the abstract. Essentially what you would do in query design view
in this sort of scenario is to design a number of simple aggregating queries
to produce each set in the chain; firstly one to produce the set of lowest
prices over, grouping the query by something or other, then another based on
the first to produce the average of those prices grouping the query by
something else. The second grouping would most probably be a subset of the
first, e.g. you might group the first by project and item and return the
lowest (MIN) prices. Then in the then second you might group by item and
return the average (AVG) prices. You'd only need to restrict the first to
the last 13 months. This doesn't sound quite like what you describe as it
returns the average of the lowest prices per item per project (i.e. it allows
for multiple prices for the same item in one project), which is not the same
as 'average of the item with the lowest price per project'.

Ken Sheridan
Stafford, England

:

Ken I am very grateful for your continued correspondence, thank you Sir.

Query worked perfectly, it's not exactly the result I am working toward but
with your help I believe that I will be getting there soon. With the
statement you've provided it appears the returned value is the average of all
the prices for all the items. I am hoping to construct a statement that
returns the 13 month average of the item with the lowest price per project.
The lowest price for each item per project averaged over the last 13 months.
This will give an average of the lowest prices for each item.

I think that you were correct with your exiting statement last time,
serveral queries joined together into a single overall query.

You were also correct regarding the design vs sql view and query construction.

So, how can I build these queries if going into design view will probably
yield errors and my SQL logic and understanding is limited?

Best Regards,

-Matthew

--
http://njgin.aclink.org


:

I've just noticed that I correlated the innermost subquery with the outer
query unnecessarily. As the subquery is not now correlated there is no need
for the aliases TI1 and TI2. Also I assume the subquery would also need to
be restricted on biddate, so would need to join the tables in the same way as
the outer query. So the query should have been:

SELECT
itemnamedescription,
AVG(unitprice) AS [avg of unitprice],
MIN(unitprice) AS [min of unitprice],
MAX(unitprice) AS [max of unitprice],
COUNT(*) AS [count of tblItems],
(SELECT AVG(lowestprice)
FROM (SELECT MIN(unitprice) AS lowestprice
FROM tblProjects INNER JOIN tblItems
ON tblProjects.projectid = tblItems.projectid
WHERE biddate >= DATEADD("m", -13, DATE())
GROUP BY tblprojects.projectid))
AS [avg of lowest prices]
FROM tblProjects INNER JOIN tblItems
ON tblProjects.projectid = tblItems.projectid
WHERE biddate >= DATEADD("m", -13, DATE())
GROUP BY itemnamedescription;

To answer your question the query itself creates the 'lowestprice' column;
its not some existing object in the database. If you take a look at the
innermost subquery you'll see that MIN(unitprice) is returned as a column
named lowestprice, so the column contains the lowest unit prices grouped by
project for the current project returned by the outermost query. The average
of the values in this column is then returned by the first subquery with
AVG(lowestprice) and the column returned by this subquery is given the name
[avg of lowest prices]. As this is the average of the lowest prices for each
project the column will contain the same value in every row returned by the
query of course.

As regards the errors you will se there is no mention of 'False' in the
above query. False is actually a Boolean value, the opposite of True. I
suspect that a reference to a Boolean False has somehow crept into the query,
possibly as a result of your having opened it in query design view and then
amended it. When using subqueries its best to do everything in SQL view and
save it as such without ever going into design view.

On the basis of the table and column names in the original string expression
you posted you should be able to paste the above SQL into a blank query and
open it. I have in fact reconstructed your tables and tried it, and it opens
without any error. One thing I'm not quite clear about, however, is whether
by 'the average of the lowest priced item per project' you mean the average
the average of the lowest prices for any item per project, or the average of
the lowest price for the item returned in the current row of the query's
result table. I'd assumed the former and this is what the above query does.
If it is in fact the latter that's required I can't see how that could be
done in a single query because, having tried it, as far as I can see the
innermost subquery can't be correlated with the outer query. It would
probably need two (or maybe even three) queries joined in a single overall
query.

Ken Sheridan
Stafford, England

:

Wow Ken!

Thank you so much for your continued response.

I tried to load it into a button and execute but I got an error. I will
review this more closely, but I wanted to acknowledge and thank you for your
input as soon as possible. Thank you.

I get an error that reads false and then in another pop-it it reads,
"...Jet...could not find the query or table named 'False'. Check name and
spelling and try again."
When I look at the code I see "SELECT AVG(lowestprice)" Where/how does
lowestprice exist? Not sure what it means but I do not have a query or table
named 'lowestprice' so I wonder if that's not it...? Maybe I could should
replace this with 'tblITEMS' ? Not sure. I'll try a couple of things in
the meantime. Thanks again for the input.
--
http://njgin.aclink.org


:
 
S

scratchtrax

Hello again Ken,
I just wanted to enter what I will probably use. Thank you for all of
your help and patience I remain grateful.

So I added combo controls that could be used as the sql statement is
built by the user interacting with the controls. In the following snip-it, I
am asking for thirteen months from today and the users selected criteria.


Private Sub cboConstructionTypePick_AfterUpdate()
On Error GoTo Err_cboConstructionTypePick_AfterUpdate

Dim strSELECT As String
Dim strWHERE As String
Dim strOrderBy As String
Dim strGroup As String
Dim strFullSQL As String
Dim stDocName As String

strSELECT = "SELECT tblPROJECTS.PROJECTID, tblPROJECTS.BIDDATE, " & _
"tblPROJECTS.WORKTYPE, tblPROJECTS.FUNCTIONALCLASS, " & _
"tblPROJECTS.CONSTRUCTCLASS, tblITEMS.ITEMNAMEDESCRIPTION, " & _
"Min(tblITEMS.UNITPRICE) AS MinPrice, Max(tblITEMS.UNITPRICE) AS
MaxPrice, " & _
"Avg(tblITEMS.UNITPRICE) AS AvgProjectPrice, " & _
"Count(tblITEMS.UNITPRICE) AS CountofPricesUsedForAvg, " & _
"qryItemNormalCosts.AvgPriceNormal,
qryProjectItemAvgMinCosts.AvgofLowPrice " & _
"FROM ((tblPROJECTS INNER JOIN tblITEMS ON tblPROJECTS.PROJECTID = " & _
"tblITEMS.PROJECTID) LEFT JOIN qryItemNormalCosts ON " & _
"tblITEMS.ITEMNAMEDESCRIPTION = qryItemNormalCosts.ITEMNAMEDESCRIPTION)
" & _
"LEFT JOIN qryProjectItemAvgMinCosts ON " & _
"tblITEMS.ITEMNAMEDESCRIPTION =
qryProjectItemAvgMinCosts.ITEMNAMEDESCRIPTION " & _
"GROUP BY tblPROJECTS.PROJECTID, tblPROJECTS.BIDDATE,
tblPROJECTS.WORKTYPE, " & _
"tblPROJECTS.FUNCTIONALCLASS, tblPROJECTS.CONSTRUCTCLASS, " & _
"tblITEMS.ITEMNAMEDESCRIPTION, qryItemNormalCosts.AvgPriceNormal, " & _
"qryProjectItemAvgMinCosts.AvgofLowPrice " & _
"HAVING (((tblPROJECTS.BIDDATE)>=DateAdd('m',-13,Date())) " & _
"AND ((tblPROJECTS.WORKTYPE)= '" & Me.cboWorkTypePick & " ') AND " & _
"((tblPROJECTS.CONSTRUCTCLASS)='" & Me.cboConstructionTypePick & "'));"





strFullSQL = strSELECT '& strWHERE & strGroup & strOrderBy
MsgBox strFullSQL
MsgBox "Worktype = " & Me.cboConstructionTypePick
[subfrmAVGTBL].[Form].[RecordSource] = "SELECT * FROM tblITEMS WHERE FALSE"
Me.subfrmAVGTBL.Form.RecordSource = strFullSQL



I am probably not at the highest efficiency with this method. Given my
Access and programming shortcomings, I'm willing to accept it and just be
happy that its working
:)
--
http://njgin.aclink.org


scratchtrax said:
Well, the queries work very well. Now I'm trying to figure which textbox
controls (fields) to add on the subform and link the source via SQL. Earlier
in this thread I had to add these fields and set the form to continuous in
order for this to work. I will probably have to use the left outer join as
you described earlier, but first I need the appropriate textbox controls (or
fields) on the subform. I'll keep you posted.

--
http://njgin.aclink.org


scratchtrax said:
Hello Ken,

I just looked at your link, very nice. Thank you for sharing. I think
you're right, we do and have done similar things.

Looking over your last reply and I'm going to try to respond as I go
through it, so we'll see if this helps or hinders.

Well that worked like a charm, thank you. I will go through the design
mode method now. Wow this it great, THANK YOU KEN!!

I am going to continue on with this and I'll reply with the resulting
codes & queries. Although, I'm sure it will be exactly as you've generously
described.

I remain very grateful, thank you so much.

Best Regards,
-Matthew

--
http://njgin.aclink.org


Ken Sheridan said:
Matthew:

I'm not sure about thoughtful; rambling and incoherent might be a better
description <G>. Whatever, I hope it makes some sense and is helpful.

To judge by your web page I think we may be in broadly similar lines of work
(or have been in my case as I'm retired now). I was a Principal Planning
Officer with in the Environmental Planning Unit of Staffordshire County
Council here:


http://www.staffordshire.gov.uk/environment


Back to business:

First start with a simple aggregating query which returns the lowest price
per item per project:

SELECT tblProjects.projectid, itemnamedescription,
MIN(unitprice) AS lowestprice
FROM tblProjects INNER JOIN tblItems
ON tblProjects.projectid = tblItems.projectid
WHERE biddate >= DATEADD("m", -13, DATE())
GROUP BY tblProjects.projectid, itemnamedescription

Save this as qryProjectItemMinCosts say. Then create another query which
averages the costs per item from the above:

SELECT itemnamedescription,
AVG(lowestprice) AS avglowestprice
FROM qryProjectItemMinCosts
GROUP BY itemnamedescription;

Both of the above can easily be created in design view. For the first
select Totals from the View menu, group by tblProjects.projectid and
itemnamedescription, and select Min for the unitprice column. Access will
give the computed column a name like MinOfunitprice, which you can either
keep or change that to lowestprice. The second is based on the first query
and is done in much the same way, grouping by itemnamedescription and select
Avg for the lowest price column (or for the default MinOfunitprice column if
you haven't changed its name in the first query.

If you wish you can then join the second query above to any other query (or
a table) which returns other data about items, joining them on the
itemnamedescription columns. You can then include the columns from the query
to which you join the second query above, along with the avglowestprice
column from the second query above in the result table of the final query.
Bear in mind, though that if one query returned rows for all items and the
other excludes some then you'd need to use a LEFT OUTER JOIN to include all
items. Say for instance you want to list all items and the average lowest
cost for each over the last 13 months, but not all items were included in
projects over that period then you could join the tblItems table to the
second query above, which I'll call qryProjectItemAvgMinCosts, using a LEFT
JOIN (the OUTER is optional and usually omitted) like so:

SELECT DISTINCT tblItems.itemnamedescription, avglowestprice
FROM tblItems LEFT JOIN qryProjectItemAvgMinCosts
ON tblItems.itemnamedescription =
qryProjectItemAvgMinCosts.itemnamedescription;

Note the use of the DISTINCT option here. This is because tblItems has
multiple rows per item so without selecting distinct rows the query would
return duplicate rows for each item. Any items not represented in the last
13 months will have a Null avglowestprice column. Again this query can
easily be created in design view.

BTW from a design point of view you should really have another table with
just the one itemnamedescription column and with each item entered as one
row. This can then be related to your tblItems table with referential
integrity enforced, thus preventing any invalid itemnamedescription names
being entered in tblItems. With this extra table tblItems in fact models a
many-to-many relationship between items and projects. It would actually be
better to call the new table tblItems and the current one tblProjectitems.

Ken Sheridan
Stafford, England

:

You have no idea how nice it is to get a thoughtful response (or maybe you do
:)

Thank you very much Ken!

Yes, I will continue to attempt to learn as much as I can about SQL, as I
would like to enjoy this powerful tool. However, it can be a bit scary, as
you noted.
I believe you are exactly right, in the abstract or not, regarding the
smaller grouping linked together. Although, I'm not sure how to link them
together. And your example is almost perfect albeit hypothetical. Here is
what I mean by 'the average of the lowest priced item per project over the
last 13 months'.

Proj #1: 01/12/2008
Price A | Price B | Price C
Item1 $7.00 | $8.00 | $10.00

Proj #2: 08/23/2007
Price A | Price D | Price E
Item1 $7.00 | $6.00 | $5.00

Take the lowest prices for Item 1 within the last 13 months of projects
(ie-$7.00, $5.00). Then average the lowest values (7 + 5 = 12, 12/2 = $6.00)
The average of the lowest price for Item 1 = $6.00

I will continue to attempt what you described previously as links in the
chain and produce the necessary 'sets'. However I am uncertain as to how I
will link them together.

Best Regards,
-Matthew

--
http://njgin.aclink.org


:

Matthew:

Switching to design view in itself won't produce any errors, but it will
present things in a way where it can be difficult to perceive the logic which
is apparent in SQL and therefore make it all too easy to change the query so
that it does produce an error inadvertently. If you want to create complex
queries using subqueries it is really necessary to learn SQL. It might look
a little scary at first, but its actually quite straightforward and logical
once you get your teeth around it. The hard part is getting a mental picture
of the logical operations involved in squirreling the information you want
out of the data, but that's something you have to do however you design a
query..

I'm having difficulty grasping what you mean by 'lowest price for each item
per project averaged over the last 13 months'. There are two aggregation
operations involved here so this means there are two sets involved.
Consequently there has to be a set of a set, i.e. there must be multiple sets
of values from which a single set of lowest values is derived, from which in
turn an average value is derived. In fact as the end result also seems to be
a set of average values, being the averages for 'each' item', it sounds as
though the end result is in fact a set of sets of sets. I think you'd have
to provide an example, as I'm just not clear in my mind what sets we are
dealing with.

Although I'm not clear on the specifics here, I can nevertheless answer your
question in the abstract. Essentially what you would do in query design view
in this sort of scenario is to design a number of simple aggregating queries
to produce each set in the chain; firstly one to produce the set of lowest
prices over, grouping the query by something or other, then another based on
the first to produce the average of those prices grouping the query by
something else. The second grouping would most probably be a subset of the
first, e.g. you might group the first by project and item and return the
lowest (MIN) prices. Then in the then second you might group by item and
return the average (AVG) prices. You'd only need to restrict the first to
the last 13 months. This doesn't sound quite like what you describe as it
returns the average of the lowest prices per item per project (i.e. it allows
for multiple prices for the same item in one project), which is not the same
as 'average of the item with the lowest price per project'.

Ken Sheridan
Stafford, England

:

Ken I am very grateful for your continued correspondence, thank you Sir.

Query worked perfectly, it's not exactly the result I am working toward but
with your help I believe that I will be getting there soon. With the
statement you've provided it appears the returned value is the average of all
the prices for all the items. I am hoping to construct a statement that
returns the 13 month average of the item with the lowest price per project.
The lowest price for each item per project averaged over the last 13 months.
This will give an average of the lowest prices for each item.

I think that you were correct with your exiting statement last time,
serveral queries joined together into a single overall query.

You were also correct regarding the design vs sql view and query construction.

So, how can I build these queries if going into design view will probably
yield errors and my SQL logic and understanding is limited?

Best Regards,

-Matthew

--
http://njgin.aclink.org


:

I've just noticed that I correlated the innermost subquery with the outer
query unnecessarily. As the subquery is not now correlated there is no need
for the aliases TI1 and TI2. Also I assume the subquery would also need to
be restricted on biddate, so would need to join the tables in the same way as
the outer query. So the query should have been:

SELECT
itemnamedescription,
AVG(unitprice) AS [avg of unitprice],
MIN(unitprice) AS [min of unitprice],
MAX(unitprice) AS [max of unitprice],
COUNT(*) AS [count of tblItems],
(SELECT AVG(lowestprice)
FROM (SELECT MIN(unitprice) AS lowestprice
FROM tblProjects INNER JOIN tblItems
ON tblProjects.projectid = tblItems.projectid
WHERE biddate >= DATEADD("m", -13, DATE())
GROUP BY tblprojects.projectid))
AS [avg of lowest prices]
FROM tblProjects INNER JOIN tblItems
ON tblProjects.projectid = tblItems.projectid
WHERE biddate >= DATEADD("m", -13, DATE())
GROUP BY itemnamedescription;

To answer your question the query itself creates the 'lowestprice' column;
its not some existing object in the database. If you take a look at the
innermost subquery you'll see that MIN(unitprice) is returned as a column
named lowestprice, so the column contains the lowest unit prices grouped by
project for the current project returned by the outermost query. The average
of the values in this column is then returned by the first subquery with
AVG(lowestprice) and the column returned by this subquery is given the name
[avg of lowest prices]. As this is the average of the lowest prices for each
project the column will contain the same value in every row returned by the
query of course.

As regards the errors you will se there is no mention of 'False' in the
above query. False is actually a Boolean value, the opposite of True. I
suspect that a reference to a Boolean False has somehow crept into the query,
possibly as a result of your having opened it in query design view and then
amended it. When using subqueries its best to do everything in SQL view and
save it as such without ever going into design view.

On the basis of the table and column names in the original string expression
you posted you should be able to paste the above SQL into a blank query and
open it. I have in fact reconstructed your tables and tried it, and it opens
without any error. One thing I'm not quite clear about, however, is whether
by 'the average of the lowest priced item per project' you mean the average
the average of the lowest prices for any item per project, or the average of
the lowest price for the item returned in the current row of the query's
result table. I'd assumed the former and this is what the above query does.
If it is in fact the latter that's required I can't see how that could be
done in a single query because, having tried it, as far as I can see the
innermost subquery can't be correlated with the outer query. It would
probably need two (or maybe even three) queries joined in a single overall
query.

Ken Sheridan
Stafford, England

:

Wow Ken!

Thank you so much for your continued response.

I tried to load it into a button and execute but I got an error. I will
review this more closely, but I wanted to acknowledge and thank you for your
input as soon as possible. Thank you.

I get an error that reads false and then in another pop-it it reads,
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top