40

I'm trying to use a calculated column in a where clause.

I've been trying everything from CROSS APPLY, to sub-query select but it does not give me anything near what I need.

My query so far:

SELECT p.Code, c.AccountNumber, Sales = (SUM(p.UnitPrice) * SUM(od.QtyShipped)) FROM [dbo].Customer c 
    LEFT JOIN [dbo].OrderHeader oh ON oh.CustomerId = c.Id 
    LEFT JOIN [dbo].OrderDetail od ON od.OrderHeaderId = oh.Id
    LEFT JOIN [dbo].Product p ON p.Id = od.ProductId
WHERE Sales > 100
GROUP BY p.Code, c.AccountNumber, Sales

This does not work, as 'Sales' is an invalid column

Surendra
  • 25
  • 8
JadedEric
  • 1,943
  • 2
  • 26
  • 49
  • 7
    Can you use `HAVING (SUM(p.UnitPrice) * SUM(od.QtyShipped)) > 100` instead of the `WHERE`? (see http://www.w3schools.com/sql/sql_having.asp) – Jonny Dec 16 '14 at 11:18

3 Answers3

49

Using Derived Columns in a predicate

You'll need to wrap the inner query in a derived table or CTE in order to be able to use derived columns in the WHERE clause (Also, note SUM() is specified just once, using the results of the multiplication):

SELECT x.Code, x.AccountNumber, x.Sales
FROM
(
  SELECT p.Code, c.AccountNumber, SUM(p.UnitPrice *od.QtyShipped) AS Sales 
  FROM [dbo].Customer c 
      LEFT JOIN [dbo].OrderHeader oh ON oh.CustomerId = c.Id 
      LEFT JOIN [dbo].OrderDetail od ON od.OrderHeaderId = oh.Id
      LEFT JOIN [dbo].Product p ON p.Id = od.ProductId
  GROUP BY p.Code, c.AccountNumber
) AS x
WHERE x.Sales > 100;

Repeating the Derived Column in a HAVING clause

As per @Jonny's comment, the other way is not to DRY up the calculated column, but to instead repeat the calculation. Use HAVING instead of WHERE after a GROUP BY has been applied.

SELECT p.Code, c.AccountNumber, SUM(p.UnitPrice *od.QtyShipped) AS Sales 
FROM [dbo].Customer c 
  LEFT JOIN [dbo].OrderHeader oh ON oh.CustomerId = c.Id 
  LEFT JOIN [dbo].OrderDetail od ON od.OrderHeaderId = oh.Id
  LEFT JOIN [dbo].Product p ON p.Id = od.ProductId
GROUP BY p.Code, c.AccountNumber
HAVING SUM(p.UnitPrice * od.QtyShipped) > 100;

In either case, as per comments below, note that the calculated expression is SUM(p.UnitPrice * od.QtyShipped) and not SUM(p.UnitPrice) * SUM(od.QtyShipped).

StuartLC
  • 104,537
  • 17
  • 209
  • 285
  • hi stuart, i get: Msg 130, Level 15, State 1, Line 4 Cannot perform an aggregate function on an expression containing an aggregate or a subquery. sql is not my thing so have no idea what this message means :$ – JadedEric Dec 16 '14 at 11:27
  • The Sales computation should just be this : `SUM(p.UnitPrice * od.QtyShipped)` – StuartLC Dec 16 '14 at 11:28
  • i see what you did, thank you. and so i'm learning as well :). thank you. – JadedEric Dec 16 '14 at 11:29
  • What is the performance effect if you repeat the derived column in a having clause compared to wrapping it? – kamaci Apr 26 '22 at 01:51
10

You can use the common table expression for this

;WITH CTE AS
    (
    SELECT p.Code, c.AccountNumber, Sales = (SUM(p.UnitPrice) * SUM(od.QtyShipped)) FROM [dbo].Customer c 
        LEFT JOIN [dbo].OrderHeader oh ON oh.CustomerId = c.Id 
        LEFT JOIN [dbo].OrderDetail od ON od.OrderHeaderId = oh.Id
        LEFT JOIN [dbo].Product p ON p.Id = od.ProductId
    
    GROUP BY p.Code, c.AccountNumber, Sale
    
    )
    
SELECT *
FROM CTE WHERE CTE.Sales>100
Manngo
  • 14,066
  • 10
  • 88
  • 110
mindbdev
  • 404
  • 3
  • 8
-5

If it's a calculated column you can use "HAVING".

SELECT p.Code, c.AccountNumber, Sales = (SUM(p.UnitPrice) * SUM(od.QtyShipped)) FROM [dbo].Customer c 
    LEFT JOIN [dbo].OrderHeader oh ON oh.CustomerId = c.Id 
    LEFT JOIN [dbo].OrderDetail od ON od.OrderHeaderId = oh.Id
    LEFT JOIN [dbo].Product p ON p.Id = od.ProductId
GROUP BY p.Code, c.AccountNumber, Sales
HAVING SALES > 100;
Muran
  • 67
  • 3
  • 7
    Sadly, not. You can use a column alias in the `ORDER BY`, but not in a `HAVING` - you'll either need to repeat the full calc in the HAVING (like @Jonny's comment), or wrap the computed column. [Demo Here](http://sqlfiddle.com/#!6/a8edb/2) – StuartLC Dec 16 '14 at 11:27