在表变量上创建索引


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

临时表“ IS”是真实表,完成后就消失了。真正的区别(除了会自动消失之外)是它在TempDB中。当涉及到索引和约束时,这实际上是巨大的,因为您可能会遇到名称冲突,不仅是代码的其他执行,还有实例中其他数据库中执行的代码。
bielawski

@bielawski,这是一个表变量,而不是临时表。表变量不允许显式命名的约束,系统生成的名称可以保证是唯一的。它们确实允许从2014年开始使用命名索引,但这并不成问题,因为只需要在一个对象内唯一地命名索引,而无需跨对象。
马丁·史密斯

我的观点是2倍。1)除了使用变量来避免事务纠缠之外,临时表和表变量之间没有实质性区别。但是在V-2000中,没有语法可用于向变量添加约束,索引...。2)给定一个可以使用临时表的地方,如果使用静态名称,则诸如索引的命名表附件与同时执行同一SP的副本冲突!明确开发了以下机制,因为我在这些确切情况下将SP故障追溯到命名索引冲突。它们必须是唯一的。
bielawski

1
@bielawski-没有索引名称在对象之间不需要唯一-只有约束名称需要。这是微不足道的测试。刚刚执行CREATE TABLE #T1(X INT); CREATE TABLE #T2(X INT); CREATE INDEX IX ON #T1(X); CREATE INDEX IX ON #T2(X);
马丁·史密斯

Answers:


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年

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

简短的回答:是的。

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中,只能通过创建UNIQUEor PRIMARY KEY约束隐式创建表变量的索引。这些约束类型之间的区别在于,主键必须位于不可为空的列上。参与唯一约束的列可以为空。(尽管SQL Server在存在NULLs 的情况下对唯一约束的实现不符合SQL标准中指定的约束)。此外,表只能具有一个主键,但可以具有多个唯一约束。

这两个逻辑约束都通过唯一索引在物理上实现。如果未明确指定,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                                 |
+-------------------------------------+-------------------------------------+

最后一个需要一些解释。在此答案开头的表变量定义中,on上的非唯一非聚集索引Name是由on上的唯一索引模拟的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
注意:仅当文本列<= 900字节时,2000-2012解决方案才有效。即。varchar(900),nvarchar(450)
Andre Figueiredo

1
@AndreFigueiredo是的,在那些版本中,这也是永久表上索引键的最大大小。
马丁·史密斯

1
我只是想指出,SQL 2014答案在Azure中很好用。谢谢马丁!
Jaxidian

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)之后,它缺少一个括号,并补充一点
Tejasvi Hegde

1
命名索引没有问题-索引只需要在表中唯一地命名。问题在于命名约束,最好的解决方案通常是不在临时表中命名-命名约束可防止临时表对象缓存。
马丁·史密斯

1
仅对于某些版本必须是正确的(如果对于任何版本都正确)。我必须提出这种解决方法,特别是因为我在同时执行过程中将sp故障追溯到命名索引的冲突。
bielawski

@bielawski您正在使用2016吗?我对临时表上的命名索引是否对并发环境存在风险感到很好奇。
Elaskanator

1
@Elaskanator是的,他们是。在负载下,我们在SQL元数据表中发现了争用,删除索引名称解决了该问题。这是2016年的SQL
丹防守

0

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

 CREATE TABLE #Table(C1 int,       
  C2 NVarchar(100) , C3 varchar(100)
  UNIQUE CLUSTERED (c1) 
 ); 
  1. 创建具有唯一聚集索引的表

  2. 将数据插入到临时“ #Table”表中

  3. 创建非聚集索引。

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

在insert语句之后创建索引以避免不必要的排序
Boopathi.Indotnet,

请注意,如果表变量在函数中,则这是不可能的
Geoff
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.