写有效率的SQL查询(IV)
本文主要介绍写SQL的另外两个误区:
1、 存储过程中使用局部变量而不使用参数变量(就是存储过程输入参数)做where条件
2、 查询条件中类型不匹配
这两种错误都是非常非常容易犯且非常发指的错误,特别是2,太多次见过了。
一、关于存储过程使用局部变量,我们举例说明。
有这么一张表
存储过程:
create proc test
(
@id int
)
as
select * from charge where charge_no > @id
那么exec test 99998,执行计划为:
请注意上图中的估计行数。
但是如果把存储过程修改为:
alter proc test
(
@id int
)
as
declare @local int
set @local = @id
select * from charge where charge_no > @local
再次观察exec test 99998的查询计划:
请再次注意估计行数,现在是30000了。而我们都知道,修改前存储过程和修改后的输出结果集都没有任何变化,为2。
由于charge_no是聚集索引,而我们的查询条件是where charge_no > XXX,不论SQLServer估计行数有多大,伊都会使用相同的clustered index seek查找到XXX,然后直接顺序遍历基础表剩下的叶节点。
但是,若charge_no是非聚集索引,由于估计结果集行数大小由两行变成了总行数的百分之三十(使用局部变量做查询条件,这种where AAA > BBB,SQLServer无法估计结果集大小,所以它使用默认估计值:30%),nonclustered index seek变成nonclustered index scan(SQL2k5中若不是覆盖查询,会是clustered index scan),这是巨大的性能损耗,必须避免。
在这里顺带着再次强调另外一个问题:缓存的查询计划可能会强力的伤害性能。为了更详细的说明它,我们把存储过程test改为:
alter proc test
(
@id int
)
as
select * from charge where charge_no > @id
然后看看执行计划exec test 99998(见上面的图,不重复贴了)。再来看看exec test 1的执行计划:
我们可以注意到,尽管真实的结果集变动非常巨大,但是查询计划还是完全不变,SQLServer在使用缓存。这种情况在使用聚集索引时不会让查询变得更糟,但是使用非聚集索引就会差上十万八千里,IO开销会差上n个数量级(n取决于真实的结果集)。
所以如果你的查询由于输入参数的不同,选择性变动剧烈,最好在创建存储过程的时候使用 WITH RECOMPILE 选项。即:
create proc test
(
@id int
)
with recompile
as
select * from charge where charge_no > @id
OK,但并不是所有的情况下在查询条件中使用局部变量都有问题。如果查询条件中涉及的索引,SQLServer发现伊的分布密度非常小(比如一个identity(1,1)列或者一个unique),那么在where AAA = XXX的情况下,SQLServer仍然会认为结果集相对总行数很小,而选择index seek类的查询计划。
二、 查询条件中的类型不匹配。
所谓的类型不匹配是说,查询条件where AAA = @var,列AAA的定义和@var不同。例如,AAA是varchar(64),@var是bigint。这种情况下,非常有可能让本来是index seek的运算变成index scan,在大数据量表中,性能差距会非常明显。
从我的经验来看,并不是所有的隐式转换都会带来这样的问题。但是这样的问题大量的存在,并且在分析性能瓶颈、做索引调优时,会给你带来极大的困扰。必须分析缓存中查询计划对应的原始语句,看那玩意属于慢性自杀。
我们写SQL一般都会类型匹配,但是通过应用程序就非常容易出错。比方说一个表有个MobileNo字段用来存储手机号码,表中是varchar。但是应用程序你这么写:
SqlConnection conn = ...;
SqlCommand cmd = new SqlCommand("select * from Users where MobileNo = @mo", conn);
cmd.Parameters.Add(new SqlParameter("@mo", 13511223344));
SqlDataReader reader = cmd.ExecuteReader();
//....
那么你挂了……
到现在为止,我没有看到任何资料说哪种形式的隐式转换会让SQL无法判定结果集大小或者可以不去爬整棵索引树。所以我的建议是,使用最强类型去匹配查询列。查询列是啥,就写啥。是varchar(64)就别简单的new SqlParamerer(“@mo”, “13511223344”),要精确指定它的类型、长度。这样做有另外一个好处,偶将在下一篇blog——比较拼SQL、参数化SQL、使用存储过程执行DB指令的优劣时说明(btw:我相信那是一个好坑:))。
==加个总结=====================================
1、存储过程中,能不使用本地变量就不使用,尽可能的使用参数变量(也就是输入参数)。如果不得不使用本地变量,那也得只用在分布密度足够小的索引上使用。
2、写查询条件时,应该尽可能的使类型匹配。使用诸如SqlCommand执行DB指令时,一定要让输入参数从类型到长度严格匹配相应的列。尽管DB端不是所有的隐式转换都会引起性能损耗。
================================================
稍微提一句,在msdn中SQL Server Database Engine>Troubleshooting the Database Engine > Troubleshooting Queries下有一篇《Troubleshooting Poor Query Performance: Constant Folding and Expression Evaluation During Cardinality Estimation》,尽管说的粗糙无比外带模棱两可,但还是推荐一读。
写有效率的SQL查询(IV)相关推荐
- 写有效率的SQL查询(V)
轉自:http://www.netfocus.cn/peoplearticle994.html 先站在应用程序的角度说说它们的不同. 1. 直接拼SQL 就像大家了解的那样,直接拼SQL带来了SQL注 ...
- 写有效率的SQL查询(I)
大型系统的生产环境,一般情况下,我们评价一条查询是否有效率,更多的是关注逻辑IO(至于为什么,回头补一篇).我们常说,"要建彪悍的索引"."要写高效的SQL", ...
- 关于SQL查询效率,100w数据,查询只要1秒
1.关于SQL查询效率,100w数据,查询只要1秒,与您分享: 机器情况 p4: 2.4 内存: 1 G os: windows 2003 数据库: ms sql server 2000 目的: 查询 ...
- SQL查询效率:100w数据查询只需要1秒钟
机器情况 p4: 2.4 内存: 1 G os: windows 2003 数据库: ms sql server 2000 目的: 查询性能测试,比较两种查询的性能 SQL查询效率 step by s ...
- exists查询慢_8个SQL查询效率优化原则
点击上方"Java秃头哥",选择"星标" 每天分享优质干货 1.对查询进行优化,应尽可能避免全表扫描 首先应考虑在 where 及 order by 涉及的列上 ...
- SQL查询优化方法 提高SQL查询效率 数据库的哪些字段适合添加索引
如何提高sql的查询效率 在正确的字段上创建索引. 优化查询sql的写法(特别是where语句的写法). 一.数据库的哪些字段适合添加索引 表的某个字段值得离散度越高,该字段越适合选作索引的关键字.主 ...
- sql如何遍历几百万的表_关于SQL查询效率,100w数据,查询只要1秒
1.关于SQL查询效率,100w数据,查询只要1秒,与您分享: 机器情况 p4: 2.4 内存: 1 G os: windows 2003 数据库: ms sql server 2000 目的: 查询 ...
- mysql 索引未命中_联合索引命中率问题导致SQL查询效率慢的问题
执行 MySQL DumpSlow 结果是:Count: 1358 Time=0.33s (448s) Lock=0.00s (0s) Rows=2.5 (3343) Count:出现次数 Ti ...
- 一个 提高SQL 查询的讨论帖
idn(关键字),产品名称,产品数量... B表,有字段:idn,a_idn(记录A表的关键字),工序,工时... A表与B表是一对多的关系, 我想取到A表的明细及B表相关的总工时 sele aa.* ...
最新文章
- 中国半导体最强助攻来了!十年免税、上下游一揽子扶持,明确「集成电路」为一级学科...
- 命令行打印文件树列表: tree
- 操作系统 : 按优先数调度算法实现处理器调度(C++)
- 云计算:大数据时代的系统工程
- map.setTerrain is not a function
- mac 2018 idea 无法 import导入或打开maven 项目
- Luogu5629 【AFOI-19】区间与除法
- python论文排版_学位论文排版教程1
- Android studio上音频文件格式问题
- IntelliJ IDEA更换主题样式分享
- 08、SpringCloud 系列:Nacos - 安装、启动
- Saliency Integration :An Arbitrator Model阅读总结
- Delphi XE6 原生解析json
- C++语言程序设计第五版 - 郑莉(第六章课后习题)
- 计算机英语及教学法,对高职计算机专业英语教学方法的探讨
- 打印机扫描功能不见了_打印机没有扫描选项怎么办
- Jetson TX2板载摄像头(一)
- css calc()函数 动态根据屏幕宽度计算宽度
- Windows10 打开SQL Server 配置管理器的方法
- python 统计检验_[转载]Python替代SPSS进行各项统计检验
热门文章
- MySQL定义条件和处理程序_MySQL教程111-MySQL定义条件和处理程序
- 如何判断 cxgrid 双击了哪一列_唐卡的价值主要体现在哪 如何判断唐卡的价值
- oracle glogin.sql sql _user,为什么我的login.sql不执行?
- java指定位置写入_java指定路径写、读文件
- 借助tkinter设计人脸检测的界面(摄像头检测,视频检测,视频检测并保存)
- HDU2141(二分查找)
- 思维dp ---- K步最短路 D. Explorer Space
- c 与matlab混编,谈谈Matlab与C/C++或C#的互调用(混合编程)
- 算法竞赛知识合集 目录(博客中转站)
- mysql rpc_使用XML-RPC和MySQL处理中文字符