对于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)。在某些情况下,使用索引可能比全表扫描慢,但这是相同数量级的。一般来说,使用索引比全表扫描要快几倍甚至是数千倍。