web-dev-qa-db-ja.com

一時テーブルはスレッドセーフですか?

私はSQLServer 2000を使用しており、多くのストアドプロシージャは一時テーブルを広範囲に使用しています。データベースには大量のトラフィックがあり、一時テーブルを作成および削除する際のスレッドセーフが心配です。

いくつかの一時テーブルを作成するストアドプロシージャがあるとしましょう。一時テーブルを他の一時テーブルに結合することもできます。また、2人のユーザーが同時にストアドプロシージャを実行するとします。

  • 1人のユーザーがspを実行し、#tempという一時テーブルを作成し、別のユーザーが同じspを実行しても、#tempというテーブルがデータベースにすでに存在するために停止する可能性はありますか?

  • 同じユーザーが同じ接続で同じストアドプロシージャを2回実行した場合はどうでしょうか。

  • 2人のユーザーのクエリが互いに干渉する可能性のある他の奇妙なシナリオはありますか?

44
Juliet

最初のケースでは、いいえ、できません。#tempはローカルの一時テーブルであり、他の接続からは見えないためです(ユーザーが個別のデータベース接続を使用していると想定されます)。一時テーブル名は、生成されたランダムな名前にエイリアスされ、ローカルの一時テーブルを参照するときにそれを参照します。

あなたの場合、ストアドプロシージャでローカル一時テーブルを作成しているので、 プロシージャのスコープが終了するとその一時テーブルは削除されます (「備考」セクションを参照)。

2番目のケースでは、はい、テーブルがすでに存在し、接続が存在する限りテーブルが存続するため、このエラーが発生します。この場合、テーブルを作成する前に、テーブルの存在を確認することをお勧めします。

35
casperOne

ローカルスコープの一時テーブル(単一の#)は、それらを一意にする識別子を最後に付けて作成されます。複数の発信者(同じログインでも)が重複してはなりません。

(試してみてください:2つの接続と同じログインから同じ一時テーブルを作成します。次にtempdb.dbo.sysobjectsにクエリを実行して、実際に作成されたテーブルを確認します...)

9
Joe

ローカル一時テーブルは、現在のコンテキスト内にのみ存在するため、スレッドセーフです。コンテキストを現在の接続と混同しないでください(from [〜#〜] msdn [〜#〜] : "ストアドプロシージャで作成されたローカル一時テーブルは、ストアドプロシージャが終了すると自動的に削除されます" )、同じ接続で、ローカル一時テーブル(#TMPなど)を作成するストアドプロシージャを2回以上安全に呼び出すことができます。

この動作をテストするには、2つの接続から次のストアドプロシージャを実行します。このSPは30秒待機するため、2つのスレッドがそれぞれのバージョンの#TMPテーブルで同時に実行されていることを確認できます。

CREATE PROCEDURE myProc(@n INT)
AS BEGIN
    RAISERROR('running with (%d)', 0, 1, @n);
    CREATE TABLE #TMP(n INT);
    INSERT #TMP VALUES(@n);
    INSERT #TMP VALUES(@n * 10);
    INSERT #TMP VALUES(@n * 100);
    WAITFOR DELAY '00:00:30';
    SELECT * FROM #TMP;
END;
5
Gerardo Lima

簡単な答えは次のとおりです。

一時テーブルの分離はクエリごとに保証であり、スレッド化、ロック、または同時アクセスに関して心配する必要はありません。

ここでの回答が「接続」とスレッドの重要性について話している理由はわかりません。これらはプログラミングの概念であるのに対し、クエリの分離はデータベースレベルで処理されます

SQLサーバーでは、ローカルの一時オブジェクトはSessionで区切られます。 2つのクエリを同時に実行している場合、それらは2つの完全に別個のセッションであり、相互に干渉しません。ログインは重要ではないため、たとえばADO.NETを使用して単一の接続文字列を使用している場合(複数の同時クエリが同じSQLサーバーの「ログイン」を使用することを意味します)、クエリはすべて引き続き実行されます)個別のセッション。接続プーリングも重要ではありません。ローカル一時オブジェクト(テーブルおよびストアドプロシージャ)他のセッションからは完全に安全です

これがどのように機能するかを明確にするため。コードにはローカル一時オブジェクトの単一の共通名がありますが、SQL Serverは、セッションごとに各オブジェクトに一意の文字列を追加して、オブジェクトを分離します。これは、SSMSで以下を実行することで確認できます。

CREATE TABLE #T (Col1 INT)

SELECT * FROM tempdb.sys.tables WHERE [name] LIKE N'#T%';

名前には次のようなものが表示されます。

T_______________00000000001F

次に、そのクエリタブを閉じずに、新しいクエリタブを開き、同じクエリを貼り付けて、もう一度実行します。次のようなものが表示されます。

T_______________00000000001F

T_______________000000000020

したがって、コードが#Tを参照するたびに、SQLServerはセッションに基づいてコードを適切な名前に変換します。分離はすべて自動的に処理されます。

4
Chris Halcrow

一時テーブルはセッションに関連付けられているため、異なるユーザーが同時にプロシージャを実行しても、競合は発生しません...

3
Jason Punyon

一時テーブルは、それらを作成するクエリまたはプロシージャのコンテキストでのみ作成されます。新しいクエリはそれぞれ、他のクエリの一時テーブルがないデータベース上のコンテキストを取得します。そのため、名前の衝突は問題ではありません。

1

Tempsデータベースを見ると、そこに一時テーブルがあり、システムで生成された名前が付いています。したがって、通常のデッドロック以外は問題ありません。

1
Otávio Décio

2つのポンド記号を使用しない限り## temp一時テーブルはローカルになり、ユーザーへのそのローカル接続に対してのみ存在します

0
SQLMenace

まず、実際の一時テーブルを使用していることを確認しましょう。#または##で始まりますか?実際のテーブルをその場で作成し、それらをドロップして繰り返し再作成する場合、同時ユーザーで実際に問題が発生します。グローバル一時テーブル(##で始まるテーブル)を作成している場合も、問題が発生する可能性があります。同時実行の問題が必要ない場合は、ローカル一時テーブルを使用します(#で始まります)。また、procの最後で(または、長いマルチステップprocを話している場合は、procで不要になったときに)明示的に閉じ、作成する前に存在を確認する(存在する場合は削除する)こともお勧めします。 。

0
HLGEM