对于SQLServer的SQL性能优化技术

1。选择最有效的表名称顺序(仅在基于规则的优化器中有效)

在从FROM子句中的条款从右到左的顺序表按照SQLServer解析器,在表的末尾写(基于表驱动表)将首先处理,在FROM子句中包含多个表,必须选择的记录数在表一个基本表,当许多台SQLSERVER处理,他们会使用排序合并连接方式,

首先,扫描第一个表(from子句中的最后一个表)并对记录进行排序;然后扫描第二个表(from子句中最后的第二个表);最后,将从第二个表检索到的所有记录与第一个表中的适当记录组合起来。

例如:表16384表5表记录,对记录,TAB2作为基本表(最好的方法)select count(*)从表1 TAB2,0.96秒的执行时间,TAB2作为基本表(差法)select count(*)从表2 1,26.09秒执行时间;

如果有超过3个表连接查询,那么我们需要选择交叉表(交集表)作为基表,而交叉表是指其他表引用的表。

例如:

EMP表描述的位置表和catery表的交集


*选择
从位置L,
catery C,
EMP E
在1000和2000之间的e.emp_no
和e.cat_no = c.cat_no
和e.locn = l.locn


它比下面的SQL更有效


*选择
来自EMP E,
位置L,
catery C
在e.cat_no = c.cat_no
和e.locn = l.locn
1000和2000之间e.emp_no


在2.where子句中的连接顺序

SQLServer使用自下而上的顺序解析WHERE子句。根据这个原则,表之间的连接必须写在其他条件之前。可以筛选出最多记录数的条件必须写在WHERE子句的结尾处。

例如:

(效率低下,执行时间为156.3秒)


*选择
从EMP E
萨尔50000
工作= 'manager
25 <(select count(*)从EMP在MGR = e.empno);
(效率高,执行时间10.6秒)
*选择
从EMP E
25 <(select count(*)从EMP在MGR = e.empno)
萨尔50000
工作= 'manager;


避免使用*在3.select条款。当你想在SELECT子句中的所有列的列表,使用动态SQL列引用*是一个方便的方法,不幸的是,这是一个非常低效的方法。事实上,在解析过程中,SQLServer,将*的转换所有的名字,这是通过查询数据字典完成的,这意味着将有更多的时间



4。减少对数据库的访问次数。执行SQL语句,每时,SQLServer执行许多任务内部:解析SQL语句,利用估算指标,结合变量,读取数据块,等等。

因此,可以看出,减少对数据库的访问次数可以减少SQLServer的工作量,例如:

有三种方法可以检索雇员人数等于0342或0291的雇员。

方法1(效率最高)


选择emp_name,工资等级
从EMP
在emp_no = 342;
选择emp_name,工资等级
从EMP
在emp_no = 291;


方法2(次低效率)


声明
光标C1(e_no号)
选择emp_name,工资等级
从EMP
在emp_no = e_no;
开始
打开C1(342);
将C1输入…,…,…;

打开C1(291);
将C1输入…,…,…;

关闭C1;
结束;


方法2(高效率)


选择a.emp_name,A.SALARY,a.grade,
b.emp_name,B.SALARY,b.grade
来自EMP A,EMP B
在a.emp_no = 342
和b.emp_no = 291;


5。使用解码功能减少处理时间



使用解码功能可以避免重复扫描同一记录或重复同一表。



例如:


选择计数(*),和(萨尔)
从EMP
在dept_no = '0020
和不like'smith %;

选择计数(*),和(萨尔)
从EMP
在dept_no = '0030
和不like'smith %;


使用解码函数可以得到同样的结果。


select count(解码(dept_no,'0020 ','X',null))d0020_count,
计数(解码(dept_no,'0030 ','X',null))d0030_count,
和(解码(dept_no,'0020,萨尔,null))d0020_sal,
和(解码(dept_no,0030,萨尔,null))d0030_sal
从EMP
在一个like'smith %;


x'represents任何领域

同样,解码功能也可用于按组和按子句排序。



6。用WHERE子句替换带子句



为了避免使用带子句,只对结果集进行过滤,直到检索到所有记录,这需要排序、统计等。



如果可以通过WHERE子句限制记录的数量,则可以降低这方面的成本。



例如:

效率低下


选择区域,AVG(log_size)
从位置
集团由区域
具有区域区域!= 'sydney
和区域!= 'perth

高效
选择区域,AVG(log_size)
从位置
区域区域!= 'sydney
和区域!= 'perth
集团由区域


7。减少对表的查询



在含有子查询的SQL语句,要特别注意减少表的查询。

例如:


效率低下
选择tab_name
从表
在tab_name =(选择tab_name)
从tab_columns
版本= 604)
和db_ver =(选择db_ver)
从tab_columns
版本= 604)

高效
选择tab_name
从表
在(tab_name,db_ver)=(选择tab_name,db_ver)
从tab_columns
版本= 604)


更新多个列示例:


效率低下
更新EMP
集emp_cat =(选择最大(catery))
从emp_cateries),
sal_range =(选择最大(sal_range)
从emp_cateries)
在emp_dept = 0020;

高效
更新EMP
集(emp_cat,sal_range)=(选择最大(catery),最大(sal_range))
从emp_cateries)
在emp_dept = 0020;


8、使用表别名(别名)。在SQL语句中连接多个表时,请使用表的别名并在每个列上添加别名,这样可以减少分析的时间,减少列歧义造成的语法错误。



9。代替存在



在许多基于查询的表查询中,为了满足一个条件,常常需要加入另一个表。



在这种情况下,使用存在(或不存在)通常会提高查询的效率。


效率低下
*选择
来自EMP(基表)
在EMPNO > 0
和部门在(SELECT DEPTNO)
从系
在LOC = 'melb)

高效
*选择
来自EMP(基表)
在EMPNO > 0
存在(select'x)
从系
在dept.deptno = emp.deptno
和LOC = 'melb)


10。而不是不存在



在子查询中,在条款的不将执行一个内部排序和归并



在任何情况下,不在是最低效的,因为它执行的子查询中的表的全表遍历



为了避免使用不在,我们可以重写它作为外部连接(外部连接)或不存在。



例如:


选择…
从EMP
在dept_no没有(选择dept_no)
从系
在dept_cat =);


重写效率以提高效率


高效
选择…
来自EMP A,B部
在a.dept_no =十堰(+)
和b.dept_no是空的
和b.dept_cat(+)= A

最有效的
选择…
从EMP E
在不存在(select'x)
从部门D
在d.dept_no = e.dept_no
和dept_cat = A);


11。用表连接替换



一般来说,表连接比现有的效率更高。



例如:


选择一个
从EMP E
在存在(select'x)
从系
在dept_no = e.dept_no
和dept_cat = A);

更有效
选择一个
从部门D,EMP E
在e.dept_no = d.dept_no
和dept_cat = A;


12。用存在替换



当提交涉及多个表信息(如部门表和雇员表)的查询时,避免在SELECT子句中使用不同的语句,通常可以考虑替换。



例如:


效率低下
选择不同的dept_no,dept_name
从部门D,EMP E
在d.dept_no = e.dept_no

高效
选择dept_no,dept_name
从部门D
在存在(select'x)
从EMP E
在e.dept_no = d.dept_no);


的存在,使得查询更加快速,因为RDBMS核心模块返回子查询满足条件后立即。



13。按指数提高效率



索引是一个概念性的部分表格提高数据检索的效率。事实上,SQLServer使用复杂的自平衡树结构



一般来说,数据查询通过索引比全表扫描的速度更快。当SQLServer找到最佳的路径来执行查询和更新语句,SQLServer优化器将使用索引



同样,使用索引连接多个表也可以提高效率。使用索引的另一个优点是它提供了对主键(主键)的唯一验证。



除了那些长或长的原始数据类型之外,您还可以对几乎所有的列进行索引。



在大表中使用索引通常是有效的。当然,使用索引也可以提高扫描小表时的效率。



虽然索引的使用可以提高查询的效率,但我们也必须注意它的成本。



索引需要存储空间,也需要定期维护。每当记录添加到表中或索引列被修改时,索引本身将被修改。



这意味着每个记录的插入、删除和更新将为此支付超过4, 5个磁盘I/O。



因为索引需要额外的存储空间和处理,不必要的索引会减慢查询的反应时间。



SQLServer有两种访问模式的指标:



1)。索引唯一扫描(索引唯一扫描)



在大多数情况下,优化器通过WHERE子句访问索引。



例如:

表倒伏有两个指标:独特的指数lodging_pk建立在住宿的柱和非唯一索引列的经理,经理住宿美元

*选择

从住宿

在住宿= 'rose山;



在内部,SQL将被分为两个步骤:



首先,该lodging_pk指数将通过索引扫描,得到相应的rowid。然后rowid将访问表来进行下一步的检索。



如果列的检索返回的是包含在索引列时,SQL Server将不会执行第二步过程(通过ROWID访问表)



由于检索的数据存储在索引中,单个访问索引可以完全满足查询结果。



2)。索引范围查询(索引范围扫描)



适用于两种情况:



1 >。基于唯一索引的检索范围



检索2 >。基于非唯一指标



1例

选择住宿

从住宿

住的地方像是%;



WHERE子句条件包括一系列的价值观,和SQLServer将通过索引范围查询查询lodging_pk



因为索引范围查询将返回一组值,所以它比索引惟一扫描效率低。



2例

选择住宿

从住宿

在经理=比尔盖茨;



该SQL的执行分为两步,住宿$经理指数范围查询(把所有合格的rowid)、倒伏和列的值是通过ROWID访问表中获得。



由于寄宿管理器是一个非唯一索引,所以数据库不能对它执行索引惟一扫描。



在WHERE子句中,如果与索引列对应的值的第一个字符由通配符(通配符)启动,则索引将不被使用。

选择住宿

从住宿

那里的经理是该;



在这种情况下,SQL Server将使用全表扫描



14。避免在索引列上使用计算



在WHERE子句中,如果索引列是函数的一部分,优化器将使用一个完整的表扫描而不使用索引。



例如:



效率低下

选择…

从系

萨尔* 12>25000;



高效

选择…

从系

12 > 25000;



需要注意的是,索引的列不应该处理在搜索重要,如修剪,to_date,类型转换等操作,从而破坏指数和使用全表扫描,SQL执行效率的影响。



15。避免在索引列中使用NULL而不是NULL。



避免使用任何空列在索引中,SQLServer将无法使用索引



对于单列索引,如果列包含空值,则该记录将不在索引中存在。



对于复合索引,如果每个列为空,则该记录不存在于索引中。如果至少有一列不是空的,则记录存在于索引中。



如果唯一性索引是建立在表的一个列和B列上,并且表中有一个记录A,则b值是(123,null),



SQLServer将不接受同一个下一个入口,B(123,null)



如果所有的索引列是空的,SQL Server将假定整个核心价值是空的,和空白不等于空。因此,您可以插入1000个具有相同键值的记录,当然,它们都是空的。



因为空值不存在于索引列中,空值比较的索引列在WHERE子句中会导致SQLServer禁用索引



低效(指数故障)

选择…

从部门

在dept_code不空



16。使用union-all和联盟



当SQL语句需要联合两个查询结果集,两结果集将在union-all合并,然后在输出最终结果排序。



如果用全部代替并集,则不需要这样的排序,提高了效率。



需要注意的是,在两个结果集的输出中将会重复相同的记录,因此仍然有可能分析从业务需求使用联合所有的可行性。



有关索引的以下经验,请参阅以下内容:



1)。如果检索表中有超过30%个数据的记录数,则使用索引的效率没有显著的提高。



2)。在某些情况下,使用索引可能比全表扫描慢,但这是相同数量级的。一般来说,使用索引比全表扫描要快几倍甚至是数千倍。