表提示 (Transact-SQL)

适用于: SQL Server Azure SQL 数据库 Azure SQL 托管实例

表提示用于在数据操作语言 (DML) 语句中替代查询优化器的默认行为。 可以指定锁定方法、一个或多个索引、查询处理操作(如表扫描或索引查找),或其他选项。 表提示在 DML 语句的 FROM 子句中指定,仅影响在该子句中引用的表或视图。

注意

由于 SQL Server 查询优化器通常会为查询选择最佳执行计划,因此我们建议仅在最后迫不得已的情况下才可由资深的开发人员和数据库管理员使用提示。

适用于:

Transact-SQL 语法约定

语法

WITH  ( <table_hint> [ [ , ] ...n ] )

<table_hint> ::=
{ NOEXPAND
  | INDEX ( <index_value> [ , ...n ] ) | INDEX = ( <index_value> )
  | FORCESEEK [ ( <index_value> ( <index_column_name> [ , ... ] ) ) ]
  | FORCESCAN
  | HOLDLOCK
  | NOLOCK
  | NOWAIT
  | PAGLOCK
  | READCOMMITTED
  | READCOMMITTEDLOCK
  | READPAST
  | READUNCOMMITTED
  | REPEATABLEREAD
  | ROWLOCK
  | SERIALIZABLE
  | SNAPSHOT
  | SPATIAL_WINDOW_MAX_CELLS = <integer_value>
  | TABLOCK
  | TABLOCKX
  | UPDLOCK
  | XLOCK
}

<table_hint_limited> ::=
{
    KEEPIDENTITY
  | KEEPDEFAULTS
  | HOLDLOCK
  | IGNORE_CONSTRAINTS
  | IGNORE_TRIGGERS
  | NOLOCK
  | NOWAIT
  | PAGLOCK
  | READCOMMITTED
  | READCOMMITTEDLOCK
  | READPAST
  | REPEATABLEREAD
  | ROWLOCK
  | SERIALIZABLE
  | SNAPSHOT
  | TABLOCK
  | TABLOCKX
  | UPDLOCK
  | XLOCK
}

参数

WITH ( <table_hint> ) [ [ , ] ...n ]

存在一些例外情况:只有在使用 WITH 关键字指定表提示时,才支持在 FROM 子句中使用这些提示。 指定表提示时必须使用括号。

重要

省略 WITH 关键字是已弃用的功能:此功能将在 SQL Server 的未来版本中删除。 请避免在新的开发工作中使用该功能,并着手修改当前还在使用该功能的应用程序。

无论是否允许使用 WITH 关键字,都可以使用以下表提示:NOLOCKREADUNCOMMITTEDUPDLOCKREPEATABLEREADSERIALIZABLEREADCOMMITTEDTABLOCKTABLOCKXPAGLOCKROWLOCKNOWAITREADPASTXLOCKSNAPSHOTNOEXPAND。 如果指定的表提示不含 WITH 关键字,则必须单独指定该提示。 例如:

FROM t (TABLOCK)

如果指定的提示含其他选项,则指定的提示必须含 WITH 关键字:

FROM t WITH (TABLOCK, INDEX(myindex))

建议在表提示之间使用逗号。

重要

用空格而不是逗号分隔提示是一项已弃用的功能:将在 SQL Server 的未来版本中删除此功能。 请避免在新的开发工作中使用该功能,并着手修改当前还在使用该功能的应用程序。

NOEXPAND

指定查询优化器处理查询时,不扩展任何索引视图来访问基础表。 查询优化器将视图当成包含聚集索引的表处理。 NOEXPAND 仅适用于索引视图。 有关详细信息,请参阅使用 NOEXPAND

INDEX ( <index_value> [ , ...n ] ) | INDEX = ( <index_value> )

INDEX() 语法指定供查询优化器在处理该语句时使用的一个或多个索引的名称或 ID。 另一供选择的 INDEX = 语法指定单个索引值。 只能为每个表指定一个索引提示。

如果存在聚集索引,则 INDEX(0) 强制执行聚集索引扫描,INDEX(1) 强制执行聚集索引扫描或查找。 如果不存在聚集索引,则 INDEX(0) 强制执行表扫描,INDEX(1) 被解释为错误。

如果在单个提示列表中使用了多个索引,则会忽略重复项,其余列出的索引将用于检索表中的行。 索引提示中的索引顺序很重要。 多索引提示还强制执行索引 AND 运算,查询优化器将对所访问的每个索引应用尽可能多的条件。 如果提示索引的集合并未包含查询引用的所有列,则会在 SQL Server 数据库引擎检索所有索引列后执行提取操作以检索其余列。

注意

如果将引用多个索引的索引提示用于星型联接中的事实数据表,则优化器将忽略索引提示,并返回一个警告消息。 另外,不允许对包含指定索引提示的表执行索引 OR 操作。

表提示中的最大索引数为 250 个非聚集索引。

KEEPIDENTITY

只适用于 INSERT 语句(当 BULK 选项与 OPENROWSET 一起使用时)。

指定导入数据文件中的标识值用于标识列。 如果不指定 KEEPIDENTITY,则将验证但不导入此列的标识值。查询优化器将根据创建表时指定的种子值和增量值自动分配唯一值。

重要

如果数据文件不包含表或视图中的标识列的值,并且标识列不是表中的最后一列,则必须跳过标识列。 有关详细信息,请参阅使用格式化文件跳过数据字段 (SQL Server)。 如果成功跳过了一个标识列,则查询优化器自动将标识列的唯一值分配到导入的表行中。

有关 INSERT ... SELECT * FROM OPENROWSET(BULK...) 语句中使用此提示的示例,请参阅批量导入数据时保留标识值 (SQL Server)

有关检查表的标识值的信息,请参阅 DBCC CHECKIDENT (Transact-SQL)

KEEPDEFAULTS

只适用于 INSERT 语句(当 BULK 选项与 OPENROWSET 一起使用时)。

指定数据记录在某一表列缺少值时插入此列的默认值(如果有),而不是插入 NULL。

有关在 INSERT ... SELECT * FROM OPENROWSET(BULK...) 语句中使用此提示的示例,请参阅在批量导入期间保留 Null 或使用默认值 (SQL Server)

FORCESEEK [ ( <index_value> ( <index_column_name> [ , ...n ] ) ) ]

指定查询优化器仅使用索引查找操作作为表或视图中的数据的访问途径。

注意

从 SQL Server 2008 R2 (10.50.x) Service Pack 1 开始,还可以指定索引参数。 在这种情况下,查询优化器仅考虑通过指定的索引(至少使用指定的索引列)执行索引查找操作。

  • index_value

    索引名称或索引 ID 值。 不能指定索引 ID 0(堆)。 若要返回索引名称或 ID,请查询 sys.indexes 目录视图。

  • index_column_name

    要包含在查找操作中的索引列的名称。 使用索引参数指定 FORCESEEK 类似于通过 INDEX 提示使用 FORCESEEK。 但是,您可以通过指定要查找的索引和查找操作中要考虑的索引列,更好地控制查询优化器使用的访问路径。 该优化器可以根据需要考虑其他列。 例如,如果指定非聚集索引,优化器除了使用指定的列之外,还可以选择使用聚集索引键列。

可通过下列方式指定 FORCESEEK 提示。

语法 示例 说明
没有索引或 INDEX 提示 FROM dbo.MyTable WITH (FORCESEEK) 查询优化器仅考虑执行索引查找操作以通过任意相关索引访问表或视图。
INDEX 提示结合使用 FROM dbo.MyTable WITH (FORCESEEK, INDEX (MyIndex)) 查询优化器仅考虑执行索引查找操作以通过指定的索引访问表或视图。
通过指定索引和索引列进行参数化 FROM dbo.MyTable WITH (FORCESEEK (MyIndex (col1, col2, col3))) 查询优化器仅考虑执行索引查找操作,以通过指定的索引(至少使用指定的索引列)访问表或视图。

使用 FORCESEEK 提示(具有或不带索引参数)时,考虑以下准则:

  • 该提示可以指定为表提示或查询提示。 有关查询提示的详细信息,请参阅查询提示 (Transact-SQL)
  • 若要将 FORCESEEK 应用到索引视图,还必须指定 NOEXPAND 提示。
  • 对每个表或视图最多应用该提示一次。
  • 不能为远程数据源指定该提示。 带索引提示指定 FORCESEEK 时,将返回错误 7377;不带索引提示使用 FORCESEEK 时,将返回错误 8180。
  • 如果 FORCESEEK 导致找不到计划,将返回错误 8622。

使用索引参数指定 FORCESEEK 时,遵循以下准则和限制:

  • 不能为作为 INSERT、UPDATE 或 DELETE 语句的目标的表指定该提示。
  • 该提示不能与 INDEX 提示或另一个 FORCESEEK 提示一起指定。
  • 至少必须指定一个列且该列为第一个键列。
  • 可以指定其他索引列,但是不能跳过键列。 例如,如果指定的索引包含键列 abc,则有效的语法应包含 FORCESEEK (MyIndex (a))FORCESEEK (MyIndex (a, b)。 无效的语法应包含 FORCESEEK (MyIndex (c))FORCESEEK (MyIndex (a, c)
  • 在提示中指定的列名顺序必须与引用的索引中列的顺序匹配。
  • 不能指定不在索引键定义中的列。 例如,在非聚集索引中,只能指定定义的索引键列。 不能指定自动包含在索引中的聚集键列,但是优化器可以使用这些列。
  • xVelocity 内存优化的列存储索引不能作为索引参数指定。 返回错误 366。
  • 修改索引定义(例如通过添加或删除列)可能需要修改引用该索引的查询。
  • 该提示阻止优化器考虑表的任何空间或 XML 索引。
  • 该提示不能与 FORCESCAN 提示一起指定。
  • 对于分区的索引,不能在 FORCESEEK 提示中指定由 SQL Server 隐式添加的分区列。

注意

指定带参数的 FORCESEEK 限制优化器可以考虑的计划数大于指定不带参数的 FORCESEEK 时的计划数。 这可能导致更多情况下发生 Plan cannot be generated 错误。 在未来的版本中,对查询优化器进行内部修改后可允许考虑更多计划。

FORCESCAN

适用于:SQL Server 2008 R2 (10.50.x) Service Pack 1 及更高版本

指定查询优化器仅使用索引扫描操作作为引用的表或视图的访问途经。 对于优化器低估受影响的行数并选择一个查找操作而非扫描操作的查询,FORCESCAN 提示很有用。 出现这样的情况时,授予该操作的内存量太小,查询性能将受影响。

指定 FORCESCAN 时有无 INDEX 提示均可。 与索引提示组合使用 (INDEX = index_name, FORCESCAN) 时,查询优化器在访问引用的表时仅考虑通过指定的索引扫描访问路径。 可以带索引提示 INDEX(0) 指定 FORCESCAN,以强制对基表执行表扫描操作。

对于分区的表和索引,在通过查询谓词评估消除分区后应用 FORCESCAN。 这意味着扫描仅适用于剩余分区而非整个表。

FORCESCAN 提示具有下列限制:

  • 不能为作为 INSERT、UPDATE 或 DELETE 语句的目标的表指定该提示。
  • 该提示不能与一个以上的索引提示一起使用。
  • 该提示阻止查询优化器考虑表的任何空间或 XML 索引。
  • 不能为远程数据源指定该提示。
  • 该提示不能与 FORCESEEK 提示一起指定。

HOLDLOCK

等效于 SERIALIZABLE。 有关详细信息,请参阅本文后面的 SERIALIZABLEHOLDLOCK 仅应用于那些为其指定了 HOLDLOCK 的表或视图,并且仅在使用了 HOLDLOCK 的语句定义的事务的持续时间内应用。 不能在包含 FOR BROWSE 选项的 SELECT 语句中使用 HOLDLOCK

IGNORE_CONSTRAINTS

只适用于 INSERT 语句(当 BULK 选项与 OPENROWSET 一起使用时)。

指定大容量导入操作将忽略对表的任何约束。 默认情况下,INSERT 会检查唯一约束和 CHECK 约束以及主键和外键约束。 当为大容量导入操作指定 IGNORE_CONSTRAINTS 时,INSERT 必须忽略对目标表的这些约束。 无法禁用 UNIQUE、PRIMARY KEY 或 NOT NULL 约束。

如果输入数据包含违反约束的行,则您可能希望禁用 CHECK 和 FOREIGN KEY 约束。 通过禁用 CHECK 和 FOREIGN KEY 约束,可以导入数据,然后使用 Transact-SQL 语句清除该数据。

不过,如果忽略了 CHECK 和 FOREIGN KEY 约束,在执行操作后,表的每个已忽略约束会在 sys.check_constraintssys.foreign_keys 目录视图中标记为 is_not_trusted。 在某一时刻,应该检查全表约束。 如果在大容量导入操作之前表不为空,则重新验证约束的开销可能超过对增量数据应用 CHECK 和 FOREIGN KEY 约束的开销。

IGNORE_TRIGGERS

只适用于 INSERT 语句(当 BULK 选项与 OPENROWSET 一起使用时)。

指定大容量导入操作将忽略为表定义的所有触发器。 默认情况下,INSERT 将应用触发器。

仅当应用程序不依赖任何触发器,并且必须最大程度地提高性能时,才使用 IGNORE_TRIGGERS。

NOLOCK

等效于 READUNCOMMITTED。 有关详细信息,请参阅本文后面的 READUNCOMMITTED

注意

对于 UPDATE 或 DELETE 语句:将在 SQL Server 的未来版本中删除此功能。 请避免在新的开发工作中使用该功能,并着手修改当前还在使用该功能的应用程序。

NOWAIT

指示数据库引擎在遇到表的锁时,立即返回一条消息。 NOWAIT 等同于为特定表指定 SET LOCK_TIMEOUT 0。 如果还包括 TABLOCK 提示,NOWAIT 提示将不起作用。 若要在使用 TABLOCK 提示时终止查询而不等待,请改为在查询前加上 SET LOCK_TIMEOUT 0;

PAGLOCK

在通常行或键采用单个锁的地方,或者通常采用单个表锁的地方,请采用页锁。 默认情况下,请使用与操作相对应的锁模式。 在从 SNAPSHOT 隔离级别操作的事务中指定时,除非将 PAGLOCK 与需要锁的其他表提示(例如,UPDLOCKHOLDLOCK)组合,否则不会取得页锁。

READCOMMITTED

指定读操作使用锁定或行版本控制来遵循有关 READ COMMITTED 隔离级别的规则。 如果 READ_COMMITTED_SNAPSHOT 数据库选项为 OFF,数据库引擎会在读取数据时获取共享锁,在读操作完成后释放这些锁。 如果数据库选项 READ_COMMITTED_SNAPSHOT 为 ON,则数据库引擎不获取锁,并使用行版本控制。 有关隔离级别的详细信息,请参阅 SET TRANSACTION ISOLATION LEVEL (Transact-SQL)

注意

对于 UPDATE 或 DELETE 语句:将在 SQL Server 的未来版本中删除此功能。 请避免在新的开发工作中使用该功能,并着手修改当前还在使用该功能的应用程序。

READCOMMITTEDLOCK

指定读操作使用锁定来遵循有关 READ COMMITTED 隔离级别的规则。 无论 READ_COMMITTED_SNAPSHOT 数据库选项的设置如何,数据库引擎都将在读取数据时获取共享锁,在读操作完成后释放这些锁。 有关隔离级别的详细信息,请参阅 SET TRANSACTION ISOLATION LEVEL (Transact-SQL)。 不能对 INSERT 语句的目标表指定此提示;将返回错误 4140。

READPAST

指定数据库引擎不读取由其他事务锁定的行。 如果指定 READPAST,则跳过行级锁,但不跳过页级锁。 也就是说,数据库引擎将跳过这些行,而不是阻塞当前事务直到锁被释放。 例如,假设表 T1 包含一个单精度整数列,其值为 1、2、3、4 和 5。 如果事务 A 将值 3 更改为 8,但尚未提交,则 SELECT * FROM T1 (READPAST) 将生成值 1、2、4 和 5。 在实现使用 SQL Server 表的工作队列时,READPAST 主要用于减少锁定争用。 使用 READPAST 的队列读取器会跳过被其他事务锁定的队列项,跳至下一个可用的队列项,而不是等待其他事务释放锁。

可为 UPDATE 或 DELETE 语句中以及 FROM 子句中引用的任何表指定 READPAST。 如果 READPAST 是在 UPDATE 语句中指定的,则仅当读取数据以标识要更新的记录时才应用 READPAST,而不考虑语句中指定 READPAST 的位置。 不能为 INSERT 语句的 INTO 子句中的表指定 READPAST。 读取外键或索引视图或者修改辅助索引时,使用 READPAST 的更新或删除操作可能发生阻塞。

仅可在运行于 READ COMMITTED 或 REPEATABLE READ 隔离级别的事务中指定 READPAST。 在从 SNAPSHOT 隔离级别操作的事务中指定时,READPAST 必须与需要锁的其他表提示(例如,UPDLOCKHOLDLOCK)组合。

READ_COMMITTED_SNAPSHOT 数据库选项设置为 ON 并且满足以下条件之一时,无法指定 READPAST 表提示:

  • 会话的事务隔离级别为 READ COMMITTED。
  • 查询中也指定了 READCOMMITTED 表提示。

若要在上述情况下指定 READPAST 提示,请删除 READCOMMITTED 表提示(如果存在),然后在查询中包括 READCOMMITTEDLOCK 表提示。

READUNCOMMITTED

指定允许脏读。 不发布共享锁来阻止其他事务修改当前事务读取的数据,其他事务设置的排他锁不会阻碍当前事务读取锁定数据。 允许脏读可能产生较多的并发操作,但其代价是读取以后会被其他事务回滚的数据修改。 这可能会使您的事务出错,向用户显示从未提交过的数据,或者导致用户两次看到记录(或根本看不到记录)。

READUNCOMMITTEDNOLOCK 提示仅适用于数据锁。 所有查询(包括那些带有 READUNCOMMITTEDNOLOCK 提示的查询)都会在编译和执行过程中获取 Sch-S(架构稳定性)锁。 因此,当并发事务持有表的 Sch-M(架构修改)锁时,将阻塞查询。 例如,数据定义语言 (DDL) 操作在修改表的架构信息之前获取 Sch-M 锁。 所有并发查询(包括那些使用 READUNCOMMITTEDNOLOCK 提示运行的查询)都会在尝试获取 Sch-S 锁时被阻塞。 相反,持有 Sch-S 锁的查询将阻塞尝试获取 Sch-M 锁的并发事务。

不能为通过插入、更新或删除操作修改过的表指定 READUNCOMMITTEDNOLOCK。 SQL Server 查询优化器忽略 FROM 子句中应用于 UPDATE 或 DELETE 语句的目标表的 READUNCOMMITTEDNOLOCK 提示。

注意

在 SQL Server 的未来版本中,将不再支持在 FROM 子句中使用应用于 UPDATE 或 DELETE 语句目标表的 READUNCOMMITTEDNOLOCK 提示。 请避免在新的开发工作上下文中使用这些提示,并计划修改当前使用它们的应用程序。

可以通过使用以下任意一种方法,在保护事务避免对未提交的数据修改进行脏读的同时最大程度地减少锁争用:

  • READ COMMITTED 隔离级别,其中 READ_COMMITTED_SNAPSHOT 数据库选项设置为 ON。
  • SNAPSHOT 隔离级别。

有关隔离级别的详细信息,请参阅 SET TRANSACTION ISOLATION LEVEL (Transact-SQL)

注意

如果在指定 READUNCOMMITTED 时收到错误消息 601,则按解决死锁错误(错误消息 1205)的方法解决该错误,然后重试语句。

REPEATABLEREAD

指定事务在 REPEATABLE READ 隔离级别运行时,使用相同的锁定语义执行一次扫描。 有关隔离级别的详细信息,请参阅 SET TRANSACTION ISOLATION LEVEL (Transact-SQL)

ROWLOCK

指定通常采用页锁或表锁时,采用行锁。 在从 SNAPSHOT 隔离级别操作的事务中指定时,除非将 ROWLOCK 与需要锁的其他表提示(例如,UPDLOCKHOLDLOCK)组合,否则不会取得行锁。 ROWLOCK 不能用于具有聚集列存储索引的表。 以下示例将向应用程序返回错误 651

UPDATE [dbo].[FactResellerSalesXL_CCI] WITH (ROWLOCK)
SET UnitPrice = 50
WHERE ProductKey = 150;

SERIALIZABLE

等效于 HOLDLOCK。 保持共享锁直到事务完成,使共享锁更具有限制性;而不是无论事务是否完成,都在不再需要所需表或数据页时立即释放共享锁。 执行扫描时所用的语义与在 SERIALIZABLE 隔离级别运行的事务的语义相同。 有关隔离级别的详细信息,请参阅 SET TRANSACTION ISOLATION LEVEL (Transact-SQL)

SNAPSHOT

适用于:SQL Server 2014 (12.x) 及更高版本

内存优化表在 SNAPSHOT 隔离下访问。 SNAPSHOT 只能用于内存优化表(不能用于基于磁盘的表),如以下示例所示。 有关详细信息,请参阅内存优化表简介

SELECT *
FROM dbo.Customers AS c WITH (SNAPSHOT)
LEFT JOIN dbo.[Order History] AS oh
    ON c.customer_id=oh.customer_id;

SPATIAL_WINDOW_MAX_CELLS = <integer_value>

适用于:SQL Server 2012 (11.x) 及更高版本

指定在分割 geometry 或 geography 对象时使用的最大单元格数。 <integer_value> 是一个介于 1 到 8192 之间的值。

通过使用此选项,可以在主要和辅助筛选器执行时间之间权衡性能以微调查询执行时间。 较大的数字将减少辅助筛选器执行时间,但会增加主要筛选器执行时间,而较小的数字恰相反。 对于较密的空间数据,较大的数字通过为主要筛选器提供更好的近似值并减少辅助筛选器执行时间,从而缩短了执行时间。 对于更稀疏的数据,较小的数字将减少主要筛选器执行时间。

此选项适用于手动和自动网格分割。

TABLOCK

指定在表级别应用获取的锁。 获取的锁类型取决于正在执行的语句。 例如,SELECT 语句可能获取一个共享锁。 通过指定 TABLOCK,将该共享锁应用到整个表而非在行或页级别应用。 如果同时指定了 HOLDLOCK,则会一直持有表锁,直至事务结束。

在使用 INSERT INTO <target_table> SELECT <columns> FROM <source_table> 语句将数据导入某个堆时,可通过为目标表指定 TABLOCK 提示,实现语句的最小日志记录和最佳锁定。 此外,数据库的恢复模式必须设置为简单或大容量日志模式。 TABLOCK 提示还允许对堆或聚集列存储索引进行并行插入。 有关详细信息,请参阅 INSERT (Transact-SQL)

在与 OPENROWSET BULK 行集提供程序一起使用以将数据导入表时,TABLOCK 允许多个客户端使用优化日志记录和锁定,以并发方式将数据加载到目标表中。 有关详细信息,请参阅在批量导入中按最小方式记录日志的前提条件

TABLOCKX

指定对表采用排他锁。

UPDLOCK

指定采用更新锁并保持到事务完成。 UPDLOCK 仅对行级别或页级别的读操作采用更新锁。 如果将 UPDLOCKTABLOCK 组合使用或出于一些其他原因采用表级锁,将采用排他 (X) 锁。

如果指定了 UPDLOCK,将忽略 READCOMMITTEDREADCOMMITTEDLOCK 隔离级别的提示。 例如,如果将会话的隔离级别设置为 SERIALIZABLE 且查询指定 (UPDLOCK, READCOMMITTED),则忽略 READCOMMITTED 提示且使用 SERIALIZABLE 隔离级别运行事务。

XLOCK

指定采用排他锁并保持到事务完成。 如果同时指定了 ROWLOCKPAGLOCKTABLOCK,则排他锁将应用于相应的粒度级别。

注解

如果查询计划不访问表,则将忽略表提示。 这可能是由于优化器选择了完全不访问该表,也可能是因为改成了访问索引视图。 在后一种情况中,使用 OPTION (EXPAND VIEWS) 查询提示可阻止访问索引视图。

所有锁提示将传播到查询计划访问的所有表和视图,其中包括在视图中引用的表和视图。 另外,SQL Server 还将执行对应的锁一致性检查。

获取行级别锁的锁提示 ROWLOCKUPDLOCKXLOCK 可能对索引键而不是实际的数据行采用锁。 例如,如果表具有非聚集索引,而且由涵盖索引处理使用锁提示的 SELECT 语句,则获得的锁针对的是涵盖索引中的索引键,而不是基表中的数据行。

如果表包含计算列,而该计算列是由访问其他表中的列的表达式或函数计算的,则不在这些表中使用表提示,并且不会传播这些提示。 例如,在查询的表中指定 NOLOCK 表提示。 此表包含的计算列是由访问另一表中的列的表达式和函数组合计算的。 表达式和函数引用的表在被访问时将不使用 NOLOCK 表提示。

对于 FROM 子句中的每个表,SQL Server 不允许存在多个来自以下各个组的表提示:

  • 粒度提示:PAGLOCKNOLOCKREADCOMMITTEDLOCKROWLOCKTABLOCKTABLOCKX
  • 隔离级别提示:HOLDLOCKNOLOCKREADCOMMITTEDREPEATABLEREADSERIALIZABLE

筛选索引提示

筛选索引可用作表提示,但如果未涵盖查询选择的所有行,则会导致查询优化器产生错误 8622。 下面是一个无效筛选索引提示的示例。 该示例创建了筛选索引 FIBillOfMaterialsWithComponentID,然后将其用作 SELECT 语句的索引提示。 筛选索引谓词包含 ComponentID 为 533、324 和 753 的数据行。 查询谓词也包含 ComponentID 为 533、324 和 753 的数据行,但它扩展了结果集,使之包含 ComponentID 为 855 和 924 的数据行,而筛选索引中则不包含这两行。 因此,查询优化器无法使用此筛选索引提示,并产生错误 8622。 有关详细信息,请参阅 Create Filtered Indexes

IF EXISTS (SELECT name FROM sys.indexes
    WHERE name = N'FIBillOfMaterialsWithComponentID'
    AND object_id = OBJECT_ID(N'Production.BillOfMaterials'))
DROP INDEX FIBillOfMaterialsWithComponentID
    ON Production.BillOfMaterials;
GO
CREATE NONCLUSTERED INDEX "FIBillOfMaterialsWithComponentID"
    ON Production.BillOfMaterials (ComponentID, StartDate, EndDate)
    WHERE ComponentID IN (533, 324, 753);
GO
SELECT StartDate, ComponentID FROM Production.BillOfMaterials
    WITH(INDEX (FIBillOfMaterialsWithComponentID))
    WHERE ComponentID in (533, 324, 753, 855, 924);
GO

如果 SET 选项不包含筛选索引所需的值,查询优化器将不考虑使用索引提示。 有关详细信息,请参阅 CREATE INDEX (Transact-SQL)

使用 NOEXPAND

NOEXPAND 仅适用于索引视图。 索引视图是包含为其创建的唯一聚集索引的视图。 如果查询包含对同时存在于索引视图和基表中的列的引用,而且查询优化器确定执行查询的最佳方法是使用索引视图,则查询优化器将对视图使用索引。 此功能称为索引视图匹配。 在 SQL Server 2016 (13.x) Service Pack 1 之前,仅在特定版本的 SQL Server 中支持查询优化器自动使用索引视图。 因为所有版本都支持自动使用索引视图。 Azure SQL 数据库和 Azure SQL 托管实例还支持在不指定 NOEXPAND 提示的情况下自动使用索引视图。

有关更多信息,请参阅查询处理体系结构指南

有关 Windows 上 SQL Server 各版本支持的功能列表,请参阅:

但是,为了使查询优化器考虑使用索引视图进行匹配,或者使用通过 NOEXPAND 提示引用的索引视图,则必须将以下 SET 选项设置为 ON。

  • ANSI_NULLS
  • ANSI_PADDING
  • ANSI_WARNINGS
  • ARITHABORT 1
  • CONCAT_NULL_YIELDS_NULL
  • QUOTED_IDENTIFIER

1 如果 ANSI_WARNINGS 设置为 ON,则 ARITHABORT 将隐式设置为 ON。 因此,不必手动调整此设置。

另外,必须将 NUMERIC_ROUNDABORT 选项设置为 OFF。

若要强制查询优化器对索引视图使用索引,请指定 NOEXPAND 选项。 仅当查询中也命名了此视图时才能使用此提示。 SQL Server 不提供用于强制在查询中使用特定索引视图的提示,该查询不直接在 FROM 子句中命名该视图。 但是,查询优化器考虑使用索引视图,即使没有在查询中直接引用它们。 使用 NOEXPAND 表提示时,SQL Server 数据库引擎仅自动创建索引视图的统计信息。 忽略此提示可能会导致出现执行计划警告:缺少无法通过手动创建统计信息解决的统计信息。

查询优化期间,数据库引擎将使用查询直接引用视图时自动或手动创建的视图统计信息,并使用 NOEXPAND 提示。

将表提示用作查询提示

也可以使用 OPTION (TABLE HINT) 子句将表提示指定为查询提示。 我们建议仅在计划指南的上下文中将表提示用作查询提示。 对于即席查询,请将这些提示仅指定为表提示。 有关详细信息,请参阅 查询提示 (Transact-SQL)

权限

KEEPIDENTITY、IGNORE_CONSTRAINTS 和 IGNORE_TRIGGERS 提示需要具有对表的 ALTER 权限。

示例

A. 使用 TABLOCK 提示指定锁定方法

下面的示例指定对 AdventureWorks2022 数据库中的 Production.Product 表采用共享锁,并保持到 UPDATE 语句结束。

UPDATE Production.Product
WITH (TABLOCK)
SET ListPrice = ListPrice * 1.10
WHERE ProductNumber LIKE 'BK-%';
GO

B. 使用 FORCESEEK 提示指定索引查找操作

以下示例使用未指定索引的 FORCESEEK 提示强制查询优化器对 AdventureWorks2022 数据库中的 Sales.SalesOrderDetail 表执行索引查找操作。

SELECT *
FROM Sales.SalesOrderHeader AS h
INNER JOIN Sales.SalesOrderDetail AS d WITH (FORCESEEK)
    ON h.SalesOrderID = d.SalesOrderID
WHERE h.TotalDue > 100
AND (d.OrderQty > 5 OR d.LineTotal < 1000.00);
GO

以下示例使用指定索引的 FORCESEEK 提示强制查询优化器对指定的索引和索引列执行索引查找操作。

SELECT h.SalesOrderID, h.TotalDue, d.OrderQty
FROM Sales.SalesOrderHeader AS h
    INNER JOIN Sales.SalesOrderDetail AS d
    WITH (FORCESEEK (PK_SalesOrderDetail_SalesOrderID_SalesOrderDetailID (SalesOrderID)))
    ON h.SalesOrderID = d.SalesOrderID
WHERE h.TotalDue > 100
AND (d.OrderQty > 5 OR d.LineTotal < 1000.00);
GO

C. 使用 FORCESCAN 提示指定索引扫描操作

以下示例使用 FORCESCAN 提示强制查询优化器对 AdventureWorks2022 数据库中的 Sales.SalesOrderDetail 表执行扫描操作。

SELECT h.SalesOrderID, h.TotalDue, d.OrderQty
FROM Sales.SalesOrderHeader AS h
    INNER JOIN Sales.SalesOrderDetail AS d
    WITH (FORCESCAN)
    ON h.SalesOrderID = d.SalesOrderID
WHERE h.TotalDue > 100
AND (d.OrderQty > 5 OR d.LineTotal < 1000.00);