SQL Puzzle – Correct the Incorrect Query – Win Price Worth USD 1000 – Aggregate and Subquery

SQL Puzzle - Correct the Incorrect Query - Win Price Worth USD 1000 - Aggregate and Subquery puzzle It has been a while since we have seen a puzzle so let us do a small puzzle today. Actually, this is a very simple puzzle if you have worked with SQL Server for a while. I got an email from one of the readers from the blog post and he wanted me to correct the following query which he had written. Well, I have been working with SQL Server for over 19 years and I often do not remember the syntax and have to look up online. Here is a very simple puzzle for you which involves Aggregate and Subquery.

Important Update: The Workshop is now over and it was one of the landmark workshops, where I shared business secrets of successful Performance Tuning Expert. You can watch the recording of the workshop for a limited period of time.

Puzzle – Aggregate and Subquery

Here is the query when we run against the AdventureWorks database, it gives us an error. Remember AdventureWorks database is a sample database and you can download and install AdventureWorks over here.

USE AdventureWorks2014
GO
SELECT pch.StandardCost, p.ProductID
FROM Production.ProductCostHistory pch
INNER JOIN Production.Product p ON pch.ProductID = p.ProductID
WHERE pch.StandardCost > AVG(p.StandardCost)
GO

Now when you run the above query, it gives us the following error:

Msg 147, Level 15, State 1, Line 17
An aggregate may not appear in the WHERE clause unless it is in a subquery contained in a HAVING clause or a select list, and the column being aggregated is an outer reference.

Now here is your task, you need to help me to fix the above query. The hint to resolve the query is already in the blog post’s title and in the query error. You need to understand what my query is attempting to do and use the hint which is already pointed out to you. After you successfully fix the query it will produce the following results.

SQL Puzzle - Correct the Incorrect Query - Win Price Worth USD 1000 - Aggregate and Subquery correctresult

Rules

Well, there are no real rules, as long as you modify my query to get me above results. Please post your query in the comments section below before April 22, 2019. I will keep all the answers hidden till April 22, 2019 11:59:00 PM.

Winner

One Winner will get 1 free seat for my upcoming class, which is worth USD 1000.

SQL Server Performance Tuning Practical Workshop for EVERYONE
Date: April 23, 2019, Tuesday
8:00 AM Pacific Time | 11:00 AM Eastern Time

If you do not want to take part in the contest, you can directly register for the class here.

Update: April 23, 2019

The contest is closed and the winning Answer is here:

USE AdventureWorks2014
GO
SELECT pch.ProductID, pch.StartDate,pch.EndDate, pch.StandardCost, pch.ModifiedDate
FROM Production.ProductCostHistory pch
INNER JOIN Production.Product p ON pch.ProductID = p.ProductID
WHERE pch.StandardCost > p.StandardCost
GO

There is no need for the AVG on the StandardCost as there will be always a single entry for the product. Even if you change the query with group by + having you will still see the query uses the simple join.

Congratulations to the winner Nicholas Månsson. He is very excited to join the SQL Server Performance Tuning Practical Workshop for EVERYONE.

Everyone who participated you got to solve this difficult puzzle so indirectly you are a winner (of knowledge) as well. This was an intense competition with over 500 comments.

Reference: Pinal Dave (https://blog.sqlauthority.com)

Contest, SQL Scripts, SQL Server
Previous Post
SQL SERVER – Relating Unrelated Tables – A Question from Reader
Next Post
Puzzle – DELETE Qualified Rows From Multiple Tables – Win USD 1000 Worth Class

Related Posts

527 Comments. Leave new

  • Bruce Lomasky
    April 18, 2019 8:37 pm

    I do not have adventureworks on my computer, but it seems to be a simple solution (hope no typos!)
    USE AdventureWorks2014
    GO
    SELECT p.ProductID, startDate, enddate, pch.StandardCost, modifiedDate
    FROM Production.ProductCostHistory pch
    INNER JOIN Production.Product p ON pch.ProductID = p.ProductID
    INNER JOIN (Select AVG(p.StandardCost) as avgCost
    FROM Production.ProductCostHistory pch
    INNER JOIN Production.Product p ON pch.ProductID = p.ProductID) as Z on 1 = 1
    WHERE pch.StandardCost > Z.avgCost
    GO

    Reply
  • Hermann Cardenas
    April 18, 2019 8:38 pm

    — i looked at the tables and realized what you’re doing here LOL

    SELECT p.ProductID, pch.StartDate, pch.EndDate, pch.StandardCost, pch.ModifiedDate
    FROM Production.ProductCostHistory pch
    INNER JOIN Production.Product p ON pch.ProductID = p.ProductID
    WHERE pch.StandardCost > p.StandardCost

    Reply
  • happychicksza
    April 18, 2019 8:41 pm

    SELECT pch.StandardCost, p.ProductID
    FROM Production.ProductCostHistory pch
    INNER JOIN (select ProductID, avg(StandardCost) as StandardCost from Production.Product group by ProductID ) as p ON pch.ProductID = p.ProductID
    WHERE pch.StandardCost > p.StandardCost

    Reply
  • –SOLUTION 1 || AGGREGATE AND SUBQUERY
    SELECT productid,
    startdate,
    enddate,
    standardcost,
    modifieddate
    FROM (SELECT A.productid,
    A.startdate,
    A.enddate,
    A.standardcost,
    P.modifieddate,
    avg_cost
    FROM (SELECT p.productid,
    Avg(p.standardcost) AS AVG_COST,
    pch.standardcost,
    PCH.startdate,
    PCH.enddate
    FROM production.productcosthistory pch
    INNER JOIN production.product p
    ON pch.productid = p.productid
    GROUP BY p.productid,
    pch.standardcost,
    startdate,
    enddate) AS A
    JOIN production.product p
    ON A.productid = p.productid) AS B
    WHERE B.standardcost > B.avg_cost

    –SOLUTION 2 | HAVING CLAUSE
    SELECT P.productid,
    PCH.startdate,
    PCH.enddate,
    PCH.standardcost,
    PCH.modifieddate
    FROM production.productcosthistory AS PCH
    INNER JOIN production.product AS P
    ON pch.productid = p.productid
    GROUP BY pch.standardcost,
    p.productid,
    startdate,
    enddate,
    PCH.modifieddate
    HAVING pch.standardcost > Avg(p.standardcost)

    Reply
  • Saurabh Shakyawar
    April 18, 2019 8:47 pm

    SELECT p.ProductID,pch.StandardCost, pch.StartDate, pch.EndDate, pch.ModifiedDate
    FROM Production.ProductCostHistory pch, Production.Product p
    Where pch.ProductID = p.ProductID
    Group by p.ProductID,pch.StandardCost,pch.StartDate, pch.EndDate, pch.ModifiedDate
    Having pch.StandardCost > AVG(p.StandardCost)
    Order By p.ProductID ASC
    GO

    Reply
  • Harshit Solanki
    April 18, 2019 8:49 pm

    It was a simple test we just needed to use the group by clause for getting the results

    SELECT p.ProductID,pch.StartDate,pch.EndDate,pch.StandardCost,pch.ModifiedDate
    FROM Production.ProductCostHistory pch
    INNER JOIN Production.Product p ON pch.ProductID = p.ProductID
    group by pch.StartDate,pch.EndDate,pch.ModifiedDate,pch.StandardCost, p.ProductID having pch.StandardCost > AVG(p.StandardCost)

    But what in case these dates will change these query will be not correct.
    So i would say i have written query to just get to your resultset.

    Regards,
    Harshit

    Reply
  • Harshit Solanki
    April 18, 2019 8:50 pm

    I have posted the comment but its not visible.

    Reply
  • Jeremy Theesmond
    April 18, 2019 8:50 pm

    SELECT p.ProductID, PCH.StartDate, PCH.EndDate, pch.StandardCost, PCH.ModifiedDate
    FROM Production.ProductCostHistory pch
    INNER JOIN
    (
    SELECT AVG(p.StandardCost) AS STANDARDCOST, p.ProductID
    FROM Production.ProductCostHistory pch
    INNER JOIN Production.Product p ON pch.ProductID = p.ProductID
    GROUP BY P.ProductID
    ) P
    ON PCH.ProductID = P.ProductID
    WHERE pch.StandardCost > p.StandardCost

    Reply
  • Please review my answer. result is same as accepted.

    Reply
  • Hi,
    The having becomes the where clause.

    SELECT p.ProductID, pch.StartDate, pch.EndDate, pch.StandardCost, pch.ModifiedDate
    FROM Production.ProductCostHistory pch
    INNER JOIN Production.Product p ON pch.ProductID = p.ProductID
    GROUP BY p.ProductID, pch.StartDate, pch.EndDate, pch.StandardCost, pch.ModifiedDate
    HAVING pch.StandardCost > AVG(p.StandardCost)

    Reply
  • Daniel Malonza
    April 18, 2019 9:00 pm

    SELECT pch.StandardCost, p.ProductID
    FROM Production.ProductCostHistory pch
    INNER JOIN Production.Product p ON pch.ProductID = p.ProductID
    WHERE pch.StandardCost > (select AVG(StandardCost) from Production.Product)

    Reply
  • SELECT ProductID, StartDate, EndDate, StandardCost, ModifiedDate
    FROM Production.ProductCostHistory AS pch
    WHERE (StandardCost >
    (SELECT AVG(StandardCost) AS StdCost
    FROM Production.Product AS p
    WHERE (ProductID = pch.ProductID)))

    Reply
  • USE AdventureWorks2012_B
    GO

    SELECT PCH.[ProductID]
    , PCH.[StartDate]
    , PCH.[EndDate]
    , PCH.[StandardCost]
    , PCH.[ModifiedDate]
    FROM Production.ProductCostHistory pch
    INNER JOIN Production.Product p
    ON pch.ProductID = p.ProductID
    GROUP BY PCH.[ProductID]
    , PCH.[StartDate]
    , PCH.[EndDate]
    , PCH.[StandardCost]
    , PCH.[ModifiedDate]
    HAVING pch.StandardCost > AVG(p.StandardCost)
    GO

    Reply
  • ProductID StartDate EndDate StandardCost ModifiedDate
    707 2006-07-01 00:00:00.000 2007-06-30 00:00:00.000 13.8782 2007-06-30 00:00:00.000
    708 2006-07-01 00:00:00.000 2007-06-30 00:00:00.000 13.8782 2007-06-30 00:00:00.000
    711 2006-07-01 00:00:00.000 2007-06-30 00:00:00.000 13.8782 2007-06-30 00:00:00.000
    858 2006-07-01 00:00:00.000 2007-06-30 00:00:00.000 9.7136 2007-06-30 00:00:00.000
    859 2006-07-01 00:00:00.000 2007-06-30 00:00:00.000 9.7136 2007-06-30 00:00:00.000
    860 2006-07-01 00:00:00.000 2007-06-30 00:00:00.000 9.7136 2007-06-30 00:00:00.000

    Reply
  • Not sure about why the dates are in the final results but not the initial query SELECT. I get the right ProductID and StandardCost using this:

    USE AdventureWorks2008r2 –well, close to the original!
    GO
    ;with cte as
    (SELECT pch.StandardCost, p.ProductID, AVG(p.StandardCost) average
    FROM Production.ProductCostHistory pch INNER JOIN
    Production.Product p ON pch.ProductID = p.ProductID
    GROUP BY pch.StandardCost, p.ProductID)
    SELECT cte.ProductID, cte.StandardCost
    FROM cte
    WHERE cte.StandardCost > cte.average
    GO

    Reply
  • Varun Chandrasekaran
    April 18, 2019 9:06 pm

    This is my solution:

    SELECT p.ProductID, StartDate, EndDate, pch.StandardCost, pch.ModifiedDate
    FROM Production.ProductCostHistory pch
    INNER JOIN Production.Product p ON pch.ProductID = p.ProductID
    GROUP BY p.ProductID,pch.StandardCost,StartDate, EndDate,pch.ModifiedDate
    HAVING pch.StandardCost > AVG(p.StandardCost)

    Reply
  • USE AdventureWorks2014
    GO
    SELECT p.[ProductID]
    , pch.[StartDate]
    , pch.[EndDate]
    , pch.[StandardCost]
    FROM Production.[ProductCostHistory] pch
    INNER JOIN Production.[Product] p
    ON pch.[ProductID] = p.[ProductID]
    LEFT JOIN (
    SELECT [ProductID]
    , AVG([StandardCost]) AS [AverageProductStandardCost]
    FROM Production.[Product]
    GROUP BY [ProductID]
    ) a
    ON p.[ProductID] = a.[ProductID]
    WHERE pch.[StandardCost] > a.[AverageProductStandardCost]
    ORDER BY p.[ProductID];
    GO

    Reply
  • USE AdventureWorks2016
    GO
    SELECT p.ProductID,pch.StartDate, pch.EndDate, pch.StandardCost, pch.ModifiedDate
    FROM Production.ProductCostHistory pch
    INNER JOIN Production.Product p ON pch.ProductID = p.ProductID
    WHERE pch.StandardCost > (select AVG(StandardCost) from production.Product
    PP where p.ProductID = PP.ProductID)
    GO

    Reply
  • Resubmitting previous responses.

    It appears from the expected result set that you want to return product cost history records where the cost was higher at some point than it is now. So the two ways I approached this were:

    1. Using only the ProductCostHistory table:
    select pch.*
    from Production.ProductCostHistory pch
    inner join
    (
    select * from
    (select *, ROW_NUMBER() over (partition by ProductID order by StartDate desc) as rownum
    from Production.ProductCostHistory) as t1
    where t1.rownum = 1
    ) as t2 on t2.ProductID = pch.ProductID
    where pch.StandardCost > t2.StandardCost;

    2. Utilizing the fact that the Product table’s StandardCost column also represents the latest cost:
    select pch.*
    from Production.ProductCostHistory pch
    inner join Production.Product p on pch.ProductID = p.ProductID
    where pch.StandardCost > p.StandardCost;

    Reply
  • WITH AVGStandardCost (ProductID, AVGStandardCost)
    AS (SELECT p.ProductID, AVG(p.StandardCost)
    FROM Production.ProductCostHistory pch
    INNER JOIN Production.Product p ON pch.ProductID = p.ProductID
    GROUP BY p.ProductID)

    SELECT pch.ProductID, pch.StartDate, pch.EndDate, pch.StandardCost, pch.ModifiedDate
    FROM Production.ProductCostHistory pch
    INNER JOIN AVGStandardCost pp ON pch.ProductID = pp.ProductID
    WHERE pch.StandardCost > pp.AVGStandardCost

    Reply

Leave a Reply