web-dev-qa-db-ja.com

ブロックがプロシージャで一時テーブルの作成に失敗した場合

私はこれを手順でやろうとしています:

DECLARE @a bit = 1;
BEGIN
    SELECT * INTO #aTemp FROM OPENROWSET( ... );

    IF @a = 0
    BEGIN
        SELECT ... INTO #bTemp FROM #aTemp;
    END
    ELSE
    BEGIN
        SELECT ... INTO #bTemp FROM #aTemp;
    END
END

エラーが発生します:

Msg 2714, Level 16, State 1, Line 10
There is already an object named '#bTemp' in the database.

なぜこれが起こっているのですか?

更新

提案されたようにDROPステートメントを追加しようとしました here がまだ機能しません。

DECLARE @a bit = 1;
BEGIN
    SELECT * INTO #aTemp FROM OPENROWSET( ... );

    IF @a = 0
    BEGIN
        IF OBJECT_ID('[tempdb]..#bTemp') IS NOT NULL
        BEGIN
            DROP TABLE #bTemp;
        END

        SELECT ... INTO #bTemp FROM #aTemp;
    END
    ELSE
    BEGIN
        IF OBJECT_ID('[tempdb]..#bTemp') IS NOT NULL
        BEGIN
            DROP TABLE #bTemp;
        END

        SELECT ... INTO #bTemp FROM #aTemp;
    END
END
3
Kermit

ドキュメント

単一のストアドプロシージャまたはバッチ内に複数の一時テーブルが作成される場合、それらは異なる名前を持つ必要があります。

次のように、IFブロックの前にテーブルを作成しました。

DECLARE @a bit = 1;
BEGIN
    IF OBJECT_ID('[tempdb]..#bTemp') IS NOT NULL
    BEGIN
        DROP TABLE #bTemp;
    END

    CREATE TABLE #bTemp (
        [c] int);

    IF @a = 0
    BEGIN
        INSERT INTO #bTemp
        SELECT 1 AS [c];
    END
    ELSE
    BEGIN
        INSERT INTO #bTemp
        SELECT 1 AS [c];
    END

    DROP TABLE #bTemp;
END
4
Kermit

これは質問への回答ではなく、##グローバル一時テーブルが同時実行性を強制終了する方法を@SebastienMeineにデモンストレーションするだけです。

1つのウィンドウで、次のようにします。

CREATE PROCEDURE dbo.floob1
  @p INT
AS
BEGIN
  SET NOCOUNT ON;

  SELECT a = @p INTO ##floob;
END
GO

CREATE PROCEDURE dbo.floob2
AS
BEGIN
  SET NOCOUNT ON;

  SELECT a FROM ##floob;
END
GO

EXEC dbo.floob1 @p = 1;
WAITFOR DELAY '00:01:00';
EXEC dbo.floob2;

結果:

a
----
1

次に、別のウィンドウを開き、これを実行します。

BEGIN TRY
  EXEC dbo.floob1 @p = 2;
END TRY
BEGIN CATCH
  PRINT 'Something bad happened.';
END CATCH
GO
EXEC dbo.floob1 @p = 2;
GO
EXEC dbo.floob2;

結果:

何か悪いことが起こった。
メッセージ2714、レベル16、状態6、手順floob1
データベースにはすでに「## floob」という名前のオブジェクトがあります。

a
----
1

そのため、## tableは、それが作成されたプロシージャが終了してからずっと前から存在していますが、まだ存在しています。

次に、最初のウィンドウが終了したら、最初のウィンドウでこの部分をもう一度実行してみます。

EXEC dbo.floob1 @p = 1;

結果:

メッセージ2714、レベル16、状態6、手順floob1
データベースにはすでに「## floob」という名前のオブジェクトがあります。

そのため、同じユーザーが単に再度作成を試みたとしても、## tableはまだ存在しています。

これは次のことを示しています。

  1. このプロシージャを一度に呼び出すことができるユーザーは1人だけです
  2. #tempテーブル(私がフェンスのそばにいる)を削除しないようにするための標準的なアドバイスは、## tempテーブルには同じようには適用されません。
3
Aaron Bertrand

これは一時テーブルを事前に作成できず、コアロジックを動的SQLに配置したくない場合に使用するソリューションです。

IF 1 = 1 -- Replace with actual condition
BEGIN
    SELECT * INTO #tmp1 FROM dbo.Table1
END
ELSE
BEGIN
    SELECT * INTO #tmp2 FROM dbo.Table2
END

-- Inserting data into global temp table so sql server can't complain on not recognizing in a context
DECLARE @Command VARCHAR(MAX)
IF OBJECT_ID('tempdb..#tmp1') IS NOT NULL
BEGIN
    SET @Command = 'SELECT * INTO ##tmp FROM #tmp1'
END
ELSE
BEGIN
    SET @Command = 'SELECT * INTO ##tmp FROM #tmp2'
END

EXECUTE(@Command)
SELECT * INTO #tmpFinal FROM ##tmp -- Again passing data back to local temp table from global temp table to avoid seeing red mark

IF OBJECT_ID('tempdb..##tmp') IS NOT NULL DROP TABLE ##tmp
IF OBJECT_ID('tempdb..#tmp1') IS NOT NULL DROP TABLE #tmp1
IF OBJECT_ID('tempdb..#tmp2') IS NOT NULL DROP TABLE #tmp2

SELECT * FROM #tmpFinal

IF OBJECT_ID('tempdb..#tmpFinal') IS NOT NULL DROP TABLE #tmpFinal
0
Prateek Rai