テーブル変数にインデックスを作成する


189

SQL Server 2000のテーブル変数にインデックスを作成できますか?

すなわち

DECLARE @TEMPTABLE TABLE (
     [ID] [int] NOT NULL PRIMARY KEY
    ,[Name] [nvarchar] (255) COLLATE DATABASE_DEFAULT NULL 
)

にインデックスを作成できますNameか?


3
両方の種類の一時テーブルを作成するにはコストがかかります。インデックスが必要となるほど大量のデータがある場合は、実際のテーブルを使用して検討するときかもしれません。トランザクションセーフになるように設定したこと。spidまたはユーザーIDでフィルタリングし、最後にそれをクリアします。実際のテーブルと一時テーブルの両方に変動がありますが、パフォーマンスに問題がある場合。実際のテーブルでも試してみてください。
u07ch 2009年

一時テーブルは「実際のテーブル」であり、作業が完了すると消えます。本当の違い(自動的になくなること以外)は、TempDBにあることです。インデックスと制約に関しては、実際には巨大です。なぜなら、コードの他の実行だけでなく、インスタンス内の他のデータベースで実行されているコードと名前が衝突する可能性があるからです。
bielawski

@bielawskiこれは一時変数ではなくテーブル変数です。テーブル変数は明示的に名前が付けられた制約を許可しません。システムで生成された名前は一意であることが保証されます。それらは2014年からの名前付きインデックスを許可しますが、インデックスはオブジェクト間ではなくオブジェクト内で一意に名前を付ける必要があるだけなので問題ありません。
マーティン・スミス

私のポイントは2倍でした。1)トランザクションの絡み合いを回避するために変数を使用する以外は、一時テーブルとテーブル変数の間に大きな違いはありません。ただし、V-2000では、変数に制約、インデックスなどを追加するための構文はありません。2)1の代わりに一時テーブルを使用することができます考えると、インデックスのような名前のテーブル付属はWILL静的名が使用されている場合、同時に同じSPのコピーを実行すると衝突します!以下のメカニズムは、これらの正確な状況で競合する名前付きインデックスまでSPの障害を追跡したため、明示的に開発されました。それらは一意でなければなりません。
bielawski 2017年

1
@bielawski -ノーのインデックス名は、オブジェクト間で一意である必要はありません-唯一の制約名が行います。これはテストするのは簡単です。実行するだけCREATE TABLE #T1(X INT); CREATE TABLE #T2(X INT); CREATE INDEX IX ON #T1(X); CREATE INDEX IX ON #T2(X);
マーティン・スミス

回答:


362

質問にはSQL Server 2000のタグが付いていますが、最新バージョンで開発している人々のために、最初にそれについて説明します。

SQL Server 2014

以下で説明する制約ベースのインデックスを追加する方法に加えて、SQL Server 2014では、一意でないインデックスをテーブル変数宣言のインライン構文で直接指定することもできます。

そのための構文例を以下に示します。

/*SQL Server 2014+ compatible inline index syntax*/
DECLARE @T TABLE (
C1 INT INDEX IX1 CLUSTERED, /*Single column indexes can be declared next to the column*/
C2 INT INDEX IX2 NONCLUSTERED,
       INDEX IX3 NONCLUSTERED(C1,C2) /*Example composite index*/
);

フィルターされたインデックスと含まれている列を持つインデックスは、現在この構文では宣言できませんが、SQL Server 2016ではこれがさらに緩和されています。CTP 3.1から、テーブル変数のフィルター選択されたインデックスを宣言できるようになりました。RTMによって可能付属の列も許可されている場合であってもよいが、現在の位置は、彼らがいることである「可能性が高いため、リソースの制約にSQL16にそれをすることはありません」

/*SQL Server 2016 allows filtered indexes*/
DECLARE @T TABLE
(
c1 INT NULL INDEX ix UNIQUE WHERE c1 IS NOT NULL /*Unique ignoring nulls*/
)

SQL Server 2000〜2012

Nameにインデックスを作成できますか?

短い答え:はい。

DECLARE @TEMPTABLE TABLE (
  [ID]   [INT] NOT NULL PRIMARY KEY,
  [Name] [NVARCHAR] (255) COLLATE DATABASE_DEFAULT NULL,
  UNIQUE NONCLUSTERED ([Name], [ID]) 
  ) 

より詳細な回答は以下のとおりです。

SQL Serverの従来のテーブルは、クラスター化インデックスを持つことも、ヒープとして構造化することもできます

クラスター化インデックスは、重複するキー値を許可しないように一意として宣言するか、デフォルトで非一意にすることができます。一意でない場合、SQL Serverは重複キーに一意の識別子を追加して、それらを一意にします。

非クラスター化インデックスは、一意として明示的に宣言することもできます。それ以外の場合、一意でない場合、SQL Server は行ロケーター(クラスター化インデックスキーまたはヒープのRID)をすべてのインデックスキー(重複するだけでなく)に追加します。これにより、再び一意になります。

SQL Server 2000-2012では、テーブル変数のインデックスは、UNIQUEまたはPRIMARY KEY制約を作成することによってのみ暗黙的に作成できます。これらの制約タイプの違いは、主キーがnullを許容しない列になければならないことです。一意性制約に参加している列はnull可能になる場合があります。(ただし、NULLsが存在する場合のSQL Serverの一意の制約の実装は、SQL標準で指定されているものではありません)。また、テーブルは1つの主キーのみを持つことができますが、一意の制約は複数持つことができます。

これらの論理制約はどちらも、一意のインデックスを使用して物理的に実装されます。明示的に指定されていない場合、PRIMARY KEYクラスター化されたインデックスと非クラスター化された一意の制約になりますが、この動作は、制約宣言を指定するCLUSTEREDNONCLUSTERED明示的に指定することでオーバーライドできます(構文例)

DECLARE @T TABLE
(
A INT NULL UNIQUE CLUSTERED,
B INT NOT NULL PRIMARY KEY NONCLUSTERED
)

上記の結果として、SQL Server 2000-2012のテーブル変数に次のインデックスを暗黙的に作成できます。

+-------------------------------------+-------------------------------------+
|             Index Type              | Can be created on a table variable? |
+-------------------------------------+-------------------------------------+
| Unique Clustered Index              | Yes                                 |
| Nonunique Clustered Index           |                                     |
| Unique NCI on a heap                | Yes                                 |
| Non Unique NCI on a heap            |                                     |
| Unique NCI on a clustered index     | Yes                                 |
| Non Unique NCI on a clustered index | Yes                                 |
+-------------------------------------+-------------------------------------+

最後のものは少し説明が必要です。この回答の先頭にテーブル変数の定義では非ユニーク上の非クラスタ化インデックスNameによってシミュレートされたユニーク上のインデックスName,Id(SQL Serverは黙って非ユニークNCIキーにクラスタ化インデックスキーを追加することをリコールとにかく)。

一意でないクラスタ化インデックスはIDENTITY、一意の識別子として機能する列を手動で追加することでも実現できます。

DECLARE @T TABLE
(
A INT NULL,
B INT NULL,
C INT NULL,
Uniqueifier INT NOT NULL IDENTITY(1,1),
UNIQUE CLUSTERED (A,Uniqueifier)
)

ただし、これはすべての行に「一意名」を追加するため、一意でないクラスター化インデックスが実際にSQL Serverに実際に実装される方法の正確なシミュレーションではありません。それを必要とする人だけではありません。


1
注:2000-2012ソリューションは、テキスト列が900バイト以下の場合にのみ機能します。すなわち。varchar(900)、nvarchar(450)
Andre Figueiredo 2017

1
@AndreFigueiredoうん、それはこれらのバージョンの永続テーブルのインデックスキーの最大サイズでもあります。
マーティンスミス

1
SQL 2014の回答がAzureで適切に機能することに注意したいだけです。マーティン、ありがとう!
Jaxidian 2018

13

パフォーマンスの観点から、変数を優先する@tempテーブルと#tempテーブルの間に違いはないことを理解する必要があります。それらは同じ場所(tempdb)に常駐し、同じ方法で実装されます。すべての違いは追加機能に表示されます。この驚くほど完全な記事をご覧くださいhttps ://dba.stackexchange.com/questions/16385/whats-the-difference-between-a-temp-table-and-table-variable-in-sql-server/16386#16386

テーブル関数やスカラー関数などの一時テーブルを使用できない場合もありますが、v2016より前のほとんどの場合(フィルターされたインデックスもテーブル変数に追加できる場合)は、#tempテーブルを使用するだけで済みます。

tempdbで名前付きインデックス(または制約)を使用する場合の欠点は、名前が衝突する可能性があることです。理論的には他のプロシージャだけでなく、#tempテーブルのコピーに同じインデックスを配置しようとするプロシージャ自体の他のインスタンスを使用すると、非常に簡単です。

名前の衝突を回避するために、次のようなものが通常は機能します。

declare @cmd varchar(500)='CREATE NONCLUSTERED INDEX [ix_temp'+cast(newid() as varchar(40))+'] ON #temp (NonUniqueIndexNeeded);';
exec (@cmd);

これにより、同じプロシージャを同時に実行する場合でも、名前が常に一意になります。


varchar(40)の後にブラケットが1つ欠落しています。追加してください
Tejasvi Hegde

1
名前付きインデックスに問題はありません。インデックスは、テーブル内で一意に名前を付ける必要があるだけです。問題は名前付き制約にあり、一般に最善の解決策は一時テーブルで名前を付けないことです。名前付き制約は一時テーブルオブジェクトのキャッシュを妨げます。
マーティンスミス

1
これは、特定のバージョンにのみ当てはまる必要があります(いずれかのバージョンに当てはまる場合)。同時実行中に名前付きインデックスの衝突までspの失敗を追跡したため、この回避策を考え出す必要がありました。
bielawski

@bielawski 2016を使用していますか?一時テーブルの名前付きインデックスが並行環境のリスクであるかどうかについて、私は非常に興味があります。
Elaskanator

1
@Elaskanatorはい、そうです。負荷がかかっているときにSQLメタデータテーブルで競合が見つかり、インデックスの名前を削除すると問題が解決しました。これは、SQL 2016年だった
ダンデフ

0

テーブル変数に大きなデータがある場合、テーブル変数(@table)の代わりに一時テーブル(#table).table変数を作成すると、挿入後にインデックスを作成できません。

 CREATE TABLE #Table(C1 int,       
  C2 NVarchar(100) , C3 varchar(100)
  UNIQUE CLUSTERED (c1) 
 ); 
  1. 一意のクラスター化インデックスを持つテーブルを作成する

  2. Temp "#Table"テーブルにデータを挿入する

  3. 非クラスター化インデックスを作成します。

     CREATE NONCLUSTERED INDEX IX1  ON #Table (C2,C3);

不要な並べ替えを避けるために、挿入ステートメントの後にインデックスを作成します
Boopathi.Indotnet

テーブル変数が関数内にある場合、これは不可能であることに注意してください
Geoff
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.