J'essaie de créer une table temporaire de manière dynamique en utilisant le code ci-dessous, et je sais que nous ne pouvons pas avoir deux instructions create pour la même table. Existe-t-il une solution de contournement pour le mettre de manière conditionnelle comme le code ci-dessous.
CREATE PROC test @var1 CHAR(1)
as
BEGIN
IF(@var1 = X)
BEGIN
SELECT * INTO #result
FROM TABLE1
END
IF(@var1 = Y)
BEGIN
SELECT * INTO #result
FROM TABLE2
END
IF(@var1 = Z)
BEGIN
SELECT * INTO #result
FROM TABLE3
END
SELECT * FROM #result r
END
L'objectif est d'avoir enfin une table nommée #result avec des colonnes basées sur la valeur de la variable (@ var1)
Edit 1: Puisque c'est un bon candidat pour l'utilisation de SQL dynamique, comme indiqué ci-dessous, mais je ne pourrai pas utiliser le #result
table en dehors de la portée SQL de la dynamique, ce dont j'ai besoin.
CREATE PROC test @var1 CHAR(1)
as
BEGIN
-- USING dynamic sql
DECLARE @sql VARCHAR(MAX)
IF(@var1 = 'X')
BEGIN
SET @sql ='SELECT t.[name],t.[object_id],t.[principal_id] INTO #result
FROM sys.tables t'
END
IF(@var1 = 'Y')
BEGIN
SET @sql ='SELECT t.[name],t.[object_id],t.[principal_id],t.[schema_id] INTO #result
FROM sys.tables t'
END
IF(@var1 = 'Z')
BEGIN
SET @sql ='SELECT t.[name],t.[object_id],t.[principal_id],t.[schema_id],t.[parent_object_id] INTO #result
FROM sys.tables t'
END
EXEC (@sql)
SELECT * FROM #result r
END
Je sais que c'est étiqueté 2008R2. Comme cela n'est officiellement pas pris en charge, une mise à niveau est peut-être dans votre avenir. Si vous vous retrouvez sur une version de SQL Server> 2012, vous pouvez utiliser du code comme celui-ci:
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
L'idée est d'utiliser dm_exec_describe_first_result_set pour déterminer quelles colonnes votre résultat produit, ainsi que leurs types de données. Nous pouvons l'utiliser pour générer une instruction ALTER TABLE dynamique pour ajouter ces colonnes à une table #temp de base créée en dehors de la portée du SQL dynamique.
Cela rend l'insertion et la sélection de données assez faciles.
Je bloguais à ce sujet quand je me suis souvenu de cette question. Encore une fois, désolé, rien n'est aussi facile pour <2012.
Vous pensez mal au problème.
Vous pouvez utiliser du SQL dynamique pour ce faire - en définissant votre forme de résultat dans l'instruction, mais en créant la table temporaire à l'extérieur de celle-ci. Ainsi:
CREATE PROC test @var1 CHAR(1)
as
BEGIN
-- USING dynamic sql
DECLARE @sql VARCHAR(MAX)
IF(@var1 = 'X')
BEGIN
SET @sql ='SELECT t.[name]
,t.[object_id]
,t.[principal_id]
FROM sys.tables t'
END
IF(@var1 = 'Y')
BEGIN
SET @sql ='SELECT t.[name]
,t.[object_id]
,t.[principal_id]
,t.[schema_id]
FROM sys.tables t'
END
IF(@var1 = 'Z')
BEGIN
SET @sql ='SELECT t.[name]
,t.[object_id]
,t.[principal_id]
,t.[schema_id]
,t.[parent_object_id]
FROM sys.tables t'
END
INSERT INTO #result
EXEC (@sql);
SELECT * FROM #result r
END
Cela fonctionne car vous pouvez utiliser les résultats générés par l'instruction EXECUTE
comme entrée de l'instruction INSERT
, cela vous permet ensuite de créer votre table temporaire en dehors du SQL dynamique. Ensuite, vous pouvez y accéder au besoin.
Il existe des limitations, telles que le SQL dynamique lui-même ne peut pas contenir un INSERT EXEC
instruction car ils ne peuvent pas être imbriqués. Pour plus d'informations, voir INSERT (Transact-SQL) , en particulier la section sur execute_statement
.
[~ # ~] modifier [~ # ~]
Sur la base des commentaires et des tests ci-dessus, il semble que cela ne fonctionne plus (bien que je me souvienne de l'avoir fait il y a quelque temps - peut-être que je vieillis!). La seule autre approche à laquelle je peux penser pour que cela fonctionne est d'utiliser OPENROWSET . En général, je ne le recommanderais pas, mais il semble que ce soit le seul moyen de répondre à vos besoins.
Vous devrez vous assurer que votre serveur est correctement configuré au préalable:
EXEC sp_configure 'Show Advanced Options', 1
GO
RECONFIGURE
GO
EXEC sp_configure 'Ad Hoc Distributed Queries', 1
GO
RECONFIGURE
GO
Ensuite, vous pouvez utiliser ce qui suit pour créer la table temporaire:
SELECT *
INTO #results
FROM OPENROWSET(
'SQLNCLI',
'Server=(local);Trusted_Connection=yes;', -- replace with actual server/instance
'SELECT * FROM sys.tables'); -- replace with query
SELECT * FROM #results;
C'est extrêmement hacky et je ne le ferais pas personnellement, je préférerais toujours pousser autant que possible dans l'instruction SQL dynamique elle-même. Mais si vous ne pouvez vraiment pas le faire autrement, alors ce qui précède fonctionnera pour vous.
MODIFICATION SUPPLÉMENTAIRE
Ce qui précède ne fonctionnera pas lorsqu'il est placé dans le corps des instructions IF
car la deuxième instruction vous indiquera que la table a déjà été définie.
Sur la base de toutes les informations que vous avez fournies, à moins que vous ne puissiez utiliser des tables Global Temp, poussez toute la logique dans le SQL dynamique ou créez des tables intermédiaires comme avec le CREATE TABLE
déclaration. Alors ce que vous voulez faire ne peut pas être fait.
@biju, que diriez-vous de mettre tout le code en sql dynamique et de mettre également le select. Il n'y aura alors aucun problème de portée de table.
Utilisez une table temporaire globale au lieu d'une table de portée session (table ## au lieu de #table). Cela vous permettra de créer la table de manière conditionnelle à l'aide de SQL dynamique, puis d'y accéder dans la session d'origine.
DECLARE @var1 CHAR(1) = 'X'
IF(@var1 = 'X')
BEGIN
EXEC ('SELECT t.[name],t.[object_id],t.[principal_id] INTO ##results
FROM sys.tables t')
END
ELSE IF(@var1 = 'Y')
BEGIN
EXEC ('SELECT t.[name],t.[object_id],t.[principal_id],t.[schema_id] INTO ##results
FROM sys.tables t')
END
IF(@var1 = 'Z')
BEGIN
EXEC ('SELECT t.[name],t.[object_id],t.[principal_id],t.[schema_id],t.[parent_object_id] INTO ##results
FROM sys.tables t')
END
SELECT * FROM ##results
DROP TABLE ##results
Plus d'informations sur Tables temporaires .
Option 1
Il semble que vos types de retour pour chacun de vos résultats soient suffisamment liés pour que vous puissiez tout ranger dans le même tableau avec quelques instructions CASE, si vous êtes d'accord avec cela.
CREATE TABLE #results
(
[name] SYSNAME,
[object_id] INT,
[principal_id] INT,
[schema_id] INT,
[parent_object_id] INT
)
INSERT INTO #results
SELECT t.[name],
t.[object_id],
t.[principal_id],
CASE
WHEN @var1 = 'X' THEN null
WHEN @var1 = 'Y' OR @var1 = 'Z' THEN t.[schema_id]
END AS [schema_id],
CASE
WHEN @var1 = 'X' OR @var1 = 'Y' THEN null
WHEN @var1 = 'Z' THEN t.[parent_object_id]
END AS [parent_object_id]
FROM sys.tables AS t
SELECT r.[name], r.[object_id], r.[principal_id], r.[schema_id], r.[parent_object_id]
FROM #results as r
DROP TABLE #results
Si vous êtes d'accord pour toujours renvoyer le même jeu de résultats avec deux colonnes nullables à la fin, vous êtes bon à ce stade sinon si vous devez retourner le type de retour corrélatif en fonction de votre variable @ var1, alors vous pouvez simplement implémenter votre la logique vérifie votre instruction SELECT à la fin comme ceci:
IF (@var1 = 'X')
BEGIN
SELECT r.[name], r.[object_id], r.[principal_id]
FROM #results as r
END
IF (@var1 = 'Y')
BEGIN
SELECT r.[name], r.[object_id], r.[principal_id], r.[schema_id]
FROM #results as r
END
IF (@var1 = 'Z')
BEGIN
SELECT r.[name], r.[object_id], r.[principal_id], r.[schema_id], r.[parent_object_id]
FROM #results as r
END
Option 2
Si vous préférez continuer via une solution Dynamic SQL, vous pouvez également résoudre le problème de concurrence avec une table temporaire globale en générant un nom unique pour votre table temporaire globale en procédant comme suit:
DECLARE @UniqueTableId AS VARCHAR(50) = (SELECT CAST(NEWID() AS VARCHAR(50)))
EXEC ('SELECT t.[name],t.[object_id],t.[principal_id] INTO ##results_' + @UniqueTableId + '
FROM sys.tables t')