web-dev-qa-db-ja.com

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

SQL Server 2000のテーブル変数にindexを作成できますか?

つまり.

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

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

175
GordyII

質問には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 it mayによって、含まれる列も許可されますが、現在の位置はそれらが "リソースの制約のために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の従来のテーブルは、クラスター化インデックスを持つか、または heaps として構造化できます。

クラスター化インデックスは、重複キー値を許可しないように一意として宣言するか、デフォルトで非一意に設定できます。一意でない場合、SQL Serverは重複キーに niqueifier をサイレントに追加して一意にします。

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

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

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

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                                 |
+-------------------------------------+-------------------------------------+

最後の説明には少し説明が必要です。この回答の冒頭のテーブル変数定義では、non uniqueNameの非クラスター化インデックスはによってシミュレートされていますName,Idのuniqueインデックス(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で非一意のクラスター化インデックスが実際にどのように実装されるかについての正確なシミュレーションではありません。それを必要とするものだけではありません。

329
Martin Smith

パフォーマンスの観点から、変数を優先する@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);

これにより、同じプロシージャの同時実行間でも名前が常に一意であることが保証されます。

12
bielawski

テーブル変数に大きなデータがある場合、テーブル変数(@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);
    
0