248
SELECT logcount, logUserID, maxlogtm
   , DATEDIFF(day, maxlogtm, GETDATE()) AS daysdiff
FROM statslogsummary
WHERE daysdiff > 120

I get

"invalid column name daysdiff".

Maxlogtm is a datetime field. It's the little stuff that drives me crazy.

Lukasz Szozda
  • 162,964
  • 23
  • 234
  • 275
user990016
  • 3,208
  • 4
  • 20
  • 29

9 Answers9

280
SELECT
   logcount, logUserID, maxlogtm,
   DATEDIFF(day, maxlogtm, GETDATE()) AS daysdiff
FROM statslogsummary
WHERE ( DATEDIFF(day, maxlogtm, GETDATE() > 120)

Normally you can't refer to field aliases in the WHERE clause. (Think of it as the entire SELECT including aliases, is applied after the WHERE clause.)

But, as mentioned in other answers, you can force SQL to treat SELECT to be handled before the WHERE clause. This is usually done with parenthesis to force logical order of operation or with a Common Table Expression (CTE):

Parenthesis/Subselect:

SELECT
   *
FROM
(
   SELECT
      logcount, logUserID, maxlogtm,
      DATEDIFF(day, maxlogtm, GETDATE()) AS daysdiff
   FROM statslogsummary   
) as innerTable
WHERE daysdiff > 120

Or see Adam's answer for a CTE version of the same.

Jamie F
  • 23,189
  • 5
  • 61
  • 77
  • 24
    This is not possible directly, because chronologically, WHERE happens before SELECT, which always is the last step in the execution chain. REFER - http://stackoverflow.com/questions/356675/using-an-alias-in-a-where-clause – david blaine Dec 21 '12 at 01:26
  • afaik if the alias in the select is a correlated subquery this will work while the CTE solution won't. – Răzvan Flavius Panda May 31 '16 at 09:15
  • As Pascal mentioned in his answer here https://stackoverflow.com/a/38822328/282887, you can use HAVING clause which seems to work faster than subqueries. – Bakhtiyor Mar 26 '19 at 08:06
  • @Bakhtiyor The `HAVING` answer doesn't work in most SQL environments, including MS-SQL which this question is about. (In T-SQL, `HAVING` requires an aggregate function.) – Jamie F Mar 26 '19 at 13:45
  • I never knew you _couldn't_ reference aliases until I came across this issue just now. Love the workaround... does this have any major performance implications? – Mr. Boy Dec 10 '21 at 10:13
  • A subquery in the FROM clause is called _derived table_. – jarlh Sep 23 '22 at 13:24
94

If you want to use the alias in your WHERE clause, you need to wrap it in a sub select, or CTE:

WITH LogDateDiff AS
(
   SELECT logcount, logUserID, maxlogtm
      , DATEDIFF(day, maxlogtm, GETDATE()) AS daysdiff
   FROM statslogsummary
)
SELECT logCount, logUserId, maxlogtm, daysdiff
FROM LogDateDiff
WHERE daysdiff > 120
Adam Wenger
  • 17,100
  • 6
  • 52
  • 63
  • 2
    Do you happen to know how this fairs efficiency wise? Is there extra overhead using a CTE? – James Jul 04 '14 at 09:20
  • 5
    A CTE is just prettier syntax for a sub-query, so the performance would be similar to that. In my experience, the performance difference has not been something that has concerned me for operations like this, but it should be fairly simple to test it in your environment to see if your specific table/query is adversely affected with this vs. calling out the formula specifically in the where clause. I suspect you will not notice a difference. – Adam Wenger Jul 04 '14 at 17:17
  • CTEs are super nice until you try to use one as a subquery. i've had to resort to creating them as views to nest them. i consider this a serious SQL shortcoming – symbiont Aug 21 '17 at 13:55
22

The most effective way to do it without repeating your code is use of HAVING instead of WHERE

SELECT logcount, logUserID, maxlogtm
   , DATEDIFF(day, maxlogtm, GETDATE()) AS daysdiff
FROM statslogsummary
HAVING daysdiff > 120
Pascal
  • 2,377
  • 3
  • 25
  • 40
  • 2
    I think using `HAVING` on aliases is not standard (it does works on MySQL, though). Specifically, I think it does not work with SQL Server. – tokland Nov 25 '16 at 12:10
  • 5
    SQL Server: `[S0001][207] Invalid column name 'daysdiff'` – Vadzim Nov 02 '17 at 17:28
  • 7
    SQL Server: `[S0001][8121] Column 'day' is invalid in the HAVING clause because it is not contained in either an aggregate function or the GROUP BY clause.` – Vadzim Nov 02 '17 at 17:28
  • @Vadzim - I just get the error `Invalid column name ''.` Either way, it doesn't work in SQL Server... – dcafdg Oct 19 '22 at 23:30
11

If you don't want to list all your columns in CTE, another way to do this would be to use outer apply:

select
    s.logcount, s.logUserID, s.maxlogtm,
    a.daysdiff
from statslogsummary as s
    outer apply (select datediff(day, s.maxlogtm, getdate()) as daysdiff) as a
where a.daysdiff > 120
Roman Pekar
  • 107,110
  • 28
  • 195
  • 197
9

How about using a subquery(this worked for me in Mysql)?

SELECT * from (SELECT logcount, logUserID, maxlogtm
   , DATEDIFF(day, maxlogtm, GETDATE()) AS daysdiff
FROM statslogsummary) as 'your_alias'
WHERE daysdiff > 120
Shekhar Joshi
  • 958
  • 1
  • 13
  • 26
7

HAVING works in MySQL according to documentation:

The HAVING clause was added to SQL because the WHERE keyword could not be used with aggregate functions.

roier.rdz
  • 113
  • 1
  • 3
  • 3
    The question is about sql-server though – baltermia Jun 09 '21 at 09:12
  • 3
    despite the question being about SQL Server, this answer was very helpful for us using mysql who got to here by google! Google doesnt index the question tags, so thanks for this answer! – Tommy Oct 20 '21 at 17:50
5

You could refer to column alias but you need to define it using CROSS/OUTER APPLY:

SELECT s.logcount, s.logUserID, s.maxlogtm, c.daysdiff
FROM statslogsummary s
CROSS APPLY (SELECT DATEDIFF(day, s.maxlogtm, GETDATE()) AS daysdiff) c
WHERE c.daysdiff > 120;

DBFiddle Demo

Pros:

  • single definition of expression(easier to maintain/no need of copying-paste)
  • no need for wrapping entire query with CTE/outerquery
  • possibility to refer in WHERE/GROUP BY/ORDER BY
  • possible better performance(single execution)
Lukasz Szozda
  • 162,964
  • 23
  • 234
  • 275
2

For me, the simplest way to use an ALIAS in the WHERE clause is to create a sub-query and select from it instead.

Example:

WITH Q1 AS (
    SELECT LENGTH(name) AS name_length,
    id,
    name
    FROM any_table
)

SELECT id, name, name_length FROM Q1 WHERE name_length > 0
Ian Kenney
  • 6,376
  • 1
  • 25
  • 44
Michael Henry
  • 644
  • 9
  • 12
1

Came here looking something similar to that, but with a CASE WHEN, and ended using the where like this: WHERE (CASE WHEN COLUMN1=COLUMN2 THEN '1' ELSE '0' END) = 0 maybe you could use DATEDIFF in the WHERE directly. Something like:

SELECT logcount, logUserID, maxlogtm
FROM statslogsummary
WHERE (DATEDIFF(day, maxlogtm, GETDATE())) > 120
Taazar
  • 1,545
  • 18
  • 27
Scy
  • 11
  • 1