2017-03-06 1 views
-1

J'essaye de construire une requête qui place sa sortie dans une table. L'exec (@inloop_query) ne connaît pas une table déclarée d'avant. (cette partie entre le ------------------ Est-ce possible ou dois-je essayer de faire quelque chose qui ne fonctionne pas? s'il vous plaît conseiller.La requête T-SQL a mal tourné exec pas dans la table

(l'erreur que j'ai est la suivante: doit déclarer la variable de table « @inloop_table » gravité 15 Etat 2)

DECLARE @frame_db_name VARCHAR(max) 
DECLARE @frame_db_id INT 
DECLARE @frame_table TABLE (
    db_id INT , 
    names VARCHAR(max)) 
DECLARE @frame_count INT 
DECLARE @frame_count_max INT 
SET @frame_count = 1 
SET @frame_count_max = 0 
SELECT @frame_count_max = count (name) FROM sys.databases WHERE Name LIKE  'B%' and state_desc = 'online' 
INSERT INTO @frame_table SELECT database_id , name FROM sys.databases  WHERE Name LIKE 'B%' and state_desc = 'online' ORDER BY database_id 

DECLARE @inloop_query VARCHAR(max) 
DECLARE @Inloop_table TABLE (
    IL_SchemaName VARCHAR(max) , 
    IL_TableName VARCHAR(max) , 
    IL_IndexName VARCHAR(max) , 
    IL_IndexID INT , 
    IL_Fragment INT) 

IF @frame_count_max <= 0 
    PRINT '@count_max (<=0) = ' + CAST(@frame_count_max AS VARCHAR) 
ELSE 
    WHILE @frame_count <= @frame_count_max 
     BEGIN 
      SELECT @frame_db_name = names , @frame_db_id = db_id FROM  @frame_table WHERE db_id IN (SELECT TOP 1 db_id FROM @frame_table ORDER BY db_id) 
     PRINT '@count_max (>=0) = ' + CAST(@frame_count_max AS VARCHAR) 
     PRINT '@count = ' + CAST(@frame_count AS VARCHAR(max)) 
     PRINT 'current DB name = ' + CAST(@frame_db_name AS VARCHAR(max)) 
     PRINT 'current DB ID = ' + CAST(@frame_db_id AS VARCHAR(max)) 
     ------------------------------------------------------------ 
     SET @inloop_query = ' 
      USE ' + CAST(@frame_db_name AS VARCHAR(max)) + 
      ' INSERT INTO @inloop_table 
       SELECT SCHEMA_NAME(o.schema_id)  AS SchemaName, 
       OBJECT_NAME(a.object_id)  AS TableName, 
       i.name      AS IndexName, 
       a.index_id     AS IndexID, 
       convert(tinyint,a.avg_fragmentation_in_percent) AS [Fragment] 
      FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL,NULL, ''LIMITED'') AS a 
       INNER JOIN sys.indexes i ON i.index_id = a.index_id 
        AND i.object_id = a.object_id 
       INNER JOIN sys.objects o ON a.object_id = o.object_id 
      ORDER BY SchemaName, TableName, IndexID' 
     EXEC(@inloop_query) 
     ------------------------------------------------------------ 
     SET @frame_count = @frame_count + 1 
     DELETE FROM @frame_table WHERE db_id IN (SELECT TOP 1 db_id FROM @frame_table ORDER BY db_id) 
    END 

Répondre

0

@inloop_table est déclarée en dehors de votre @inloop_query, lorsque celui-ci est exécuté, il n'a aucune idée de cette variable. Comment utiliser une table réelle?

/* comment this out: 
DECLARE @inloop_query VARCHAR(max) 
DECLARE @Inloop_table TABLE (
    IL_SchemaName VARCHAR(max) , 
    IL_TableName VARCHAR(max) , 
    IL_IndexName VARCHAR(max) , 
    IL_IndexID INT , 
    IL_Fragment INT) 
*/ 
-- Create an auxiliary table 
CREATE TABLE InLoop_Table (
     IL_SchemaName VARCHAR(max) , 
    IL_TableName VARCHAR(max) , 
    IL_IndexName VARCHAR(max) , 
    IL_IndexID INT , 
    IL_Fragment INT 
); 
-- ... And use this table in your dynamic sql: 
SET @inloop_query = ' 
     USE ' + CAST(@frame_db_name AS VARCHAR(max)) + 
     ' INSERT INTO InLoop_Table ... 

-- Finally, clean up: 
DROP TABLE InLoop_Table; 
+0

Merci pour votre repley. Je pensais à ça aussi mais le problème est que je ne peux pas faire de tables sur la BD sur laquelle je travaille. J'ai aussi essayé (et cela a fonctionné) de faire la TABLE dans @inloop_query mais j'ai besoin de la sortie dans un autre temps. J'espère vraiment qu'il y a un moyen de faire en sorte que cela fonctionne. – hexedecimal

+0

Qu'en est-il alors de la solution de GarethD? –

0

La portée d'une variable de table est spécifique à un lot. Par conséquent, comme votre SQL dynamique s'exécute en tant que nouveau lot, il est hors de portée et n'est pas reconnu. Vous pourriez bien sûr le déclarer dans votre SQL dynamique, mais cela serait inutile car vous ne pourriez pas y accéder plus tard. Vous avez deux choix décents:

Vous pouvez placer l'insert en dehors du sql, par ex.

DECLARE @inloop_query NVARCHAR(MAX) = 'USE Master; SELECT 1, 2, 3;'; 
DECLARE @inloop_table TABLE (A INT, B INT, C INT); 

INSERT @inloop_table 
EXEC(@inloop_query); 

SELECT * FROM @inloop_table; 

Vous pouvez également utiliser une table temporaire plutôt qu'une variable de table. Une table temporaire a une portée de session, donc est encore reconnue avec EXEC():

CREATE TABLE #inloop_table (A INT, B INT, C INT); 
DECLARE @inloop_query NVARCHAR(MAX) = 'USE Master; INSERT #inloop_table SELECT 1, 2, 3;'; 

EXEC(@inloop_query); 

SELECT * FROM #inloop_table; 

Je recommande également d'utiliser un curseur correctement déclarée plutôt qu'une boucle WHILE itérer une variable de table. L'aspect clé ici étant correctement défini. Souvent, les gens utilisent simplement DECLARE .. CURSOR FOR SELECT.. et les options par défaut sont beaucoup plus lents et plus de mémoire que la consommation si vous dites le curseur que vous ne serez pas faire des mises à jour, ne seront pas régressent, etc.

DECLARE DBCursor CURSOR LOCAL STATIC FORWARD_ONLY READ_ONLY 
FOR 
SELECT database_id , name 
FROM sys.databases  
WHERE Name LIKE 'B%' and state_desc = 'online' 
ORDER BY database_id; 

OPEN DBCursor; 
FETCH NEXT FROM DBCursor INTO @frame_db_id, @frame_db_name; 

WHILE @@FETCH_STATUS = 0 
BEGIN 
    -- DO WHATEVER YOU NEED WITH EACH DB 

    FETCH NEXT FROM DBCursor INTO @frame_db_id, @frame_db_name; 
END 

CLOSE DBCursor; 
DEALLOCATE DBCursor; 

Un dernier commentaire, est que je perds toujours sp_executesql sur EXEC() et this article pretty much covers why, dans ce cas, cela ne fait pas beaucoup de différence, mais il est à noter.

+0

En fait nous pourrions ** accéder à ** une variable qui est déclarée dans le SQL dynamique si nous avons utilisé 'sp_executesql', mais c'est un peu impliqué: http://stackoverflow.com/a/42528011/1465748 –

+0

@GiorgosAltanis Cela ne veut pas appliquer aux variables de table, vous pouvez seulement produire des variables scalaires. Vous devrez donc sérialiser le résultat et le renvoyer en tant que variable scalaire, XML étant le candidat évident, alors vous devrez désérialiser votre XML dans une table pour faire n'importe quoi avec les résultats. Alors oui, c'est possible, mais c'est tellement d'efforts que ce n'est probablement jamais une meilleure option que d'utiliser une table temporaire. – GarethD

+0

Merci pour la clarification sur la variable de table, je suis d'accord que c'est une approche difficile. –