185

I am trying to execute this query:

declare @tablename varchar(50)
set @tablename = 'test'
select * from @tablename

This produces the following error:

Msg 1087, Level 16, State 1, Line 5

Must declare the table variable "@tablename".

What's the right way to have the table name populated dynamically?

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
SoftwareGeek
  • 13,780
  • 18
  • 57
  • 78

10 Answers10

143

For static queries, like the one in your question, table names and column names need to be static.

For dynamic queries, you should generate the full SQL dynamically, and use sp_executesql to execute it.

Here is an example of a script used to compare data between the same tables of different databases:

Static query:

SELECT * FROM [DB_ONE].[dbo].[ACTY]
EXCEPT
SELECT * FROM [DB_TWO].[dbo].[ACTY]

Since I want to easily change the name of table and schema, I have created this dynamic query:

declare @schema varchar(50)
declare @table varchar(50)
declare @query nvarchar(500)

set @schema = 'dbo'
set @table = 'ACTY'

set @query = 'SELECT * FROM [DB_ONE].[' + @schema + '].[' + @table + '] EXCEPT SELECT * FROM [DB_TWO].[' + @schema + '].[' + @table + ']'

EXEC sp_executesql @query

Since dynamic queries have many details that need to be considered and they are hard to maintain, I recommend that you read: The curse and blessings of dynamic SQL

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
mdma
  • 54,185
  • 11
  • 85
  • 125
  • 1
    The best practice when assembling object names into dynamic SQL statements is to use [`QuoteName()`](https://docs.microsoft.com/en-us/sql/t-sql/functions/quotename-transact-sql) to avoid problems with odd names, e.g. `New Table` with a space or reserved words like `From`. – HABO Jan 09 '21 at 21:17
110

Change your last statement to this:

EXEC('SELECT * FROM ' + @tablename)

This is how I do mine in a stored procedure. The first block will declare the variable, and set the table name based on the current year and month name, in this case TEST_2012OCTOBER. I then check if it exists in the database already, and remove if it does. Then the next block will use a SELECT INTO statement to create the table and populate it with records from another table with parameters.

--DECLARE TABLE NAME VARIABLE DYNAMICALLY
DECLARE @table_name varchar(max)
SET @table_name =
    (SELECT 'TEST_'
            + DATENAME(YEAR,GETDATE())
            + UPPER(DATENAME(MONTH,GETDATE())) )

--DROP THE TABLE IF IT ALREADY EXISTS
IF EXISTS(SELECT name
          FROM sysobjects
          WHERE name = @table_name AND xtype = 'U')

BEGIN
    EXEC('drop table ' +  @table_name)
END

--CREATES TABLE FROM DYNAMIC VARIABLE AND INSERTS ROWS FROM ANOTHER TABLE
EXEC('SELECT * INTO ' + @table_name + ' FROM dbo.MASTER WHERE STATUS_CD = ''A''')
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Tim
  • 1,201
  • 1
  • 8
  • 2
38

Use:

CREATE PROCEDURE [dbo].[GetByName]
    @TableName NVARCHAR(100)
    AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;
    DECLARE @sSQL nvarchar(500);

    SELECT @sSQL = N'SELECT * FROM' + QUOTENAME(@TableName);

    EXEC sp_executesql @sSQL
END
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Atul Chaudhary
  • 1,005
  • 1
  • 13
  • 15
  • 15
    QUOTENAME is important for security. Thanks. – Cihan Yakar Apr 28 '15 at 10:05
  • 3
    But how to return value from such query? E.g. `COUNT(*)` ? – Suncatcher Mar 03 '18 at 09:57
  • @Suncatcher You could have an output parameter or return it as a result set. To show how to read it into a variable and without leading 'at' symbols on variables due to comment posting restrictions: declare nCount int Exec('select nCount = count(*) from ' + sTableName) – RowanPD Nov 14 '20 at 13:10
35

You can't use a table name for a variable. You'd have to do this instead:

DECLARE @sqlCommand varchar(1000)
SET @sqlCommand = 'SELECT * from yourtable'
EXEC (@sqlCommand)
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
dcp
  • 51,027
  • 19
  • 136
  • 157
16

You'll need to generate the SQL content dynamically:

declare @tablename varchar(50)

set @tablename = 'test'

declare @sql varchar(500)

set @sql = 'select * from ' + @tablename

exec (@sql)
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Paul Kearney - pk
  • 5,223
  • 23
  • 27
8

Use sp_executesql to execute any SQL, e.g.

DECLARE @tbl    sysname,
        @sql    nvarchar(4000),
        @params nvarchar(4000),
        @count  int

DECLARE tblcur CURSOR STATIC LOCAL FOR
   SELECT object_name(id) FROM syscolumns WHERE name = 'LastUpdated'
   ORDER  BY 1
OPEN tblcur

WHILE 1 = 1
BEGIN
   FETCH tblcur INTO @tbl
   IF @@fetch_status <> 0
      BREAK

   SELECT @sql =
   N' SELECT @cnt = COUNT(*) FROM dbo.' + quotename(@tbl) +
   N' WHERE LastUpdated BETWEEN @fromdate AND ' +
   N'                           coalesce(@todate, ''99991231'')'
   SELECT @params = N'@fromdate datetime, ' +
                    N'@todate   datetime = NULL, ' +
                    N'@cnt      int      OUTPUT'
   EXEC sp_executesql @sql, @params, '20060101', @cnt = @count OUTPUT

   PRINT @tbl + ': ' + convert(varchar(10), @count) + ' modified rows.'
END

DEALLOCATE tblcur
bzlm
  • 9,198
  • 5
  • 59
  • 86
ghgh
  • 91
  • 1
  • 1
2

You need to use the SQL Server dynamic SQL:

DECLARE @table     NVARCHAR(128),
        @sql       NVARCHAR(MAX);

SET @table = N'tableName';

SET @sql = N'SELECT * FROM ' + @table;

Use EXEC to execute any SQL:

EXEC (@sql)

Use EXEC sp_executesql to execute any SQL:

EXEC sp_executesql @sql;

Use EXECUTE sp_executesql to execute any SQL:

EXECUTE sp_executesql @sql
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Reza Jenabi
  • 2,706
  • 18
  • 27
1
Declare  @tablename varchar(50) 
set @tablename = 'Your table Name' 
EXEC('select * from ' + @tablename)
  • 1
    Welcome to Stack Overflow! While this code may solve the question, [including an explanation](//meta.stackexchange.com/q/114762) of how and why this solves the problem would really help to improve the quality of your post, and probably result in more up-votes. Remember that you are answering the question for readers in the future, not just the person asking now. Please [edit] your answer to add explanations and give an indication of what limitations and assumptions apply. [From Review](/review/low-quality-posts/26179331) – double-beep May 20 '20 at 11:04
  • I like the simplicity of this answer, and it has the same 3 lines the question addresses. I don't think explanation is needed. Thank you. – can.do Aug 04 '20 at 20:46
0

Also, you can use this...

DECLARE @SeqID varchar(150);
DECLARE @TableName varchar(150);
SET @TableName = (Select TableName from Table);
SET @SeqID = 'SELECT NEXT VALUE FOR ' + @TableName + '_Data'
exec (@SeqID)
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Disha Sojitra
  • 11
  • 1
  • 5
-1
Declare @fs_e int, @C_Tables CURSOR, @Table varchar(50)

SET @C_Tables = CURSOR FOR
        select name from sysobjects where OBJECTPROPERTY(id, N'IsUserTable') = 1 AND name like 'TR_%'
OPEN @C_Tables
FETCH @C_Tables INTO @Table
    SELECT @fs_e = sdec.fetch_Status FROM sys.dm_exec_cursors(0) as sdec where sdec.name = '@C_Tables'

WHILE ( @fs_e <> -1)
    BEGIN
        exec('Select * from ' + @Table)
        FETCH @C_Tables INTO @Table
        SELECT @fs_e = sdec.fetch_Status FROM sys.dm_exec_cursors(0) as sdec where sdec.name = '@C_Tables'
    END
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123