Alcance de tablas temporales y SQL dinámico en SQL Server
Comprende por qué una tabla temporal creada dentro de SQL dinámico desaparece y define quién debe crear, consumir y eliminar los datos intermedios.
Un procedimiento crea una tabla temporal mediante SQL dinámico y después intenta consultarla. La inserción funciona, pero la lectura informa de un objeto inexistente. La causa habitual es el alcance: una tabla exterior puede ser visible dentro del trabajo anidado, mientras una tabla creada dentro no sobrevive al terminar ese ámbito.
Crear la tabla en el ámbito que la necesita
El ejemplo crea #OuterWork en el lote llamador. El SQL dinámico inserta en ella y crea #InnerWork para su propio uso. Al finalizar, #OuterWork sigue disponible. Una consulta dinámica posterior contra #InnerWork produce el error 208, capturado por TRY/CATCH.
CREATE TABLE #OuterWork (ItemId int NOT NULL PRIMARY KEY);
EXEC sys.sp_executesql N'
INSERT #OuterWork (ItemId) VALUES (@Id);
CREATE TABLE #InnerWork (ItemId int NOT NULL);
INSERT #InnerWork VALUES (99);
SELECT ItemId AS VisibleInside FROM #InnerWork;',
N'@Id int', @Id = 7;
SELECT ItemId AS VisibleOutside FROM #OuterWork;
BEGIN TRY
EXEC sys.sp_executesql N'SELECT ItemId FROM #InnerWork;';
END TRY
BEGIN CATCH
SELECT ERROR_NUMBER() AS ErrorNumber,
ERROR_MESSAGE() AS ErrorMessage;
END CATCH;
DROP TABLE #OuterWork;
El primer resultado contiene 99, el segundo 7 y el último describe el objeto ausente. No hay una segunda conexión. Es una frontera dentro de la misma sesión, por lo que mantener abierta la conexión no conserva la tabla interna.
Si instrucciones posteriores necesitan las filas, crea la tabla con esquema explícito en el ámbito exterior y deja que el lote dinámico la llene. El contrato resulta visible: el llamador define estructura, el trabajo anidado produce datos y el llamador consume y elimina.
La misma idea se aplica a procedimientos almacenados. Una tabla temporal local creada por un procedimiento desaparece al terminarlo, aunque sus procedimientos anidados pueden usarla mientras exista. Un procedimiento puede utilizar una tabla del llamador, pero introduce una dependencia implícita. Documenta columnas y restricciones esperadas.
Separar valores, esquemas y conexiones
sp_executesql ejecuta un lote distinto. Las variables escalares normales del llamador no están disponibles automáticamente. Pasa valores como parámetros tipados, como @Id en el ejemplo. La visibilidad de una tabla temporal ya existente sigue otra regla; confundir ambas suele conducir a concatenación innecesaria.
Una variable de tabla tampoco se vuelve accesible al SQL dinámico por estar declarada cerca. Para una entrada de varias filas, un parámetro de tipo tabla puede proporcionar un contrato explícito. Para resultados intermedios modificables compartidos con trabajo dinámico, una tabla temporal exterior puede ser más sencilla.
Los nombres de columnas dinámicos son un problema diferente de los valores variables. Un esquema de salida distinto en cada solicitud complica las instrucciones estáticas posteriores y el cliente. Considera devolver directamente el resultado dinámico o representar atributos variables como filas con columnas estables. Valida y delimita correctamente cualquier identificador construido.
La tabla temporal local pertenece a una sesión física SQL. Dos llamadas de la aplicación no tienen garantizada la misma conexión del pool. Guardar una tabla para otra petición web es, por tanto, una estrategia de estado poco fiable aunque funcione en una prueba individual.
Evitar arreglos que introduzcan datos compartidos
Cambiar #Work por ##Work crea una tabla temporal global y modifica visibilidad y duración. No se limita a prolongar una variable local. Solicitudes concurrentes pueden colisionar por nombre o ver datos ajenos. Para trabajo entre peticiones, una tabla persistente con identificador único de tarea suele ofrecer aislamiento y limpieza más claros.
Evita reutilizar el mismo nombre temporal en ámbitos anidados. SQL Server puede mantener objetos locales homónimos en contextos distintos, con resolución difícil de mantener. Asigna nombres diferentes a responsabilidades diferentes en lugar de depender de una coincidencia de resolución.
El almacenamiento temporal tampoco es infinito. Filas anchas, índices y sesiones largas pueden retener espacio de tempdb. Elimina intermediarios grandes tras su último uso, especialmente si el procedimiento continúa con otras tareas. Considera además cómo un rollback afecta a los datos insertados.
Prueba el recorrido real de la aplicación: procedimientos anidados, SQL dinámico, errores y conexiones separadas del pool. La solución útil define propietario y duración de los datos. Con esa frontera clara, los errores de objeto dejan de parecer intermitentes y se convierten en un comportamiento comprobable.
Referencias técnicas: Microsoft Learn: CREATE TABLE and temporary scope · Microsoft Learn: sp_executesql.