在SQL Server 2000中,可以在表变量上创建索引吗?

即。

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

我可以在名称上创建索引吗?


这个问题被标记为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

我可以在名称上创建索引吗?

简单的回答:是的。

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)添加到所有索引键(不仅仅是重复的),这再次确保它们是唯一的。

In SQL Server 2000 - 2012 indexes on table variables can only be created implicitly by creating a UNIQUE or PRIMARY KEY constraint. The difference between these constraint types are that the primary key must be on non nullable column(s). The columns participating in a unique constraint may be nullable. (though SQL Server's implementation of unique constraints in the presence of NULLs is not per that specified in the SQL Standard). Also a table can only have one primary key but multiple unique constraints.

这两个逻辑约束都是用唯一的索引在物理上实现的。如果没有显式地指定,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                                 |
+-------------------------------------+-------------------------------------+

最后一个需要解释一下。在答案开头的表变量定义中,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中实际实现,因为这将“Uniqueifier”添加到所有行。不仅仅是那些需要它的公司。


应该理解的是,从性能的角度来看,@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);

这确保了即使在同一过程的同时执行之间,名称也始终是唯一的。


如果表变量有大量数据,则创建临时表(# Table)而不是表变量(@table)。表变量不允许在插入后创建索引。

 CREATE TABLE #Table(C1 int,       
  C2 NVarchar(100) , C3 varchar(100)
  UNIQUE CLUSTERED (c1) 
 ); 

创建具有唯一聚集索引的表 插入数据到Temp“#Table”表中 创建非聚集索引。 创建非聚类索引IX1 (C2,C3)