`
SQL专家云技术团队
  • 浏览: 17664 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

SQL SERVER——SQL语句的优化建议

阅读更多

 

重中之重---语句执行顺序

 

我们先看看语句的执行顺序

如果我没记错这是《SQL SERVER 2005技术内幕--查询》这本书的开篇第一章第一节。书的作者也要让读者首先了解语句是怎么样的一个执行顺序,因为不知道顺序何谈写个好语句?

 

查询的逻辑执行顺序:

 (1) FROM < left_table> 

 (3) < join_type>  JOIN < right_table>   (2) ON < join_condition> 

 (4) WHERE < where_condition> 

 (5) GROUP BY < group_by_list> 

 (6) WITH {cube | rollup}

 (7) HAVING < having_condition> 

 (8) SELECT  (9) DISTINCT (11) < top_specification>  < select_list> 

 (10) ORDER BY < order_by_list> 

 标准的SQL 的解析顺序为:

 (1).FROM 子句 组装来自不同数据源的数据

 (2).WHERE 子句 基于指定的条件对记录进行筛选

 (3).GROUP BY 子句 将数据划分为多个分组

 (4).使用聚合函数进行计算

 (5).使用HAVING子句筛选分组

 (6).计算所有的表达式

 (7).使用ORDER BY对结果集进行排序

 

 

执行顺序:

 1.FROM:对FROM子句中前两个表执行笛卡尔积生成虚拟表vt1

 2.ON:对vt1表应用ON筛选器只有满足< join_condition> 为真的行才被插入vt2

 3.OUTER(join):如果指定了 OUTER JOIN保留表(preserved table)中未找到的行将行作为外部行添加到vt2 生成t3如果from包含两个以上表则对上一个联结生成的结果表和下一个表重复执行步骤和步骤直接结束

 4.WHERE:对vt3应用 WHERE 筛选器只有使< where_condition> 为true的行才被插入vt4

 5.GROUP BY:按GROUP BY子句中的列列表对vt4中的行分组生成vt5

 6.CUBE|ROLLUP:把超组(supergroups)插入vt6 生成vt6

 7.HAVING:对vt6应用HAVING筛选器只有使< having_condition> 为true的组才插入vt7

 8.SELECT:处理select列表产生vt8

 9.DISTINCT:将重复的行从vt8中去除产生vt9

 10.ORDER BY:将vt9的行按order by子句中的列列表排序生成一个游标vc10

 11.TOP:从vc10的开始处选择指定数量或比例的行生成vt11 并返回调用者

 

  我们了解了sqlserver执行顺序,请以前不知道的看官们,反复试验反复记忆!那么我们就接下来进一步养成日常sql好习惯,也就是在实现功能的同时又考虑性能的思想!

 

 

设计思路

  设计思路说的有点大了,下面介绍几个最常见的设计问题!

  

  循环改批量

  循环单条操作,请改成批量操作,如果没办法修改,请尽量想办法修改!这算是最常见的吧:

  1. 应用代码端一记 for 循环再恶心点的每次打开关闭连接,跑个几分钟,数量大点几小时。请把你的每次for循环出来的结果放在一个datatable,list啥的,不要找到一条就往数据库写一条!
  2. 数据库中的游标也是差不多的道理,如果有可能不用游标循环一条一条处理,请尽量不要使用。如果自己认为必须用,也请问问别人是否可以有其他方式做批量!
  3. 如果没法避免一条一条的写入,那么在处理前显示开启一个事务 begin tran  在处理完成后 commit 这样也要比不开显示事务会快很多!

 

  上个小例子:

 
create table test_0607 (a int,b nvarchar(100))

declare @i int 
set @i = 1

while @i < 10000
begin 
insert into test_0607
select @i,'0607无显示整体事务'
set @i = @i + 1
end

 

 
drop table test_0607
create table test_0607 (a int,b nvarchar(100))

 

---加上事务
begin tran
declare @i int 
set @i = 1
while @i < 10000
begin 
insert into test_0607
select @i,'0607 显示整体事务'
set @i = @i + 1
end
----结束事务,提交
commit

 

 

结果 : 8秒和0.8秒的区别,不用多说啥了吧! 凡事有利有弊,这种显示开启大事务要保证的整体的过程不会执行特别长的时间,如果执行的操作特别多而且时间长就是灾难了!

 

 

  

  降低语句复杂性

  前文语句优化三板斧中已经介绍过,降低语句复杂性是常见的优化方式。这里在说一下,导致语句特别复杂一般有两个原因:

  1. 程序逻辑本身就很复杂,需要很多表连接,又要排序又要聚合,时不时来几个子查询,外加几个函数。
  2. 由于业务有很大的共性,所以创建出很多视图,甚至视图嵌套很多层视图,最后外层又要关联单个模块的特殊性表。

 

  对于第一种情况,代码看起来就很长很复杂,看起来很牛逼的代码其实在高手看来都是很LOW的。而对于第二种,看起来代码很简洁,但经过SQL优化器的二次编译,其实和第一种并无区别。这两种的解决办法都是降低复杂性,把一些能拆分出来的尽量拆分出来放入临时表或者表变量中,比如先把条件筛选性较强的几张表关联,然后把结果放入临时表,在用临时表和其他表关联。可以理解成我有10张表关联,我先拿5张表出来关联,然后把结果放入临时表,再跟另外5张表关联。这样这个查询的复杂度由10张表的联合变成 5+6,这样降低了复杂语句复杂度。

  复杂视图也是如此,在视图和外层关联前,放入临时表,再跟外层关联。

  子查询也是如此,可以分离出来成为临时表的子查询,先分离出来。

  对于表值函数,其实也是有内联和表值之分:

---方式1:内联

 CREATE FUNCTION [dbo].[tvf_inline_Test]()
 RETURNS TABLE
 AS
    RETURN
     SELECT  ProductID
     FROM    Sales.SalesOrderHeader soh
             INNER JOIN Sales.SalesOrderDetail sod ON soh.SalesOrderID = sod.SalesOrderID 
---此写法可以结合外层查询二次编译(也就是可以利用外层的关联条件及WHERE 条件)

 


---方式2:表值

CREATE FUNCTION [dbo].[tvf_multi_Test]()
 RETURNS @SaleDetail TABLE ( ProductId INT )
 AS
     BEGIN 
         INSERT  INTO @SaleDetail
                 SELECT  ProductID
                 FROM    Sales.SalesOrderHeader soh
                         INNER JOIN Sales.SalesOrderDetail sod ON soh.SalesOrderID = sod.SalesOrderID 
         RETURN 
     END

---此写法不能应用外层条件筛选,如果数据量大会对性能产生影响。

 

 

 

  高能预警:这里说的是适当使用临时表,我遇到的很多开发人员一般都有这样一个过程。开始巨复杂的语句,知道使用临时表以后,每个步骤很小的操作都要用临时表。这会给你的TempDB造成很大的压力!

  避免重复读取

  曾经遇到过很多这样的程序,类似对商品有多种分析,而每种分析要做一些不同的处理,但是他们都会读取同一份基础数据商品和商品明细等。很多程序都是按照每种分析作为一个单独的存储过程去处理,那么也就是说有20种处理他们创建了20个存储过程,并且每个存储过程的第一步,就是先读取基础数据--商品和明细等等。不巧的是商品和商品明细有巨大的数据量,虽然做了分表(按照月份,每个表大概2QW数据),但是每个存储过程要读取一年的数据,大概是2QW * 12 ,这么庞大的数据巨量,查询后被放入一张temp表,20个存储过程顺序执行,也就是说这份基础数据每天晚上会被查询20次! 基本上这个处理占据了系统夜间维护的所有时间,有时甚至会跑不完影响白天正常业务!

   也许你看完描述就会笑,谁会把处理设计成这个样子?这不开玩笑么?没错,解决这个问题其实超简单,把20个存储过程合成一个。让基础数据的查询只查询一次,放入临时表,创建出下面逻辑处理需要的索引,在用这个临时表分别做下面所有的处理。这样一个夜间需要跑6小时以上的处理被缩短成40分钟!(当然说的有点夸张,里面还有些其他的优化,√)

    

 

    这里就提到一个使用临时表比较重要的问题,那就是类似上面的大量数据写入临时表,一定要用 先create 再 insert 的方式,不要直接使用 select into 临时表的方式,否则就是灾难了!

论索引的重要性

 

    老生常谈的话题了,我想所有公司招人的时候都会问到这样的面试题: 什么是索引,索引有哪些类,有何不同?等等....

    索引是啥?什么是聚集索引?什么是非聚集索引?什么是主键查找?什么是主键扫描?什么是索引查找?什么是书签查找?有啥区别? 这里都不介绍,请自行百度!

    很多开发人员意识不到索引到底对语句,甚至对系统有对重要。关于索引对系统的重要性请关注后续文章

    如何建立索引

    最为简单粗暴的方式,当你写完一条语句的时候,打开执行计划,执行一下按照优化器的提示创建索引。

    高能预警:这里需要你的条件可以用索引!比如 你的语句中 索引列不能带函数,不能参与计算如 where productID/2 = @a ,不能有隐式转换等!

   

 

   

   

   

   

    建立索引后,同样并不是每个查询都会使用索引,在使用索引的情况下,索引的使用效率也会有很大的差别。如上面缺失的索引我们添加上以后再查询!

    

 

 

 

 

    索引查找(seek),一般为最优(但查找也要看查找的筛选性),尽量吧where 条件中的字段建成一个组合索引,并且包含要查询select 中的字段。这里就不继续深入了。

 

    看懂执行计划创建

    如何看懂执行计划这就是一个可以写几百页书的话题了,但是看懂执行计划是做优化的重中之重了!以后的文章中会详细讲解。

    通过执行计划可以看出语句的主要消耗到底在哪里,另外配合set statistics io on 等分析读次数,也是优化的关键,创建或优化索引页是主要从这里出发。

 

     

语句常规习惯

 

  

  只返回需要的数据

    返回数据到客户端至少需要数据库提取数据、网络传输数据、客户端接收数据以及客户端处理数据等环节,如果返回不需要的数据,就会增加服务器、网络和客户端的无效劳动,其害处是显而易见的,避免这类事件需要注意:

    横向来看:

  1. 不要写SELECT * 的语句,而是选择你需要的字段。
  2. 当在SQL语句中连接多个表时, 请使用表的别名并把别名前缀于每个Column上.这样一来,就可以减少解析的时间并减少那些由Column歧义引起的语法错误。

   纵向来看:

  1. where 条件要尽量的多且保证高筛选性。
  2. 业务中很常见要返回大批量数据到前端,但是这些数据真的都是必要的么?前端是否可以加一些默认条件呢?

  减少不必要的操作

  写语句之前,理清你的思路!

  1. 杜绝不必要的表连接,多一个表链接代表多很大部分开销。
  2. 减少不必要的条件判断,很多时候前台传入为空值得时候 后台语句被写成XX=XX OR XX IS NULL OR XX LIKE OR ...OR ...OR 等。这是比较经典的问题了,请加入判断在拼入最后的条件!
  3. 你的语句需要去重复么? distinct 、union等操作
  4. LEFT JOIN 和 inner join的区别,是否真的需要left join,否则选用inner join 来减少不必要的数据返回。
  5. order by 你的语句是否需要排序?排序是否可以通过索引来降低性能消耗? 我见过竟然插入数据也带着order by的 !

  

  尽量早的筛选

  1. 最经典的例子就是where 和 having的区别,看过语句执行顺序你应该已经明白了。能写在where 中不要放在having中。
  2. 使用临时表降低语句复杂性,要降低临时表的数据量,也就是要把有条件的表尽量关联并做成临时表。
  3. 前面提到的隐式转换,索引字段使用计算或函数,也会导致数据不能尽早筛选。

 

  常用的写法误区(以下都是网上片面结论)

  所有别人提到的方法到底有无效

  1. or 要用union all 代替 (or是很常规的一种写法,情况分很多种,一个表的两个条件用  a.a =X or a.a = XX ,一个表两个字段用 a.a =X or a.b = x,两个不同表字段用 a.a = X or b.a = X 这是网上说的union all代替的)
  2. 避免使用 in、not in (数据量小的时候不会有问题,如果数据量大可能影响性能,数据量大处理方式先把in 中的数据放入临时表)
  3. 事务操作过程要尽量小,能拆分的事务要拆分开来。(前文中提到的例子,有些情况循环写入下,显示开启一个大事务会有很大帮助)
  4. 使用with(nolock)查询语句不会阻塞 (一般情况下是这样,但是如果有架构修改或快照发布等使用with(nolock)也会阻塞)
  5. 用exists 代替 in (情况也很复杂不能一概而论)

 

-----------------------------------------------------------------------------------------------------

  总结 :说到语句优化,有太多太多的注意,这些需要明白原理,能看懂执行计划,并且不断积累。

      单单的几篇优化大全是帮助是微乎其微的,另外要动手实践,明白为什么这样写会好!

分享到:
评论

相关推荐

    优化SQL语句——利用Quest Central for SQL Server来自动化你的工作.pdf

    《优化SQL语句——利用Quest Central for SQL Server来自动化你的工作》 在数据库管理领域,SQL语句的优化是提升系统性能的关键环节。为了提高生产力,减少用户因错误导致的问题,我们常常需要对SQL语句进行调整和...

    C# +SQL Server——通讯录

    【标题】:“C# + SQL Server —— 通讯录” 这篇内容主要介绍了一个使用C#编程语言和SQL Server数据库技术开发的通讯录系统,适用于课程设计项目。该系统旨在提供一个用户友好的界面,实现对联系人信息的有效管理...

    SQLServer语句优化

    本文主要围绕SQL Server语句优化中的关键点——索引使用策略展开讨论,旨在帮助读者更好地理解和运用这些技术,以提高数据库性能。 #### 二、索引的基础概念 1. **聚集索引**:这是一种特殊的索引类型,它按照特定...

    SQL语句优化——in,not in,exists,not exists, left join...on博客所需SQL语句2.txt

    SQL语句优化——in,not in,exists,not exists, left join...on博客所需SQL语句2.txt,欢迎下载!

    SQL_Server_2008查询性能优化

    SQL Server通过解析查询语句,生成一个执行计划,然后按照这个计划来执行查询。优化查询计划是提高性能的关键。使用`EXPLAIN PLAN`或`SET SHOWPLAN_ALL ON`可以查看查询计划,帮助我们分析查询的运行方式,找出可能...

    适用SQL Server 2016版本的数据库加载驱动包——sqljdbc42.jar

    2. **创建Statement或PreparedStatement对象**:这些对象用于执行SQL语句。Statement适合简单的SQL,PreparedStatement则适用于含有参数的预编译SQL,提高性能和安全性。 3. **执行SQL操作**:通过Statement或...

    C++链接SQL Server,用SQL语句查询数据库中的内容

    本文将深入探讨如何使用C++与SQL Server交互,并通过SQL语句查询数据库中的内容。 首先,要实现C++与SQL Server的连接,我们需要一个中间库来处理底层的通信和数据转换。ODBC(Open Database Connectivity)和ADO...

    c#及SQL server数据库——简单的图书管理系统

    使用SQL语句进行数据的CRUD(创建、读取、更新、删除)操作,如INSERT插入新书,SELECT查询图书信息,UPDATE修改图书状态,DELETE删除已注销的图书。 在用户登录注册方面,系统需要验证用户名和密码的正确性。这...

    SQL SERVER 将select数据生成insert语句

    首先,`SELECT INTO` 语句是SQL Server中创建新表并填充数据的一种快速方式。它的基本语法如下: ```sql SELECT column1, column2, ... INTO new_table FROM existing_table WHERE condition; ``` 这个语句会根据...

    sql server 数据库文件——图书订购

    使用这个数据库,你可以练习SQL查询语句(如SELECT、INSERT、UPDATE、DELETE),了解如何检索、更新或删除数据。同时,也可以学习使用JOINs来联接不同表,获取如客户订单详情之类的综合信息。 6. **备份与恢复**:...

    SQL Server 和 MySql 语法和关键字的区别——用于SQLServer到MySql的转换

    SQL Server 和 MySql 语法和关键字的区别——用于 SQLServer 到 MySql 的转换 SQL Server 和 MySql 是两种常用的关系型数据库管理系统,但是它们之间存在一定的语法和关键字差异。这些差异使得开发者在从 SQL ...

    SQL语句优化——in,not in,exists,not exists, left join...on博客所需SQL语句.txt

    SQL语句优化——in,not in,exists,not exists, left join...on博客所需SQL语句.txt欢迎下载!

    SQLPrompt v5.3.0.3支持SQLServer2012

    它可以分析SQL查询的执行计划,帮助找出潜在的性能瓶颈,并给出优化建议。这对于SQL Server 2012的使用者来说尤其有价值,因为2012版本引入了很多性能优化特性,比如Columnstore索引,SQLPrompt可以帮助开发者更好地...

    SQL Server JDBC驱动jar包

    3. **msutil.jar**: 这个文件包含了辅助工具和实用程序类,它们支持SQL Server JDBC驱动的一些高级功能和性能优化,例如预编译的SQL语句(PreparedStatement)、批处理操作以及数据库连接池管理。 使用这些jar包,...

    图书馆管理系统——vs2017、c#、sqlserver

    《图书馆管理系统——基于VS2017、C#与SQLServer的实现》 图书馆管理系统是信息化建设中的一个重要组成部分,它能够高效地管理图书资源,提供便捷的借阅、归还和人员管理服务。本系统采用Microsoft Visual Studio ...

    数据库应用技术——SQL Server 2000简明教程电子教案

    "数据库应用技术——SQL Server 2000简明教程电子教案"将带领学习者深入了解如何使用SQL Server 2000进行数据库管理,从基础概念到高级应用,覆盖了数据库设计、数据操作、安全管理、性能优化等多个方面,是学习和...

    数据库应用技术SQL——server

    总结,本教程“数据库应用技术——SQL Server 2000简明教程电子教案”将引导读者深入理解SQL Server的基本概念和操作,为后续的数据库管理及开发工作打下坚实基础。随着技术发展,尽管SQL Server 2000已不再是最新...

    SQL Server 2012数据库技术与应用(微课版)-综合实训及答案.zip

    《SQL Server 2012数据库技术与应用》是一门深度探讨关系型数据库管理系统SQL Server 2012的课程,旨在帮助学习者掌握数据库设计、管理、优化以及实际应用等核心技能。作为微课版,它可能包含了视频教程、实践案例、...

Global site tag (gtag.js) - Google Analytics