SQL Server的系统查询过程 负责在SELECT查询执行时候产生查询执行计划。SQL Server会“智能”地选择一个高效计划来取代低效的一个。大多数时候,SQL Server会把这份工作干得很棒。但正如有些人所担忧的,SQL Server并不是万能的,有时候,我们通过查询执行计划、表统计信息、支撑的索引及其他因素,研究性能之后,发现查询优化器选择的执行计划没有达到预期的效果,或者说,查询优化器做出了错误的选择。此时我们可能需要使用Hints(提示)来覆盖SQL Server查询优化器决定的过程。

  Hints(提示)是指定的强制选项或策略,由 SQL Server 查询处理器针对 SELECT、INSERT、UPDATE 或 DELETE 语句执行。提示将覆盖查询优化器可能为查询选择的任何执行计划。

  使用Hints之前,注意:

  (1)SQL Server 绝大多数情况下会做出正确的选择,即便使用的Hints短期内有效,但随着数据库内容的更改,使用的查询计划反而可能更高效,但此时因为Hints更霸道,SQL Server并不会“自作主张”地使用优化器。

  (2)SQL Server 发布补丁后,有效的Hints也可能会改变。

  SQL Server三种不同类型的Hints:联接提示(Join Hints)、查询提示(Query Hints)、表提示(Table Hints),包括SQL Server2008中引入的提示ForceSeek,可以用它将索引查找来替换索引扫描。

1、使用联接提示(Join Hints)

  官方解释:联接提示用于指定查询优化器在两个表之间强制执行联接策略。

  用法:<join_hint> ::= { LOOP | HASH | MERGE | REMOTE }

  联结提示会强制查询优化器来使用你命令的方式联结表,通过内部 JOIN 操作来实现。可用的联结提示:

提示名 描述
LOOP 当一个表很小另一个表很大并且在链接的列上有索引的情况下LOOP工作的很好
HASH HASH联结对于大的未排序的表最理想
MERGE MERGE联结对根据联结列排序的中大表最理想
REMOTE REMOTE联结应该左表为本地,且比远程表更少的行为佳

代码演示:

USE TestDb2
GO
IF NOT OBJECT_ID('HintsDemo','U') IS NULL
DROP TABLE HintsDemo
GO
IF NOT OBJECT_ID('HintsDemo2','U') IS NULL
DROP TABLE HintsDemo2
GO
----创建测试数据表
CREATE TABLE dbo.HintsDemo (HID int ,HTitle Nvarchar(50))
GO
CREATE TABLE dbo.HintsDemo2 (HID2 int ,HID int)
GO
----插入20条数据
INSERT INTO HintsDemo(HID,HTitle)
VALUES ( cast(rand()*10 AS INT),replicate('X',cast(rand()*25 AS INT)) )
GO 20--重复该语句20次 INSERT INTO HintsDemo2
SELECT TOP 10 cast(rand()*10 AS INT), HID from HintsDemo
GO 2--重复该语句2次 --此时两个表各有20条记录
SELECT * FROM HintsDemo
SELECT * FROM HintsDemo2 SET SHOWPLAN_XML ON
GO SELECT h.HID,h.HTitle,d.HID2
FROM HintsDemo h
INNER JOIN HintsDemo2 d ON
h.HID = d.HID
GO SET SHOWPLAN_XML OFF
GO

默认使用嵌套查询

  

下面使用Hash Join

SELECT h.HID,h.HTitle,d.HID2  
FROM HintsDemo h Inner HASH JOIN HintsDemo2 d ON h.HID = d.HID

  

2、使用查询提示(Query Hints)

  官方解释:在查询语句的持续时间内,查询提示优先于查询优化器的默认行为。您可以使用查询提示指定受影响的表的锁定方法、一个或多个索引、一个查询处理操作(如表扫描或索引查找)或其他选项。查询提示应用于整个查询。

  它的参数比较复杂:

<query_hint > ::=
{ { HASH | ORDER } GROUP | { CONCAT | HASH | MERGE } UNION | { LOOP | MERGE | HASH } JOIN | EXPAND VIEWS | FAST number_rows | FORCE ORDER | IGNORE_NONCLUSTERED_COLUMNSTORE_INDEX | KEEP PLAN | KEEPFIXED PLAN | MAXDOP number_of_processors | MAXRECURSION number
| OPTIMIZE FOR ( @variable_name { UNKNOWN | = literal_constant } [ , ...n ] )
| OPTIMIZE FOR UNKNOWN
| PARAMETERIZATION { SIMPLE | FORCED }
| RECOMPILE | ROBUST PLAN | USE PLAN N'xml_plan' | TABLE HINT ( exposed_object_name [ , <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
| SPATIAL_WINDOW_MAX_CELLS = integer | TABLOCK | TABLOCKX | UPDLOCK | XLOCK
}

  一般情况下, 我们可以在存储过程中使用ReCompile查询提示,这样可以实现SQL语句级的重编译,而不是整个存储过程(批处理语句)的重编译。我们通过一个示例来说明。

/****** 使用查询提示 ******/
DECLARE @HintsTitleDemo nvarchar(50) = '0E2FAB59-9A22-4E14-B7BE-33AB500E3B9E' SELECT HID,HTitle FROM HintsDemo
WHERE HTitle = @HintsTitleDemo
ORDER BY HID
/*HID HTitle6 0E2FAB59-9A22-4E14-B7BE-33AB500E3B9E*/ --使用DMV查询查看内存中的统计计划是否可以重用
SELECT cacheobjtype, objtype, usecounts
FROM sys.dm_exec_cached_plans
CROSS APPLY sys.dm_exec_sql_text(plan_handle)
WHERE text LIKE 'DECLARE @HintsTitleDemo%'

  

--先清除过程缓存,!!!请不要在生产环境中使用下句
DBCC FREEPROCCACHE; /* DBCC execution completed. If DBCC printed error messages, contact your system administrator.*/
DECLARE @HintsTitleDemo nvarchar(50) = '0E2FAB59-9A22-4E14-B7BE-33AB500E3B9E' SELECT HID,HTitle
FROM HintsDemo
WHERE HTitle = @HintsTitleDemo
ORDER BY HID
OPTION (RECOMPILE)--强制重新编译

  

  

再次友情提醒,绝大多数情况下,SQL Server 会做出较优的选择,极端情况下,我们才需要干预它的查询计划,以覆盖SQL Server的选择。

3、表提示(Table Hints)

  官方解释:通过指定锁定方法、一个或多个索引、查询处理操作(如表扫描或索引查找)或其他选项,表提示可在数据操作语言 (DML) 语句执行期间覆盖查询优化器的默认行为。表提示在 DML 语句的 FROM 子句中指定,仅影响在该子句中引用的表或视图。

表提示与查询提示类似,用于覆盖Select、INSERT、UPDATE和DELETE的默认行为。可以为一个表设置多个表提示,并使用逗号分开,只要它们不改属于同个分组即可。

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

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

  值得注意的是 NOlOCK 选项,如下语句:

  

--不锁定执行查询
SELECT HID,HTitle
FROM HintsDemo
WITH (NOLOCK)
WHERE HID = 4 /*
HID HTitle
4 E6DA3DB2-3D41-47B4-B4E3-DDA90918434C
4 1C4C9211-EB1C-42B5-A08A-558DC73462B4
4 667C9985-3B0A-4767-AED9-82FEE623433D
*/

 NOLOCK 表提示让查询在不在被影响的行或数据是放置共享锁——允许你在不被阻塞或不阻塞其他查询的情况下读取(但会遇到“脏读”问题)。

  最后,SQL Server 2008引入的 FORCESEEK 表提示,它可以用来将 索引扫描 替换为 索引查找。会有一些原因导致SQL Server产生不良的查询计划。例如表数据经常变化并且信息忆不再准确,或带有拙劣where子句的查询没有为查询优化器过程提供有用的或足够的信息。

  如果为了单独查找指定数据,而在检索一行之前对整个非常大的表进行了整表扫描,由此带来的I/O开销当然是不能接受的。假定上面的测试表非常大。

  

  

SET SHOWPLAN_XML ON
GO
--本例使用With(ForceSeek)未必最优,只是提供了一个修改系统访问数据的方式
SELECT DISTINCT HTitle from HintsDemo WITH (FORCESEEK)
WHERE HID BETWEEN 8 AND 10 and HTitle ='141466E4-E8CC-4219-A9AF-7C0D2B86A668'
GO SET SHOWPLAN_XML OFF

  

  也可以更进一步指定使用哪个索引

SELECT DISTINCT HTitle from HintsDemo WITH (FORCESEEK,INDEX(idx_ForceSeekDemo))
WHERE HID BETWEEN 8 AND 10 and HTitle ='141466E4-E8CC-4219-A9AF-7C0D2B86A668'
GO

对于本例,如果你需要得更好的性能,可以考虑使用SQL Server引入的指定行集索引功能,比如你只关注某房价表中均价在5000-6000元的楼盘信息,那么可以专门为这个区间建立索引。

  小结:本文简要介绍SQL Server三种不同类型的联接提示(Join Hints)、查询提示(Query Hints)、表提示(Table Hints),包括SQL Server2008中引入的提示ForceSeek,可以用它将索引查找来替换索引扫描。

注意:提示需慎用!!!

SQL Server 2008中的Hints(提示)的简单整理的更多相关文章

  1. SQL Server 2008中的CDC(Change Data Capture)功能使用及释疑

    SQL Server 2008中的CDC(Change Data Capture)功能使用及释疑 关键词:CDC   原文:http://www.cnblogs.com/chenxizhang/arc ...

  2. SQL Server 2008中的数据压缩

    SQL Server 2008中引入了数据压缩的功能,允许在表.索引和分区中执行数据压缩.这样不仅可以大大节省磁盘的占用空间,还允许将更多数据页装入内存中,从而降低磁 盘IO,提升查询的性能.当然,凡 ...

  3. SQL Server 2008中新增的 1.变更数据捕获(CDC) 和 2.更改跟踪

    概述 1.变更数据捕获(CDC)        每一次的数据操作都会记录下来 2.更改跟踪       只会记录最新一条记录   以上两种的区别:         http://blog.csdn.n ...

  4. SQL Server 2008中新增的变更数据捕获(CDC)和更改跟踪

    来源:http://www.cnblogs.com/downmoon/archive/2012/04/10/2439462.html  本文主要介绍SQL Server中记录数据变更的四个方法:触发器 ...

  5. SQL Server 2008中增强的"汇总"技巧

    本文转载:http://www.cnblogs.com/downmoon/archive/2012/04/06/2433988.html SQL Server 2008中的Pivot和UnPivot: ...

  6. SQL Server 2008中数据压缩

    SQL Server 2008中引入了数据压缩的功能,允许在表.索引和分区中执行数据压缩.这样不仅可以大大节省磁盘的占用空间,还允许将更多数据页装入内存中,从而降低磁盘IO,提升查询的性能.当然,凡事 ...

  7. 利用Ring Buffer在SQL Server 2008中进行连接故障排除

    原文:利用Ring Buffer在SQL Server 2008中进行连接故障排除 出自:http://blogs.msdn.com/b/apgcdsd/archive/2011/11/21/ring ...

  8. SQL点滴15—在SQL Server 2008中调用C#程序

    原文:SQL点滴15-在SQL Server 2008中调用C#程序 T-SQL的在执行普通的查询的时候是很高效的,但是在执行循环,判断这样的语句的时候效率就不那么的高了.这时可以借助CLR了,我们可 ...

  9. 在SQL Server 2008中调用.net,dll

    原文:在SQL Server 2008中调用.net,dll T-SQL的在执行普通的查询的时候是很高效的,但是在执行循环,判断这样的语句的时候效率就不那么的高了.这时可以借助CLR了,我们可以在SQ ...

随机推荐

  1. 【NOIP2014】生活大爆炸版石头剪刀布

    石头剪刀布是常见的猜拳游戏:石头胜剪刀,剪刀胜布,布胜石头.如果两个人出拳一 样,则不分胜负.在<生活大爆炸>第二季第 8 集中出现了一种石头剪刀布的升级版游戏. 升级版游戏在传统的石头剪 ...

  2. 如何在java中跳出当前多重嵌套循环?有几种方法?

    如何在java中跳出当前多重嵌套循环?有几种方法? - 两种方法   - 1.在外层循环定义标记          ok:          for(int i=0;i<100;i++){    ...

  3. jquery.uploadify 使用过程

    HTML: <html xmlns="http://www.w3.org/1999/xhtml"> <head runat="server"& ...

  4. Tasker to detect application running in background

    We used to be told that tasker is only capable of detecting foreground application, if the app gets ...

  5. USB ISP(ICSP) Open Programmer < PWM ADC HV PID >

    http://sourceforge.net/projects/openprogrammer/?source=navbar Open Programmer http://openprog.alterv ...

  6. oracle 锁系列

    http://www.cnblogs.com/lhrbest/p/6091277.html

  7. JDBC 连Sql Server 接数据库--The TCP/IP connection to the host localhost, port 1433 has failed

    原文:https://blog.csdn.net/qq_39241986/article/details/80848855 这样的错误,你有遇到过吗? The TCP/IP connection to ...

  8. #pragma mark 添加分割线 及 其它类似标记 - 转

    #pragma marks Comments containing: MARK: TODO: FIXME: !!!: ???: 除了使用 #pragma mark -添加分割线之外, 以上几种标记均可 ...

  9. Android下setLatestEventInfo警告、Handler警告、SimpleDateFormat警告

    正 文: 今天飘易在做Android 4.4.2下的APP开发时,使用了Notification下的setLatestEventInfo()方法时,Eclipse却提示:“ 不建议使用类型 Notif ...

  10. 比起 JSON 更方便、更快速、更簡短的 Protobuf 格式

    Protocol Buffers 是由 Google 所推出的一格式(後台真硬),你可以把它想像成是 XML 或 JSON 格式,但是更小.更快,而且更簡潔.這能夠幫你節省網路與硬體資源,且你只需要定 ...