以下のコードを使用して一時テーブルを動的に作成しようとしていますが、同じテーブルに対して2つのcreateステートメントを作成することはできません。これを以下のコードのような条件付きの方法で配置するための回避策はありますか?.
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
目的は、変数(@ var1)の値に基づいて列を含む#resultという名前のテーブルを最終的に作成することです
編集1:以下に示すように、これは動的SQLの使用に適した候補であるため、動的SQLスコープの外では#result
テーブルを使用できません。私は欲しい。
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
これは2008R2のタグが付いていることを知っています。正式にはサポートされていないので、おそらくアップグレードはあなたの将来にあります。 SQL Serverのバージョン> 2012になる場合は、次のようなコードを使用できます。
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
アイデアは dm_exec_describe_first_result_set を使用して、結果が生成する列とそのデータ型を決定することです。これを使用して、動的ALTER TABLEステートメントを生成し、動的SQLのスコープ外に作成されたベースの#tempテーブルにこれらの列を追加できます。
これにより、データの挿入と選択が非常に簡単になります。
この質問を思い出したとき、私はこれについてブログを書いていた。繰り返しになりますが、申し訳ありませんが、<2012はそれほど簡単なことではありません。
あなたは問題を間違った方法で考えています。
動的SQLを使用してできますこれを行うには、ステートメント内で結果の形状を定義しますが、その外部に一時テーブルを作成します。そのようです:
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
これは、EXECUTE
ステートメントによって生成された結果をINSERT
ステートメントへの入力として使用できるため機能します。これにより、動的SQLの外部で一時テーブルを構築できます。その後、必要に応じてアクセスできます。
動的SQL自体はネストできないため、INSERT EXEC
ステートメントを含めることができないなどの制限があります。詳細については、 INSERT(Transact-SQL) 、特にexecute_statement
のセクションを参照してください。
[〜#〜]編集[〜#〜]
上記のフィードバックとテストに基づいて、これは機能しなくなったようです(私はしばらく前にこれを行ったことを覚えていますが、たぶん私は年をとったばかりです!)。これを機能させるために私が考えることができる他の唯一のアプローチは [〜#〜] openrowset [〜#〜] を使用することです。一般的にはお勧めしませんが、要件を満たす唯一の方法のようです。
事前にサーバーが正しく構成されていることを確認する必要があります。
EXEC sp_configure 'Show Advanced Options', 1
GO
RECONFIGURE
GO
EXEC sp_configure 'Ad Hoc Distributed Queries', 1
GO
RECONFIGURE
GO
次に、以下を使用して一時テーブルを作成できます。
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;
これは非常にハックであり、個人的には行いません。動的SQLステートメント自体にできる限りプッシュすることを常に好みます。しかし、本当に他の方法でそれを行うことができない場合は、上記でうまくいきます。
さらに編集
2番目のステートメントはテーブルがすでに定義されていることを通知するため、上記はIF
ステートメントの本文内に配置すると機能しません。
提供したすべての情報に基づいて、グローバル一時テーブルを使用できない場合は、すべてのロジックを動的SQLにプッシュするか、CREATE TABLE
ステートメントなどを使用して中間テーブルを作成します。そうすると、やりたいことができなくなります。
@biju、コード全体を動的SQLに配置し、selectも配置する方法についてはどうでしょうか。その後、テーブルのスコープの問題はありません。
セッションスコープのテーブルではなくグローバル一時テーブルを使用します(#tableの代わりに## table)。これにより、動的SQLを使用して条件付きでテーブルを作成し、元のセッションでそれにアクセスできるようになります。
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
一時テーブル に関する詳細情報。
オプション1
各結果の戻り値の型は密接に関連しているように見えますが、もしそれがうまくいけば、いくつかのCASEステートメントですべてを同じテーブルに詰め込むことができます。
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
常に最後に2つのnull許容列がある同じ結果セットを返すことに問題がない場合は、この時点で問題ありません。変数@ var1に応じて相関する戻り値の型を返す必要がある場合は、ロジックは、次のように最後にSELECTステートメントをチェックします。
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
オプション2
あるいは、動的SQLソリューションを介して続行したい場合は、次のようにしてグローバル一時テーブルに一意の名前を生成することにより、グローバル一時テーブルの同時実行の問題を修正できます。
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')