Tinker Toy
Let’s say you have dynamic SQL that selects different different data based on some conditions.
Let’s also say that data needs to end up in a temp table.
Your options officially suck.
If you create the table outside dynamic SQL, you need to know which columns to use, and how many, to insert into the table.
You can’t do SELECT…INTO with an EXEC.
If you create the table inside dynamic SQL, you can’t use it outside the dynamic SQL.
But…
Altered Images
There’s a fun function in SQL Server 2012+, dm_exec_describe_first_result_set.
People mainly use it for stored procedures (I think?), but it can also work like this:
DECLARE @sql1 NVARCHAR(MAX) = N'SELECT TOP 10 * FROM dbo.Users AS u WHERE u.Reputation > @i'; DECLARE @sql2 NVARCHAR(MAX) = N'SELECT TOP 10 * FROM dbo.Posts AS p WHERE p.Score > @i'; SELECT column_ordinal, name, system_type_name FROM sys.dm_exec_describe_first_result_set(@sql1, NULL, 0); SELECT column_ordinal, name, system_type_name FROM sys.dm_exec_describe_first_result_set(@sql2, NULL, 0);
The results for the Users table look like this:
Don’t Judge Me
The best way I’ve found to do this is to use that output to generate an ALTER TABLE to add the correct columns and data types.
Here’s a dummy stored procedure that does it:
CREATE OR ALTER PROCEDURE dbo.dynamic_temp ( @TableName NVARCHAR(128)) AS BEGIN SET NOCOUNT ON; CREATE TABLE #t ( Id INT ); DECLARE @sql NVARCHAR(MAX) = N''; IF @TableName = N'Users' BEGIN SET @sql = @sql + N'SELECT TOP 10 * FROM dbo.Users AS u WHERE u.Reputation > @i'; END; IF @TableName = N'Posts' BEGIN SET @sql = @sql + N'SELECT TOP 10 * FROM dbo.Posts AS p WHERE p.Score > @i'; END; SELECT column_ordinal, name, system_type_name INTO #dfr FROM sys.dm_exec_describe_first_result_set(@sql, NULL, 0) ORDER BY column_ordinal; DECLARE @alter NVARCHAR(MAX) = N'ALTER TABLE #t ADD '; SET @alter += STUFF(( SELECT NCHAR(10) + d.name + N' ' + d.system_type_name + N',' FROM #dfr AS d WHERE d.name <> N'Id' ORDER BY d.column_ordinal FOR XML PATH(N''), TYPE ).value(N'.[1]', N'NVARCHAR(4000)'), 1, 1, N''); SET @alter = LEFT(@alter, LEN(@alter) - 1); EXEC ( @alter ); INSERT #t EXEC sys.sp_executesql @sql, N'@i INT', @i = 10000; SELECT * FROM #t; END; GO
I can execute it for either Users or Posts, and get back the results I want.
EXEC dbo.dynamic_temp @TableName = 'Users'; EXEC dbo.dynamic_temp @TableName = 'Posts';
So yeah, this is generally a pretty weird requirement.
It might even qualify as Bad Idea Spandex™
Thanks for reading!
Going Further
If this is the kind of SQL Server stuff you love learning about, you’ll love my training. I’m offering a 75% discount to my blog readers if you click from here. I’m also available for consulting if you just don’t have time for that and need to solve performance problems quickly.
I’ve done similar for dynamic pivots that I want to re-query.
1) Start with a long table (probably something like similar to EAV)
2) Create a temp table with a key column(s) (use whatever the pivot would be grouped by)
3) Get each attribute from the long table and create an ALTER statement to add columns by those names
4) Create a dynamic pivot insert into the wide temp table
5) Query the resulting table