There is already an object name ‘#t’ in the database (curiosidades de tempdb)

En los foros de MSDN de SQL Server aparecen de vez en cuando preguntas que se salen de lo normal, o que al menos plantean situaciones curiosas. Una de ellas fue una titulada “There is already an object named #table in the database” en la que al tratar de ejecutar un script sencillo el motor devolvía ese mensaje de error. El siguiente script reproduce el problema:

USE tempdb
IF OBJECT_ID('tempdb..#t') IS NOT NULL
       DROP TABLE #t
IF DATEPART(DW, GETDATE()) = 1
       SELECT name
       INTO #t
       FROM sys.objects
ELSE IF DATEPART(DW, GETDATE()) <> 1
       SELECT name
       INTO #t
       FROM sys.databases

Al ejecutarlo, obtenemos el mensaje

Msg 2714, Level 16, State 1, Line 12
There is already an object named '#t' in the database.

En principio no tiene sentido encontrarnos con este error porque las condiciones que hemos puesto son incompatibles, así que el motor sólo debería crear una única tabla temporal llamada “t”. ¿Por qué entonces todo parece indicar que efectivamente se crea la misma tabla varias veces?

La explicación es más sencilla de lo que parece. Dentro de los múltiples cambios que se realizaron en la versión 2005, muchos de ellos fueron en la mejora del uso y administración de tempdb. En lo que aquí nos afecta, se cambió la forma en la que, en ciertas circunstancias, se gestiona la creación de tablas temporales: para evitar que el proceso de crear/eliminar/recrear tablas temporales se convierta en un cuello de botella, el motor se encarga de almacenar en caché el objeto recién creado. En el whitepaper «Working with tempdb in SQL Server 2005» se detalla esta y otras mejoras dentro del apartado “Improvements in SQL Server 2005”, y desde luego recomiendo su lectura.

La solución, tal como se detalla en ese documento, pasa por o bien crear la tabla explícitamente (CREATE TABLE #t…) o meter el código SELECT…INTO dentro de una ejecución dinámica (sp_executesql)