Use a calculated column in a where clause
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)
.
Reuse calculated column in WHERE clause
There is no way to reuse the calculated field on the same level SELECT
. You will need to nest it in order to use the alias.
SELECT field1
, calc_field
FROM (
SELECT field1
, CONCAT (field2, field3) AS calc_field
FROM MyTable
) tbl
WHERE calc_field LIKE 'A%'
This is because of the order in which clauses are executed in a SQL query. As you can see in the way the clauses are listed, the SELECT
clause, where the alias is generated, is executed after the WHERE
clause.
Thus, the alias is not "visible" in the WHERE
clause, because the alias is generated after the WHERE
is applied.
Reference alias (calculated in SELECT) in WHERE clause
You can't reference an alias except in ORDER BY because SELECT is the second last clause that's evaluated. Two workarounds:
SELECT BalanceDue FROM (
SELECT (InvoiceTotal - PaymentTotal - CreditTotal) AS BalanceDue
FROM Invoices
) AS x
WHERE BalanceDue > 0;
Or just repeat the expression:
SELECT (InvoiceTotal - PaymentTotal - CreditTotal) AS BalanceDue
FROM Invoices
WHERE (InvoiceTotal - PaymentTotal - CreditTotal) > 0;
I prefer the latter. If the expression is extremely complex (or costly to calculate) you should probably consider a computed column (and perhaps persisted) instead, especially if a lot of queries refer to this same expression.
PS your fears seem unfounded. In this simple example at least, SQL Server is smart enough to only perform the calculation once, even though you've referenced it twice. Go ahead and compare the plans; you'll see they're identical. If you have a more complex case where you see the expression evaluated multiple times, please post the more complex query and the plans.
Here are 5 example queries that all yield the exact same execution plan:
SELECT LEN(name) + column_id AS x
FROM sys.all_columns
WHERE LEN(name) + column_id > 30;
SELECT x FROM (
SELECT LEN(name) + column_id AS x
FROM sys.all_columns
) AS x
WHERE x > 30;
SELECT LEN(name) + column_id AS x
FROM sys.all_columns
WHERE column_id + LEN(name) > 30;
SELECT name, column_id, x FROM (
SELECT name, column_id, LEN(name) + column_id AS x
FROM sys.all_columns
) AS x
WHERE x > 30;
SELECT name, column_id, x FROM (
SELECT name, column_id, LEN(name) + column_id AS x
FROM sys.all_columns
) AS x
WHERE LEN(name) + column_id > 30;
Resulting plan for all five queries:
Referencing a calculated column in the where clause SQL
In addition to Aaron's answer, you could use a common table expression:
;with cte_FreeDaysRemaining as
(
select
frdFreedays - DateDiff(dd,conReceiptToStock,GetDate()) As FreeDaysRemaining
--, more columns
from yourtable
)
select
FreeDaysRemaining
--, more columns
from cte_FreeDaysRemaining
where FreeDaysRemaining <= @intFreeDays
Use a calculated field in the where clause
Logically, the select
clause is one of the last parts of a query evaluated, so the aliases and derived columns are not available. (Except to order by
, which logically happens last.)
Using a derived table is away around this:
select *
from (SELECT a, b, a+b as TOTAL FROM (
select 7 as a, 8 as b FROM DUAL
UNION ALL
select 8 as a, 8 as b FROM DUAL
UNION ALL
select 0 as a, 0 as b FROM DUAL)
)
WHERE TOTAL <> 0
;
How can I reuse a calculated CASE column in the WHERE clause?
In SQL Server you are unable to use a calculated column in the WHERE
clause, only in the ORDER BY
clause.
So you either need a sub-query of some form or you have to repeat the calculation. CROSS APPLY
is a neat way to accomplish this.
SELECT T1.id, X.AmberRed
FROM db.tbl1 T1
CROSS APPLY (VALUES (
CASE WHEN T1.id LIKE 'A_SEQ%' THEN 'amber'
WHEN TestReason = 'itf' THEN 'red'
ELSE NULL END
)) AS X (AmberRed)
WHERE X.AmberRed IS NOT NULL;
But a simple sub-query will also do the job
SELECT X.id, X.AmberRed
FROM (
SELECT T1.id
, CASE WHEN T1.id LIKE 'A_SEQ%' THEN 'amber'
WHEN TestReason = 'itf' THEN 'red'
ELSE NULL END
FROM db.tbl1 T1
) X
WHERE X.AmberRed IS NOT NULL;
Or you can even just repeat the expression if its simple:
SELECT T1.id
, CASE WHEN T1.id LIKE 'A_SEQ%' THEN 'amber'
WHEN TestReason = 'itf' THEN 'red'
ELSE NULL END
FROM db.tbl1 T1
WHERE CASE WHEN T1.id LIKE 'A_SEQ%' THEN 'amber'
WHEN TestReason = 'itf' THEN 'red'
ELSE NULL END IS NOT NULL;
SQL Server Reference a Calculated Column
No.
All the results of a single row from a select are atomic. That is, you can view them all as if they occur in parallel and cannot depend on each other.
If you're referring to computed columns, then you need to update the formula's input for the result to change during a select.
Think of computed columns as macros or mini-views which inject a little calculation whenever you call them.
For example, these columns will be identical, always:
-- assume that 'Calc' is a computed column equal to Salaray*.25
SELECT Calc, Salary*.25 Calc2 FROM YourTable
Also keep in mind that the persisted
option doesn't change any of this. It keeps the value around which is nice for indexing, but the atomicity doesn't change.
Calculated column in where-clause - performance
You can use HAVING to filter on a computed column:
SELECT a,b,c,(a*b+c) AS d, n FROM table
HAVING d > n
ORDER by d
Note that you need to include n
in the SELECT clause for this to work.
Oracle SQL Calculated Column - How do I reference a calculated column in another calculation in the same query?
In other Stack Overflow questions/responses, CROSS APPLY has come up. This is not working for me, since I have 10-20 calculated columns I need to create (all of which reference the other calculated columns).
CROSS APPLY
could be chained:
SELECT t.*, s1.val1, s2.val2, s3.val3
FROM tab t
CROSS APPLY (SELECT t.val1 * t.val2 AS val3 FROM dual) s1
CROSS APPLY (SELECT s1.val3 * 100 AS val4 FROM dual) s2
CROSS APPLY (SELECT s2.val4 * 1000 AS val5 FROM dual) s3
--...
All you need is to make sure that you are referencing objects in correct order.
Related:
How to use a calculated column to calculate another column in the same view
Related Topics
Merge Overlapping Time Intervals, How
Detect SQL Island Over Multiple Parameters and Conditions
Postgresql Gin Index Slower Than Gist for Pg_Trgm
Product with Multiple Category Type Database Schema
There Are No Primary or Candidate Keys in the Referenced Table
Difference Between Inner Join and Where in Select Join SQL Statement
Implications of Nvarchar (50) VS Nvarchar (Max)
Does SQLite Support Scope_Identity
Do I Need to Call Rollback If I Never Commit
JSON Without Array Wrapper on Lower Levels
Any Performance Impact in Oracle for Using Like 'String' VS = 'String'
How to Find the Number of Days Between Two Dates
Why No "Select Foo.* ... Group by Foo.Id" in Postgres
Xml Query() Works, Value() Requires Singleton Found Xdt:Untypedatomic