Tell me more ×
Database Administrators Stack Exchange is a question and answer site for database professionals who wish to improve their database skills and learn from others in the community. It's 100% free, no registration required.

What is the difference between a Common Table Expression (CTE) and a temp table? And when should I use one over the other?

CTE

WITH cte (Column1, Column2, Column3)
AS
(
    SELECT Column1, Column2, Column3
    FROM SomeTable
)

SELECT * FROM cte

Temp Table

SELECT Column1, Column2, Column3
INTO #tmpTable
FROM SomeTable

SELECT * FROM #tmpTable
share|improve this question
Related on SO: stackoverflow.com/a/698634/27535 – gbn Feb 16 '12 at 7:52

6 Answers

up vote 30 down vote accepted

This is pretty broad, but I'll give you as general and answer as I can.

CTEs...

  • Are unindexable (but can use existing indexes on referenced objects)
  • Cannot have constraints
  • Are essentially disposable VIEWs
  • Persist only until the next query is run
  • Can be recursive
  • Do not have dedicated stats (rely on stats on the underlying objects)

#Temp Tables...

  • Are real materialized tables that exist in tempdb
  • Can be indexed
  • Can have constraints
  • Persist for the life of the current CONNECTION
  • Can be referenced by other queries or subprocedures
  • Have dedicated stats generated by the engine

As far as when to use each, they have very different use cases. If you will have a very large result set, or need to refer to it more than once, put it in a #temp table. If it needs to be recursive, is dispoable, or is just to simplify something logically, a CTE is preferred.

Also, a CTE should never be used for performance. You will almost never speed things up by using a CTE, because, again, it's just a disposable view. You can do some neat things with them but speeding up a query isn't really one of them.

share|improve this answer
Given the big differences between these two, I think a more interesting question would be: What's the difference between a CTE and a derived table? – Nick Chammas Feb 15 '12 at 17:26
3  
@NickChammas - Not much. That is another inline table expression. You can't reference the same derived table more than once (i.e. you can't self join) and support for recursion. – Martin Smith Feb 15 '12 at 17:30
Of course, if what one does with the CTE is simple enough, it may be considerably faster than the temp table (e.g. the answer "streamed" through the other query components directly instead of having to actually write out a table to disk) – Billy ONeal Feb 15 '12 at 21:45
If it's small enough not to matter if you have stats or indexes then I wouldn't expect it to be any faster to use a CTE since a #temp table would also be held in memory. – JNK Feb 15 '12 at 21:47
If you need a CTE and want to reuse the CTE results (usually recursion in my scenarios), you could always insert the results into a temp table or table variable. So it is easy to use one or the other or both. – Ryan Feb 16 '12 at 15:26
show 2 more comments

CTE

Read more on MSDN

A CTE creates the table being used in memory, but is only valid for the specific query following it. When using recursion, this can be an effective structure, but bear in mind that it will need to be recreated everytime it's needed.

You might also consider here a table variable. This is used as a temp table is used, but is also in-memory only, but can be used multiple times without needing to be recreated every time. Also, if you need to persist a few records now, add a few more records after the next select, add a few more records after another op, then return just those handful of records, then this is a handy in-memory structure.

Temp Table

Read more on MSDN - Scroll down about 40% of the way

A temp table is literally a table created on disk, just in a specific database that everyone knows can be deleted. It is the responsibility of a good dev to destroy those tables when they are no longer needed, but a DBA can also wipe them.

Temporary tables come in two variety: Local and global. In terms of MS Sql Server you use a #tableName designation for local, and ##tableName designation for global (note the use of a single or double # as the identifying characteristic).

Notice that with temp tables, as opposed to table variables or CTE, you can apply indexes and the like, as these are legitimately tables in the normal sense of the word.


Generally I would use temp tables for longer or larger queries, and CTEs or table variables if I had a small dataset already and wanted to just quickly script up a bit of code for something small.

share|improve this answer
3  
The CTE is not materialised as a table in memory. It is just a way of encapsulating a query definition. In the case of the OP it will be inlined and the same as just doing SELECT Column1, Column2, Column3 FROM SomeTable – Martin Smith Feb 15 '12 at 16:55
4  
Also CTEs are not read only. They have the same semantics as updatable views. Updating or deleting from them affects the base tables as the CTE definition just gets expanded out into the query and they do not exist as objects in their own right. – Martin Smith Feb 15 '12 at 17:02
huh, I was led to believe from my DBA that it was treated like a materialised view, non-indexable and heavy. Removed the part about read-only, thanks @MartinSmith – jcolebrand Feb 15 '12 at 17:03
Most of the time they do not get materialised up front, which is why this returns no rows WITH T(X) AS (SELECT NEWID())SELECT * FROM T T1 JOIN T T2 ON T1.X=T2.X, also check the execution plans. Though sometimes it is possible to hack the plan to get a spool. There is a connect item requesting a hint for this. – Martin Smith Feb 15 '12 at 17:08
2  
Also sorry but table variables aren't in memory necessarily either. They are created in tempdb too and have exactly the same page structure as #temp tables. For both of them the data pages may or may not get flushed to disc. – Martin Smith Feb 15 '12 at 17:12
show 9 more comments

A CTE may be called repeatedly within a query and is evaluated every time it is referenced - this process can be recursive. If it is just referred once then it behaves much like a sub-query, although CTEs can be parameterised.

A temporary table is physically persisted, and may be indexed. In practice the query optimiser may also persist intermediate join or sub-query results behind the scenes, such as in spool operations, so it is not strictly true that the results of CTEs are never persisted to disk.

IIRC table variables (on the other hand) are always in-memory structures.

share|improve this answer

Temp table is a real object in tempdb, but cte is only a kind of wrapper around complex query to simplify syntax of organize recursion in one step.

share|improve this answer

Another behavior to note is that while a CTE can be referenced multiple times in other CTEs, CTEs can only be used to return data once.

Example:

--this first call will return data
select * from my_CTE

--this second call will throw an error
select * from my_CTE

Temp Tables can be used to return data as many times as needed

--this first call will return data
select * from #my_temp_table

--this second call will also return data
select * from #my_temp_table
share|improve this answer
Thanks, but that was already listed multiple times in other answers." – Rachel Feb 15 '12 at 20:07

@JNK; I'd say it's a bit disengenuous to say that CTEs don't improve performance. In the context of CTEs versus temp tables, I've just finished removing a swathe of junk from a suite of stored procs because some doofus must've thought there was little or no overhead to using temp tables. I shoved the lot into CTEs, except those which were legitimately going to be re-used throughout the process. I gained about 20% performance by all metrics. I then set about removing all the cursors which were trying to implement recursive processing. This was where I saw the greatest gain. I ended up slashing response times by a factor of ten.

I guess I'm agreeing with you in part, because you state that CTEs and temp tables have very different use cases. I just want to emphasise that, while not a panacea, the comprehension and correct use of CTEs can lead to some truly stellar improvements in both code quality/maintainability and speed. Since I got a handle on them, I see temp tables and cursors as the great evils of SQL processing. I can get by just fine with table variables and CTEs for almost everything now. My code is cleaner and faster.

share|improve this answer
1  
Don't think "disingenuous" is the correct word here. "Not candid or sincere, typically by pretending that one knows less about something than one really does.". BTW why are you happy to use table variables but not #temp tables? – Martin Smith Feb 17 '12 at 13:16
I don't know what dictionary you pulled that definition from, but I used "disingenuous" in the sense of a misleading statement. The point is that to write, in bold type, on a public web page, that a CTE should never be used for performance is misleading, if not downright incorrect. I illustrated that CTEs are in fact staggeringly effective depending on the task. I know the discussion is really about temp tables. I just didn't want the OP to go off thinking "All CTE bad. Avoid CTE". – Mel Padden Feb 20 '12 at 8:57
To answer the second part of your question, I'm perfectly happy to use both. I just have very little patience for the kind of fire-and-forget thinking that accompanies copious or cargo-cult use of one or the other solution. I don't want to make assumptions about which side you're on but you seem pretty hot on temp tables. – Mel Padden Feb 20 '12 at 9:03
So, to sum up, my decision-making process goes like this: Use a CTE first, if it needs to be visible after the current scope then move to a table var, if I need a lot of records or if it needs to be visible outside the proc use a temp table. If I'm writing a UDF I can't use a temp table anyway. – Mel Padden Feb 20 '12 at 9:07
No, you illustrated that using CTEs correctly is better than misusing #temp tables. If you are querying a CTE then the same query will ALWAYS be faster if run against a materialized #temp table with appropriate indexes. I seldom use always when talking about SQL but there's no ambiguity there. – JNK Mar 6 '12 at 18:27
show 10 more comments

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.