作为一个变量的表名

225

我正试图执行这个查询:

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

这会产生以下错误:

Msg 1087, Level 16, State 1, Line 5

必须声明表变量“@tablename”。

有什么正确的方法可以动态地填充表名?

10个回答

181

对于静态查询,比如你问题中的那个,表名和列名需要是静态的。

对于动态查询,应该动态生成完整的SQL,并使用sp_executesql来执行它。

这里是一个用于比较不同数据库中相同表格数据的脚本示例:

静态查询:

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

因为我希望能轻松更改表(table)模式(schema)的名称,所以我创建了这个动态查询:

declare @schema sysname;
declare @table sysname;
declare @query nvarchar(max);

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

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

EXEC sp_executesql @query

由于动态查询需要考虑许多细节并且难以维护,我建议您阅读:动态SQL的祝福与诅咒


5
在将对象名称组合到动态SQL语句中时,最佳实践是使用QuoteName()来避免出现奇怪的名称问题,例如带有空格的New Table或保留字From - HABO

128

将您的最后一条语句更改为以下内容:

EXEC('SELECT * FROM ' + @tablename)

这是我在存储过程中的做法。第一个块将声明变量,并根据当前年份和月份设置表名,例如TEST_2012OCTOBER。然后,我检查它是否已经存在于数据库中,如果存在则删除。接下来的块将使用SELECT INTO语句创建表并使用参数从另一个表填充记录。

--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''')

2
这是最好的答案。 - ColinMac
1
这是最佳答案,因为它最直接适用于原帖中的现有代码。 - B H
2
仅凭 EXEC('SELECT * FROM ' + @tablename) 是一个非常糟糕的想法,因为它很容易受到 SQL 注入攻击。 - Aaron Bertrand

44

使用:

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

22
QUOTENAME 对于安全性很重要。谢谢。 - Cihan Yakar
4
如何从这样的查询中返回值?比如 COUNT(*) - Suncatcher
1
@Suncatcher 你可以使用输出参数或将其作为结果集返回。为了展示如何将其读入变量并避免由于评论发布限制而在变量前加上“at”符号:声明nCount int,执行('select nCount = count(*) from ' + sTableName)。 - RowanPD

39

您不能将表名用作变量名称。取而代之,您需要执行以下操作:

DECLARE @sqlCommand varchar(1000)
SET @sqlCommand = 'SELECT * from yourtable'
EXEC (@sqlCommand)

16

你需要动态生成SQL内容:

declare @tablename varchar(50)

set @tablename = 'test'

declare @sql varchar(500)

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

exec (@sql)

8
使用 sp_executesql 来执行任何 SQL,例如:
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

1
这个例子非常有用。 - Downhillski

1
您需要使用SQL Server动态SQL:

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

SET @table = N'tableName';

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

使用EXEC执行任何SQL语句:

EXEC (@sql)

使用EXEC sp_executesql执行任何SQL语句:

EXEC sp_executesql @sql;

使用EXECUTE sp_executesql执行任何SQL:
EXECUTE sp_executesql @sql

0
Declare  @tablename varchar(50) 
set @tablename = 'Your table Name' 
EXEC('select * from ' + @tablename)

1
欢迎来到Stack Overflow!虽然这段代码可能解决了问题,但是包括解释它如何以及为什么解决了问题将有助于提高您的帖子质量,并可能导致更多的赞。请记住,您正在回答未来读者的问题,而不仅仅是现在提问的人。请[编辑]您的答案以添加解释并指出适用的限制和假设。来自审核 - double-beep
1
我喜欢这个答案的简洁性,它与问题所涉及的三行代码相同。我认为不需要解释。谢谢。 - can.do

-2

另外,您可以使用这个...

DECLARE @SeqID varchar(150);
DECLARE @TableName varchar(150);
SET @TableName = (Select TableName from Table);
SET @SeqID = 'SELECT NEXT VALUE FOR ' + @TableName + '_Data'
exec (@SeqID)

-3
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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接