SQL优化(查询)

1.选择最有效率的表名顺序(只在基于规则的优化器中有效)     
   SQLSERVER的解析器按照从右到左的顺序处理FROM子句中的表名,因此FROM子句中写在最后的表(基础表driving table)将被最先处理,在FROM子句中包含多个表的情况下,必须选择记录条数最少的表作为基础表,当SQLSERVER处理多个表时,会运用排序及合并的方式连接它们,  

   首先,扫描第一个表(FROM子句中最后的那个表)并对记录进行排序;然后扫描第二个表(FROM子句中最后第二个表);最后将所有从第二个表中检索出的记录与第一个表中合适记录进行合并  

   例如: 表 TAB1 16,384 条记录表 TAB2 5 条记录,选择TAB2作为基础表 (最好的方法) select count(*) from tab1,tab2 执行时间0.96秒,选择TAB2作为基础表 (不佳的方法) select count(*) from tab2,tab1 执行时间26.09秒;  

如果有3个以上的表连接查询,那就需要选择交叉表(intersection table)作为基础表,交叉表是指那个被其他表所引用的表     
   例如:  
   EMP表描述了LOCATION表和CATEGORY表的交集  
   SELECT *  
   FROM LOCATION L,  
   CATEGORY C,  
   EMP E  
   WHERE E.EMP_NO BETWEEN 1000 AND 2000  
   AND E.CAT_NO = C.CAT_NO  
   AND E.LOCN = L.LOCN  
     
   将比下列SQL更有效率  
   SELECT *  
   FROM EMP E ,  
   LOCATION L ,  
   CATEGORY C  
   WHERE E.CAT_NO = C.CAT_NO  
   AND E.LOCN = L.LOCN  
   AND E.EMP_NO BETWEEN 1000 AND 2000  
     
   2.WHERE子句中的连接顺序     
   SQLSERVER采用自下而上的顺序解析WHERE子句,根据这个原理,表之间的连接必须写在其他WHERE条件之前,那些可以过滤掉最大数量记录的条件必须写在WHERE子句的末尾     
   例如:  
   (低效,执行时间156.3秒)  
   SELECT *  
   FROM EMP E  
   WHERE SAL > 50000  
   AND JOB = ’MANAGER’  
   AND 25 < (SELECT COUNT(*) FROM EMP WHERE MGR=E.EMPNO);     
   (高效,执行时间10.6秒)  
   SELECT *  
   FROM EMP E  
   WHERE 25 < (SELECT COUNT(*) FROM EMP WHERE MGR=E.EMPNO)  
   AND SAL > 50000  
   AND JOB = ’MANAGER’;  
     
   3.SELECT子句中避免使用’*’。当你想在SELECT子句中列出所有的COLUMN时,使用动态SQL列引用’*’是一个方便的方法,不幸的是,这是一个非常低效的方法。实际上,SQLSERVER在解析的过程中,会将’*’依次转换成所有的列名,这个工作是通过查询数据字典完成的,这意味着将耗费更多的时间  

     
   4.减少访问数据库的次数。当执行每条SQL语句时,SQLSERVER在内部执行了许多工作:解析SQL语句,估算索引的利用率,绑定变量,读数据块等等  
   由此可见,减少访问数据库的次数,就能实际上减少SQLSERVER的工作量,例如:  
   以下有三种方法可以检索出雇员号等于0342或0291的职员     
   方法1 (最低效)  
   SELECT EMP_NAME, SALARY, GRADE  
   FROM EMP  
   WHERE EMP_NO = 342;   
   SELECT EMP_NAME, SALARY, GRADE  
   FROM EMP  
   WHERE EMP_NO = 291;  
     
   方法2 (次低效)  
   DECLARE  
   CURSOR C1 (E_NO NUMBER) IS  
   SELECT EMP_NAME,SALARY,GRADE  
   FROM EMP  
   WHERE EMP_NO = E_NO;  
   BEGIN  
   OPEN C1(342);  
   FETCH C1 INTO …,…,…;  
   …  
   OPEN C1(291);  
   FETCH C1 INTO …,…,…;  
   …  
   CLOSE C1;  
   END;  
     
   方法2 (高效)  
   SELECT A.EMP_NAME, A.SALARY, A.GRADE,  
   B.EMP_NAME, B.SALARY, B.GRADE  
   FROM EMP A, EMP B  
   WHERE A.EMP_NO = 342  
   AND B.EMP_NO = 291;  
     
   5.使用DECODE函数来减少处理时间  

     
   使用DECODE函数可以避免重复扫描相同记录或重复连接相同的表  
     
   例如:  
   SELECT COUNT(*), SUM(SAL)  
   FROM EMP  
   WHERE DEPT_NO = ’0020’  
   AND ENAME LIKE ’SMITH%’;  
     
   SELECT COUNT(*), SUM(SAL)  
   FROM EMP  
   WHERE DEPT_NO = ’0030’  
   AND ENAME LIKE ’SMITH%’;  
     
   你可以用DECODE函数高效地得到相同结果  
   SELECT COUNT(DECODE(DEPT_NO, ’0020’, ’X’, NULL)) D0020_COUNT,  
   COUNT(DECODE(DEPT_NO, ’0030’, ’X’, NULL)) D0030_COUNT,  
   SUM(DECODE(DEPT_NO, ’0020’, SAL, NULL)) D0020_SAL,  
   SUM(DECODE(DEPT_NO, 0030, SAL, NULL)) D0030_SAL  
   FROM EMP  
   WHERE ENAME LIKE ’SMITH%’;  
     
   ’X’表示任何一个字段  
   类似的,DECODE函数也可以运用于GROUP BY和ORDER BY子句中  
     
   6.用Where子句替换HAVING子句  
     
   避免使用HAVING子句,HAVING只会在检索出所有记录之后才对结果集进行过滤,这个处理需要排序、统计等操作  
     
   如果能通过WHERE子句限制记录的数目,那就能减少这方面的开销  
     
   例如:  
   低效  
   SELECT REGION, AVG(LOG_SIZE)  
   FROM LOCATION  
   GROUP BY REGION  
   HAVING REGION REGION != ’SYDNEY’  
   AND REGION != ’PERTH’  
     
   高效  
   SELECT REGION, AVG(LOG_SIZE)  
   FROM LOCATION  
   WHERE REGION REGION != ’SYDNEY’  
   AND REGION != ’PERTH’  
   GROUP BY REGION  
     
   7.减少对表的查询  
     
   在含有子查询的SQL语句中,要特别注意减少对表的查询     
   例如:     
   低效  
   SELECT TAB_NAME  
   FROM TABLES  
   WHERE TAB_NAME = (SELECT TAB_NAME  
   FROM TAB_COLUMNS  
   WHERE VERSION = 604)  
   AND DB_VER = (SELECT DB_VER  
   FROM TAB_COLUMNS  
   WHERE VERSION = 604)  
     
   高效  
   SELECT TAB_NAME  
   FROM TABLES  
   WHERE (TAB_NAME, DB_VER) = (SELECT TAB_NAME, DB_VER  
   FROM TAB_COLUMNS  
   WHERE VERSION = 604)  

     
   Update多个Column例子:  
   低效  
   UPDATE EMP  
   SET EMP_CAT = (SELECT MAX(CATEGORY)  
   FROM EMP_CATEGORIES),  
   SAL_RANGE = (SELECT MAX(SAL_RANGE)  
   FROM EMP_CATEGORIES)  
   WHERE EMP_DEPT = 0020;  
     
   高效  
   UPDATE EMP  
   SET (EMP_CAT, SAL_RANGE) = (SELECT MAX(CATEGORY), MAX(SAL_RANGE)  
   FROM EMP_CATEGORIES)  
   WHERE EMP_DEPT = 0020;  
     
   8.使用表的别名(Alias),当在SQL语句中连接多个表时,请使用表的别名并把别名前缀于每个Column上,这样可以减少解析的时间并减少那些由Column歧义引起的语法错误  
     
   9.用EXISTS替代IN  
     
   在许多基于基础表的查询中,为了满足一个条件,往往需要对另一个表进行联接  
     
   在这种情况下,使用EXISTS(或NOT EXISTS)通常将提高查询的效率  
     
   低效  
   SELECT *  
   FROM EMP (基础表)  
   WHERE EMPNO > 0  
   AND DEPTNO IN (SELECT DEPTNO  
   FROM DEPT  
   WHERE LOC = ’MELB’)  
     
   高效  
   SELECT *  
   FROM EMP (基础表)  
   WHERE EMPNO > 0  
   AND EXISTS (SELECT ’X’  
   FROM DEPT  
   WHERE DEPT.DEPTNO = EMP.DEPTNO  
   AND LOC = ’MELB’)  
     
   10.用NOT EXISTS替代NOT IN  
     
   在子查询中,NOT IN子句将执行一个内部的排序和合并  
     
   无论在哪种情况下,NOT IN都是最低效的,因为它对子查询中的表执行了一个全表遍历  
     
   为了避免使用NOT IN,我们可以把它改写成外连接(Outer Joins)或NOT EXISTS  
     
   例如:  
   SELECT …  
   FROM EMP  
   WHERE DEPT_NO NOT IN (SELECT DEPT_NO  
   FROM DEPT  
   WHERE DEPT_CAT = ’A’);  
     
   为了提高效率改写为  
   高效  
   SELECT …  
   FROM EMP A, DEPT B  
   WHERE A.DEPT_NO = B.DEPT(+)  
   AND B.DEPT_NO IS NULL  
   AND B.DEPT_CAT(+) = ’A’  
     
   最高效  
   SELECT …  
   FROM EMP E  
   WHERE NOT EXISTS (SELECT ’X’  
   FROM DEPT D  
   WHERE D.DEPT_NO = E.DEPT_NO  
   AND DEPT_CAT = ’A’);  
     
   11.用表连接替换EXISTS  
     
   通常来说,采用表连接的方式比EXISTS更有效率  
     
   例如:  
   SELECT ENAME  
   FROM EMP E  
   WHERE EXISTS (SELECT ’X’  
   FROM DEPT  
   WHERE DEPT_NO = E.DEPT_NO  
   AND DEPT_CAT = ’A’);  
     
   更高效  
   SELECT ENAME  
   FROM DEPT D, EMP E  
   WHERE E.DEPT_NO = D.DEPT_NO  
   AND DEPT_CAT = ’A’;  
     
   12.用EXISTS替换DISTINCT  
     
   当提交一个包含多表信息(比如部门表和雇员表)的查询时,避免在SELECT子句中使用DISTINCT,一般可以考虑用EXIST替换  
     
   例如:  
     
   低效  
   SELECT DISTINCT DEPT_NO, DEPT_NAME  
   FROM DEPT D, EMP E  
   WHERE D.DEPT_NO = E.DEPT_NO  
     
   高效  
   SELECT DEPT_NO, DEPT_NAME  
   FROM DEPT D  
   WHERE EXISTS (SELECT ’X’  
   FROM EMP E  
   WHERE E.DEPT_NO = D.DEPT_NO);  
     
   EXISTS使查询更为迅速,因为RDBMS核心模块将在子查询的条件一旦满足后,立刻返回结果  
     
   13.用索引提高效率  
     
   索引是表的一个概念部分,用来提高检索数据的效率。实际上,SQLSERVER使用了一个复杂的自平衡B-tree结构  
     
   通常,通过索引查询数据比全表扫描要快。当SQLSERVER找出执行查询和Update语句的最佳路径时,SQLSERVER优化器将使用索引  
     
   同样,在联结多个表时使用索引也可以提高效率。另一个使用索引的好处是,它提供了主键(primary key)的唯一性验证  
     
   除了那些LONG或LONG RAW数据类型,你可以索引几乎所有的列  
     
   通常在大型表中使用索引特别有效,当然,在扫描小表时,使用索引同样能提高效率  
     
   虽然使用索引能得到查询效率的提高,但是我们也必须注意到它的代价  
     
   索引需要空间来存储,也需要定期维护,每当有记录在表中增减或索引列被修改时,索引本身也会被修改  
     
   这意味着每条记录的INSERT、DELETE、UPDATE将为此多付出4、5次的磁盘I/O  
     
   因为索引需要额外的存储空间和处理,那些不必要的索引反而会使查询反应时间变慢  
     
   SQLSERVER对索引有两种访问模式:  
     
   1).索引唯一扫描(INDEX UNIQUE SCAN)  
     
   大多数情况下, 优化器通过WHERE子句访问INDEX  
     
   例如:  
   表LODGING有两个索引:建立在LODGING列上的唯一性索引LODGING_PK和建立在MANAGER列上的非唯一性索引LODGING$MANAGER  
   SELECT *  
   FROM LODGING  
   WHERE LODGING = ’ROSE HILL’;  
     
   在内部,上述SQL将被分成两步执行:  
     
   首先,LODGING_PK索引将通过索引唯一扫描的方式被访问,获得相对应的ROWID;然后通过ROWID访问表的方式执行下一步检索  
     
   如果被检索返回的列包括在INDEX列中,SQLSERVER将不执行第二步的处理(通过ROWID访问表)  
     
   因为检索数据保存在索引中,单单访问索引就可以完全满足查询结果  
     
   2).索引范围查询(INDEX RANGE SCAN)  
     
   适用于两种情况:  
     
   1>.基于唯一性索引的一个范围的检索  
     
   2>.基于非唯一性索引的检索  
     
   例1  
   SELECT LODGING  
   FROM LODGING  
   WHERE LODGING LIKE ’M%’;  
     
   WHERE子句条件包括一系列值,SQLSERVER将通过索引范围查询的方式查询LODGING_PK  
     
   由于索引范围查询将返回一组值,它的效率就要比索引唯一扫描低一些  
     
   例2  
   SELECT LODGING  
   FROM LODGING  
   WHERE MANAGER = ’BILL GATES’;  
     
   这个SQL的执行分两步,LODGING$MANAGER的索引范围查询(得到所有符合条件记录的ROWID),通过ROWID访问表得到LODGING列的值  
     
   由于LODGING$MANAGER是一个非唯一性的索引,数据库不能对它执行索引唯一扫描  
     
   WHERE子句中,如果索引列所对应的值的第一个字符由通配符(WILDCARD)开始,索引将不被采用  
   SELECT LODGING  
   FROM LODGING  
   WHERE MANAGER LIKE ’%HANMAN’;  
     
   在这种情况下,SQLSERVER将使用全表扫描  
     
   14.避免在索引列上使用计算  
     
   WHERE子句中,如果索引列是函数的一部分,优化器将不使用索引而使用全表扫描  
     
   例如:  
     
   低效  
   SELECT …  
   FROM DEPT  
   WHERE SAL * 12 > 25000;  
     
   高效  
   SELECT …  
   FROM DEPT  
   WHERE SAL > 25000/12;  
     
   请务必注意,检索中不要对索引列进行处理,如:TRIM,TO_DATE,类型转换等操作,破坏索引,使用全表扫描,影响SQL执行效率  
     
   15.避免在索引列上使用IS NULL和IS NOT NULL  
     
   避免在索引中使用任何可以为空的列,SQLSERVER将无法使用该索引  
     
   对于单列索引,如果列包含空值,索引中将不存在此记录;  
     
   对于复合索引,如果每个列都为空,索引中同样不存在此记录。如果至少有一个列不为空,则记录存在于索引中  
     
   如果唯一性索引建立在表的A列和B列上,并且表中存在一条记录的A,B值为(123,null),  
     
   SQLSERVER将不接受下一条具有相同A,B值(123,null)的记录插入  
     
   如果所有的索引列都为空,SQLSERVER将认为整个键值为空,而空不可能等于空,因此你可以插入1000条具有相同键值的记录,当然它们都是空!  
     
   因为空值不存在于索引列中,所以WHERE子句中对索引列进行空值比较将使SQLSERVER停用该索引  
     
   低效(索引失效)  
   SELECT …  
   FROM DEPARTMENT  
   WHERE DEPT_CODE IS NOT NULL  
     
   16.使用UNION-ALL和UNION  
     
   当SQL语句需要UNION两个查询结果集合时,这两个结果集合会以UNION-ALL的方式被合并,然后在输出最终结果前进行排序  
     
   如果用UNION ALL替代UNION,这样排序就不是必要了,效率就会因此得到提高  
     
   需要注意的是,UNION ALL将重复输出两个结果集合中相同记录,因此还是要从业务需求分析使用UNION ALL的可行性  
     
   关于索引下列经验请参考:  
     
   1).如果检索数据量超过30%的表中记录数,使用索引将没有显著的效率提高  
     
   2).在特定情况下,使用索引也许会比全表扫描慢,但这是同一个数量级上的差距;而通常情况下,使用索引比全表扫描要快几倍乃至几千倍!
SQLServer安全及性能优化 修补漏洞 安装程序补丁修补漏洞 随时关注微软官方网站补丁升级 关闭不必要的端口 关闭联必要的服务 数据库引擎 SQL Server Analysis Services SQL Server Reporting Services SQL Server Integration Services SQL Server 代理 SQL Full-text Filter Daemon launcher SQL Server Browser 同时开启所有服务系统性能会变得很差,根据需要手动启动或者禁用某个服务 DTC: Distributed Transaction Coordinator(分布式事务处理协调器),用于协调多个数据库、消息队列、文件系统等等资源管理器的事务,由于内部开发中并不使用这个功能,远程数据库服务器上也并不经常使用,因此建议关闭这个服务 禁用不使用的协议 Shared Memory 默认为已启用状态,这个协议只能用于本地连接,不能用于远程连接,一般用于其它协议出问题的时候管理作诊断使用 TCP/IP 禁用不需要使用的协议,减少网络攻击对象 减少监听的网卡和IP地址 改变监听端口号 安全地设置账户 Windows身份验证[微软推荐的方式] 优势: 1.访问SqlServer时速度更快,不用输入用户名和密码 2.可以利用Windows系统的自身工具和安全策略管理账户 3.安全确认和口令加密、审核、口令失效、最小口令长度和账号锁定 SqlServer身份验证 1.将sa账户名更改为其它账户名比如nocial,防止黑客利用sa进行攻击 2.删除不使用的账户 3.对已有账户设置安全密码[强制密码规则] 4.限制登录->远程登录、匿名登录 5.限制用户角色和权限,一般将权限设置到最低。设置角色的时候不要为public角色授予任何权限,并且从sysadmin这个角色中删除windows的administrators组,提高系统安全性。 删除不必要的数据库对象 删除危险的存储过程 xp_cmdshell:执行操作系统命令,这是一个系统后门[可以移动文件位置、创建用户、提升用户权限],建议不需要则删除掉。 ole自动化存储过程 任务管理存储过程 强化文件和目录安全 数据库最终以文件的形式存储在文件系统中 使用NTFS设置权限 限制共享【不能设置为完全控制】 及时审核日志 sqlserver的审核机制可以帮助跟踪并且阻止系统中没有授权的用户他的行为。比如没有授权的用户登录系统会阻止这次登录,并且把这次操作给记录下来。审核机制既能跟踪失败记录也能跟踪成功记录。所有的数据库平台均在不同程度上提供了审查功能。 跟踪用户行为 保护数据库 数据库性能优化 数据库的性能优化主要有两个方面:减少查询比较次数、减少资源的征用。 使用工具Sql Server Profiler优化数据库的性能,减少资源的征用 SqlServer Profiler的功能 Sql Server Profiler的用法  定义跟踪  登录连接、失败和断开  Select、Insert、Update和Delete语句  SQL批处理的开始或结束  写入到Sql server错误日志的错误  安全权限检查  Profiler执行的事件 让Profiler监视我们感兴趣的事件,可以监视的事件太多,监视太多会大大降低性能和增大表数据,只监视与数据库的性能密切相关的哪些事件。常见的感兴趣的事件:  执行查询的性能  单个用户或应用程序的活动  逻辑磁盘的读写  语句级别上的CPU占用  Standart模板的事件类 优化数据库性能可以从五个层次来进行:  优先级一:减少数据的访问【减少磁盘访问】  优先级二:返回更少数据【减少网络传输或磁盘访问】  优先级三:减少交互次数【减少网络传输或磁盘访问】  优先级四:减少开销【减少CPU及内存开销】  优先级五:利用更多资源【增加资源】 技术上从四个方面来解决性能优化问题 1、调整数据库结构设计 2、调整应用程序结构设计 3、调整数据库SQL语句 4、调整服务器内存分配 如果不熟悉sqlserver可以使用数据库引擎优化顾问来对数据库提出优化建议,然后通过系统管理的修改达到目的。 数据库引擎优化顾问  数据库引擎优化顾问介绍  分析一个或多个数据库的工作负荷和物理实现,工作负荷可以是优化sql语句或者sqlserver profiler的跟踪文件和数据表。我们可以在运行引擎优化顾问前运用sqlserver profiler记录一些事件,然后将跟踪结果存储为文件或者数据表,然后把这些提供给数据库引擎优化顾问,让它去分析。  提出合理的物理设计结构,物理设计结构包括数据库中的索引、索引视图、非聚集索引、聚集索引视图等等。对工作负荷进行分析后,数据库优化顾问会建议添加删除修改数据库的物理设计结构。推荐一组合理的物理结构以降低工作负荷的开销。从而提高数据库的性能 数据库性能优化的常见问题 如何发现问题,如何分析导致性能降低的原因仍然是数据库管理员要掌握的知识。 事务占用资源的时间过长,造成阻塞 许多用户同时访问数据库的时候会产生大量事务,许多用户同时竞争一个资源导致占用资源的时间过长,造成阻塞。从而降低了数据库执行效率。产生这样的现象的原因如下: 1、多表连接查询查询期间占用多个表 2、事务需要占用太多资源,容易出现多个事务占用对方资源的状况。从而导致死锁 解决之道: 1、避免多表连接查询,联合过多的表会在查询中占用过多的资源。很容易因为别的事务占用资源而相互等待。 2、使用统一的SQL语句规范,特别是访问表的顺序要保持一致,这样可以避免互相占用资源而导致的死锁。 不合理的数据文件设置,影响事务处理的性能 当事务处理产生大量数据的时候,数据文件的大小如果设置不合理将导致数据文件的不断扩展,这也会影响到事务处理的性能,进而影响到整个数据库的性能。 1、频繁操作数据库,导致日志文件增长的过快,因为日志文件记录数据库的原始操作。所以它的增长速度比数据文件要快得多。当日志文件的增长大小设置不合理的时候会导致频繁地扩展文件。从而影响性能 2、查询操作比较频繁,系统数据Tempdb的大小设置不合理。 查询操作比较频繁的时候系统数据Tempdb增长得会比较快,因为查询所产生的临时数据都存放在这个数据库上。如果Tempdb过小当查询数据量较大的时候Tempdb会自动扩展,如果遇到频繁的查询会导致Tempdb不断扩展,从而影响系统性能。这种情况我尽可能地使查询的返回结果比较小 3、大量插入数据,导致数据文件增长过快。不要设置数据文件的自动收缩,它会在忙碌的系统上导致不必要的性能开销。所以如果没有特别需要不要设置数据库的自动收缩。最好采用手动收缩。 磁盘数据组织不合理,导致磁盘的访问次数过多 数据库的磁盘访问都是按照页来访问数据的,无论访问的数据再少都是以页为单位读取,1页为8K。所以如果将经常访问的数据放在一起,数据库读取尽量少的页面就能够完成读取操作。这样效率自然就提高了。也减少了磁盘头的来回移动。否则会多次读取硬盘页面导致访问的效率降低。 对于表A和表B、表C、表D,如果经常查询表A和表B中的数据,那么可以将他们放在同一个文件组M中;如果经常访问表C和表D中的数据可以将他们放在同一个文件组N中。这样读取效率就比较高,因为一次读取就可能包含了两个表中的数据,因此提高了查询效率。要解决“磁盘数据组织不合理,导致磁盘的访问次数过多”这个问题,我们可以将经常读写的数据放置在不同的磁盘上,也就是将经常在一起被多表连接查询的表放在同一个文件组上。这里强调:这里反复提到的“不同的磁盘”指的的是不同的磁盘,而不是同一个硬盘的不同分区。 批量导入数据的时候,要进行特殊设置 当用户需要大批量导入数据的时候会突然增加很多日志记录,并且如果数据表上有索引,数据表每增加一条记录就会在索引上增加一条数据从而降低插入的性能。解决方案: 1、大批量导入数据的时候设置数据库的恢复模式为“大容量日志恢复模式” 2、导入前禁用索引,导入完毕后重建索引。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值