Nom de la table comme variable


171

J'essaye d'exécuter cette requête:

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

Cela produit l'erreur suivante:

Msg 1087, niveau 16, état 1, ligne 5

Doit déclarer la variable de table "@tablename".

Quelle est la bonne façon d'avoir un nom de table rempli dynamiquement?

Réponses:


131

Pour les requêtes statiques, comme celle de votre question, les noms de table et les noms de colonne doivent être statiques.

Pour les requêtes dynamiques, vous devez générer le SQL complet de manière dynamique et utiliser sp_executesql pour l'exécuter.

Voici un exemple de script utilisé pour comparer des données entre les mêmes tables de différentes bases de données:

requête statique:

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

puisque je veux facilement changer le nom de tableet schemaj'ai créé cette requête dynamique:

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

Étant donné que les requêtes dynamiques ont de nombreux détails qui doivent être pris en compte et qu'ils sont difficiles à maintenir, je vous recommande de lire: La malédiction et les bénédictions du SQL dynamique


103

Modifiez votre dernière déclaration en ceci:

EXEC('SELECT * FROM ' + @tablename)

C'est ainsi que je fais le mien dans une procédure stockée. Le premier bloc déclarera la variable et définira le nom de la table en fonction de l'année en cours et du nom du mois, dans ce cas TEST_2012OCTOBER. Je vérifie ensuite s'il existe déjà dans la base de données et je le supprime si c'est le cas. Ensuite, le bloc suivant utilisera une instruction SELECT INTO pour créer la table et la remplir avec des enregistrements d'une autre table avec des paramètres.

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

C'est la meilleure réponse.
ColinMac le

C'est la meilleure réponse car c'est la plus directement applicable au code existant de l'OP.
BH

37

Un peu en retard pour une réponse mais devrait aider quelqu'un d'autre:

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

15
QUOTENAME est important pour la sécurité. Merci.
Cihan Yakar

3
Mais comment retourner la valeur d'une telle requête? Par exemple COUNT(*)?
Suncatcher

35

Vous ne pouvez pas utiliser un nom de table pour une variable, vous devez le faire à la place:

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

14

Vous devrez générer le SQL dynamiquement:

declare @tablename varchar(50) 

set @tablename = 'test' 

declare @sql varchar(500)

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

exec (@sql)

8

Utilisez sp_executesqlpour exécuter n'importe quel SQL, par exemple

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

cet exemple est très utile.
Downhillski

0

En outre, vous pouvez utiliser ceci ...

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

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

1
Bienvenue dans Stack Overflow! Bien que ce code puisse résoudre la question, inclure une explication sur comment et pourquoi cela résout le problème aiderait vraiment à améliorer la qualité de votre publication et entraînerait probablement plus de votes à la hausse. N'oubliez pas que vous répondez à la question des lecteurs à l'avenir, pas seulement à la personne qui la pose maintenant. Veuillez modifier votre réponse pour ajouter des explications et donner une indication des limites et des hypothèses applicables. De l'avis
double-beep

0

vous devez utiliser le SQL dynamique SQL Server

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

SET @table = N'tableName';

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

Utilisez EXEC pour exécuter n'importe quel SQL

EXEC (@sql)

Utilisez EXEC sp_executesql pour exécuter n'importe quel SQL

EXEC sp_executesql @sql;

Utilisez EXECUTE sp_executesql pour exécuter n'importe quel SQL

EXECUTE sp_executesql @sql

-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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.