您可以在 SQL Server 2000 中的表变量上创建索引吗?
IE
DECLARE @TEMPTABLE TABLE (
[ID] [int] NOT NULL PRIMARY KEY
,[Name] [nvarchar] (255) COLLATE DATABASE_DEFAULT NULL
)
我可以在 上创建索引Name
吗?
您可以在 SQL Server 2000 中的表变量上创建索引吗?
IE
DECLARE @TEMPTABLE TABLE (
[ID] [int] NOT NULL PRIMARY KEY
,[Name] [nvarchar] (255) COLLATE DATABASE_DEFAULT NULL
)
我可以在 上创建索引Name
吗?
这个问题被标记为 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 中,表变量的索引只能通过创建UNIQUE
orPRIMARY KEY
约束来隐式创建。这些约束类型之间的区别在于主键必须在不可为空的列上。参与唯一约束的列可以为空。(尽管 SQL Server 在存在NULL
s 的情况下实现的唯一约束并不符合 SQL 标准中的规定)。此外,一张表只能有一个主键,但可以有多个唯一约束。
这两个逻辑约束都是用唯一索引物理实现的。如果未明确指定,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 中实际实现的准确模拟,因为这会将“唯一性”添加到所有行。不仅仅是那些需要它的人。
应该理解,从性能的角度来看,@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)
);
创建具有唯一聚集索引的表
将数据插入临时“#Table”表
创建非聚集索引。
CREATE NONCLUSTERED INDEX IX1 ON #Table (C2,C3);