zoukankan      html  css  js  c++  java
  • SQL Server海量数据查询代码优化建议

    1.尽量避免在WHERE子句中对字段进行NULL值判断

    在WHERE子句中对字段进行NULL值判断会导致引擎放弃使用索引而进行全表扫描。如:

    SELECT id FROM t WHERE num IS NULL
    

    可以在num字段设置默认值0,确保表中 num字段没有NULL值,然后这样查询:

    SELECT id FROM t WHERE num=0
    

    2.尽量避免在WHERE子句中使用!=或<>操作符

    在WHERE子句中使用!=或<>操作符将导致引擎放弃使用索引而进行全表扫描。优化器将无法通过索引来确定将要命中的行数,因此需要搜索该表的所有行。

    3.尽量避免在WHERE子句中使用OR来连接条件

    在WHERE子句中使用OR连接条件,将会导致引擎放弃使用索引而进行全表扫描,如:

    SELECT id FROM t WHERE num=10 OR num=20
    

    可以改成:

    SELECT id FROM t WHERE num=10
    UNION ALL
    SELECT id FROM t WHERE num=20
    

     4.慎用IN和NOT IN

    因为IN会使系统无法使用索引,而只能直接搜索表中的数据。如:

    SELECT id FROM t WHERE num in(1,2,3)
    

    对于连续的数值,能用BETWEEN就不要用IN

    SELECT id FROM t WHERE num BETWEEN 1 AND 3
    

     5.尽量避免在索引过的字符数据中,使用'%'开头搜索。

    此情况下引擎无法利用索引,如:

    SELECT * FROM t WHERE name Like '%Jhon%'

    建议使用全文检索。

    6.必要时强制查询优化器使用某个索引

    如在WHERE子句中使用参数,也会导致全表扫描。因为SQL只有在运行时才会解析局部变量,但优化程序不能讲访问计划的选择推迟到运行时,它必须在编译时进行选择。然而,如果在编译时简历访问计划,变量的值还是未知的,因而无法作为索引选择的输入项。如下面语句会进行全表扫描:

    SELECT id FROM t WHERE num = @num
    

    可以改为强制查询使用索引:

    SELECT id FROM t WITH(INDEX(索引名)) WHERE num=@num
    

     7.尽量避免在WHERE子句中对字段进行表达式操作

    这将导致引擎放弃使用索引而进行全表扫描。如:

    SELECT * FROM t WHERE num/2=100
    

     应改为:

    SELECT * FROM t WHERE num=100*2
    
    SELECT* FROM t WHERE SUBSTRING(num,1,4)='5678'
    

     应改为:

    SELECT * FROM t WHERE num LIKE '5678%'
    
    SELECT * FROM t WHERE DATEDIFF(yy,num,GETDATE())>21
    

     应改为:

    SELECT * FROM t WHERE num<DATEADD(yy,-21,GETDATE())
    

     即任何对列的操作都将导致表扫描,包括数据库函数、计算表达式等;查询时要尽可能将操作移至右边。

    8.尽量避免在WHERE子句中对字段进行函数操作

    会导致引擎放弃使用索引而进行全表扫描,如:

    SELECT id FROM t WHERE SUBSTRING(name,1,3)='abc'
    SELECT id FROM t WHERE DATEDIFF(day,createdate,'2015-01-30')=0
    

     应改为:

    SELECT id FROM t WHERE name like 'abc%'
    SELECT id FROM t WHERE createdate>='2015-01-30' AND createdate<'2015-01-31'
    

     9.不要在WHERE子句中的"="左边进行函数、运算符或其他表达式运算

    该操作会导致系统可能无法正确使用索引。

    10.如果索引是复合索引,要用该索引的第一个字段作为条件

    在使用索引字段作为条件时,如果该索引是符合索引,则必须使用该索引中的第一个字段作为条件时才能保证系统使用该索引,否则该索引将不会被使用。且应尽可能让字段顺序与索引顺序一致。 

    11.很多时候EXISTS是个好的选择

    SELECT num FROM t1 WHERE num in(SELECT num FROM t2)
    

     用下面的语句替换:

    SELECT num FROM t1 WHERE EXISTS(SELECT 1 FROM t2 WHERE num=t1.num)
    
    SELECT SUM(t1.c1) FROM t1 WHERE (SELECT COUNT(*) FROM t2 WHERE t2.c2=t1.c2>0)
    

     用下面的语句替换:

    SELECT SUM(t1.c1) FROM t1 WHERE EXISTS(SELECT * FROM t2 WHERE t2.c2=t1.c2)
    

     两者产生相同的结果,但是后者的效率明显高于前者。因为后者不会产生大量锁定的表扫描或索引扫描。

    如果想校验表里是否存在某条记录,不要用COUNT(*),那样效率很低且浪费服务器资源。

    可以用EXISTS代替,如:

    IF (SELECT COUNT(*) FROM t WHERE name='xxx')
    

     可以改成:

    IF EXISTS(SELECT * FROM t WHERE name='xxx')
    

     经常需要写一个T-SQL语句比较一个父结果集和子结果集,从而找到是否存在在父结果集中有而在子结果集中没有的记录,如:

    SELECT t1.a FROM t1 WHERE NOT EXISTS(SELECT * FROM tb WHERE t1.a=t2.a)
    SELECT t1.a FROM t1 LEFT JOIN tb ON t1.a=t2.b WHERE t2.a IS NULL
    SELECT a FROM t1 WHERE a NOT IN(SELECT a FROM t2)
    

     三种写法都可以得到相同的结果,但是效率一次降低。

    12.尽量使用表变量代替临时表。

    如果表变量包含大量数据,请注意索引非常有限(只有主键索引)

    13.避免频繁创建和删除临时表,以减少系统表资源消耗

    14.合理利用临时表

    适当使用临时表可以提高效率,例如,当需要重复引用大型表或常用表中某个数据集时,但是,对于一次性事件,最好使用导出表。

    15.临时表的SELECT INTO和CREATE TABLE

    在新建临时表时,如果一次性插入数据量很大,可以使用SELECT INTO代替CREATE TABLE,避免造成大量log,以提高速度;如果数据量不大,为了缓和系统表资源,应先CREATE TABLE再INSERT。

    16.如果使用到临时表,在存储过程的最后务必将所有的临时表显式删除

    先TRUNCATE TABLE,然后DROP TABLE,这样可以避免系统表长时间锁定。

    17.SET NOCOUNT ON 和SET NOCOUNT OFF

    在所有的存储过程和触发器的开始处设置SET NOCOUNT ON,在结束时设置SET NOCOUNT OFF。无需再执行存储过程和触发器的每个语句后向客户端发送DONE_IN_PROC消息。

    18.尽量避免大事务操作,提高系统并发能力。

    19.尽量避免向客户端返回大数据量

    若数据量过大,应该考虑相应需求是否合理。

    20.避免使用不兼容的数据类型

    例如FLOAT和INT,CHAR和VARCHAR,BINARY和VARBINARY是不兼容的。数据类型的不兼容可能使优化器无法执行一些本来可以进行的优化操作。例如:

    SELECT name FROM employee WHERE salary > 6000
    

     在这条语句中,如salary字段是MONEY型的,则优化器很难对其进行优化,因为6000是整形。我们应当在编程时将整形转换为MONEY类型,而不要等到运行时转化。

    21.充分利用连接条件

    在某些情况下,两个表之间可能不止一个连接条件,这时在WHERE子句中将连接条件完整地写上,有可能大大提高查询速度。如:

    SELECT SUM(account.amount) FROM account,card WHERE account.card_no=card.card_no
    SELECT SUM(account.amount) FROM account,card WHERE account.card_no=card.card_no AND account.account_no=card.account_no 
    

     第二句将比第一句执行快得多

     22.使用视图加速查询

    把表的一个子集进行排序并创建视图,有时能加速查询。它有助于比偶棉多重排序操作,而且在其他方面黑能简化优化器的工作。如:

    SELECT cust.name,rcvbles.balance,...other columns
    FROM cust,rcvbles
    WHERE cust.customer_id=rcvbles.customer_id
    AND rcvbles.balance>0
    AND cust.postcode>'98000'
    ORDER BY cust.name
    

     如果这个查询要被执行多次而不止一次,可以把所有未付款的客户找出来放在一个视图中,并按客户的名字进行排序:

    CREATE VIEW v_cust_rcvbles
    AS
    SELECT cust.name,rcvbles.balance,...other columns
    FROM cust,rcvbles
    WHERE cust.customer_id=rcvbles.customer_id
    AND rcvbles.balance>0 
    ORDER BY cust.name
    

    然后以下面的方式在视图中查询:

    SELECT * FROM v_cust_rcvbles WHERE postcode>'98000'
    

     视图中的行要比主表中的行少,而且物理顺序就是所要求的顺序,减少了磁盘I/O,所以查询工作量可以得到大幅减少。

    23.能用DISTINCT的就不用GROUP BY

    SELECT id FROM products WHERE price>10 GROUP BY id
    

     可改为:

    SELECT DISTINCT id FROM products WHERE price>10
    

     24.能用UNION ALL就不要用UNION

    UNION ALL不执行SELECT DISTINCT函数,这样就会减少很多不必要的资源

    25.尽量不要用SELECT INTO语句

    SELECT INTO语句会导致表锁定,阻止其他用户访问该表。

    26.并不是所有索引对查询都有效

    SQL是根据表中数据来进行查询优化的,当索引列有大量数据重复时,SQL查询可能不会去利用索引,如表中有字段sex,male,female几乎各一半,那么即使在sex上建立了索引也对查询效率起不了作用。

    27.索引并不是越多越好

    索引固然可以提高相应的SELECT效率,但同时也降低了INSERT和UPDATE的效率,因为INSERT或UPDATE时有可能会重建索引,所以怎样建索引需要慎重考虑。一个表的索引数量最好不要超过6个。

    28.尽可能避免更新CLUSTERED索引数据列

    因为CLUSTERED索引数据列的顺序就是表记录的物理存储顺序,一旦该列值改变将导致整个表记录的顺序的调整,会耗费相当大的资源。若应用系统需要频繁更新CLUSTERED索引数据列,那么需要考虑是否应该将该索引建为CLUSTERED索引。

    29.尽量使用数字型字段

    若只含数值信息的字段尽量不要设计为字符型,这会降低查询和连接的性能,并会增加存储开销。这是因为引擎在处理查询和连接时会逐个比较字符串中每个字符,而对于数字型而言只需要比较一次。

    30.尽可能使用VARCHAR/NVARCHAR替代CHAR/NCHAR

    因为首先变长字段存储空间小,可以节省存储空间,其次对于查询来说,在一个相对较小的字段内搜索效率显然要高。

    31.不要使用SELECT * FROM

    用具体字段列表代替"*",不要返回任何用不到的字段

    32.尽量避免使用游标

    游标效率较差,如果游标操作的数据超过1万行就应该考虑改写

    33.尝试使用基于集的方法

    在使用基于游标的方法或临时表之前,先寻找基于集的解决方案

  • 相关阅读:
    web.xml中<web-app>报错
    groovy初体验:groovy在java中的应用
    Mac安装JMeter时Unable to access jarfile ./ApacheJMeter.jar 解决方法
    intellij idea中解决java.lang.VerifyError: Expecting a stackmap frame at branch target的方法
    关于go get无法安装国内被墙软件解决办法
    Oracle 序列
    无锁并发框架Disruptor学习入门
    vsftp服务器部署
    FinalShell 推荐
    supperset (python 2.7.12 + mysql)记录
  • 原文地址:https://www.cnblogs.com/ecosu/p/4313459.html
Copyright © 2011-2022 走看看