T-SQL动态查询(2)——关键字查询
接上文:T-SQL动态查询(1)——简介
前言:
在开发功能的过程中,我们常常会遇到类似以下情景:应用程序有一个查询功能,允许用户在很多查询条件中选择所需条件。这个也是本系列的关注点。
但是有时候你也许会发现,有些条件或多或少是互相排斥的。比如用户通过下面其中一个条件查找信息:
1. 客户名
2. 客户ID
3. 客户身份标识号(如国内身份证、美国社保号等)。
并且这三列上都有适当的索引。本系列主要研究动态SQL和OPTION(RECOMPILE)查询提示来处理需求,但是前面已经提到过,频繁编译、重编译会给服务器带来严重压力,特别是对于那些操作特别频繁的功能。另外对于一些简单的问题使用动态SQL也略微浪费。
针对上面的问题,我们可以使用一个较为轻量的方法:使用IF语句。
本文出处:http://blog.csdn.net/dba_huangzj/article/details/49929669
IF语句:
针对上面问题,我们来看看下面例子:
IF @custno IS NOT NULL --客户号 SELECT ... FROM customers WHERE custno = @custno ELSE IF @idno IS NOT NULL --身份标识号 SELECT ... FROM customers WHERE natregno = @natregno ELSE IF @custname IS NOT NULL --客户名 SELECT TOP 200 ... FROM customers WHERE custname LIKE @custname + '%' ORDER BY custname ELSE RAISERROR('没有提供查询条件!', 16, 1)
注意:不要过于纠结里面的表和列是否存在,这里只是个大概演示。
另外,上面的TOP 200是为了避免由于用户输入一个非常短的字符串查询客户名时,由于过于模糊的查询导致返回大量数据导致性能问题。
如果这时候你系统同时返回其他表的数据,并且不喜欢重复JOIN,可以把所有匹配的客户数预存到一个表变量或临时表,然后最后再JOIN:
IF @custno IS NOT NULL INSERT @cust(custno) VALUES (@custno) ELSE IF @natregno IS NOT NULL INSERT @cust(custno) SELECT custno FROM customers WHERE natregno = @natregno ELSE IF @custname IS NOT NULL INSERT @cust(custno) SELECT TOP (200) custno FROM customers WHERE custname LIKE @custname + '%' ORDER BY custname ELSE RAISERROR('没有提供查询条件!', 16, 1) SELECT ... FROM @cust c JOIN customers cst ON cst.custno = c.custno JOIN ...
本文出处:http://blog.csdn.net/dba_huangzj/article/details/49929669
这种写法有一个潜在的性能问题,不管用户如何选择查询条件,我们都希望优化器能使用查询列上的索引。但是由于SQL Server创建执行计划的方式导致这种情况很难总是如愿。前文提到过参数嗅探的问题,当存储过程被执行并在缓存中没有找到可重用的执行计划时,SQL Server会对整个存储过程及当前值进行“嗅探”,产生一个对当前值最优的执行计划。
换句话说,如果第一个用户选择以客户号作为参数查询,那么优化器会对客户号进行优化,而底层处理中会对客户名附以NULL的形式,如果后续用户使用客户名进行搜索,会导致表扫描,这种情况肯定不是你期望的。
为了避免这种情况,可以使用一些预防措施。其中一种是把存储过程中原有的SELECT语句拆成三种针对性的SELECT语句,但是无可否认的是,这种方式会使得语句越来越庞大。另外一种是在语句中使用索引提示指定索引,但是这样语句就被绑死了,如果由于某些原因导致索引重命名,那么语句会运行失败。
在某种程度上,使用适当的OPTIMIZEFOR提示可能是更好的选择:
SELECT TOP 200 custno FROM customers WHERE custname LIKE @custname + '%' ORDER BY custname OPTION (OPTIMIZE FOR (@custname = N'ZZZZZZZ'))
这种提示会触发SQL Server针对你指定的值(如上面的ZZZZZZ)进行查询计划的创建,而不会在乎你实际传入什么值。但是这时你就要挑选一个有足够选择度和代表性的值,如果指定了一个很少会用到的值,那比不指定可能更惨。
但是不管使用什么方式,都应该在生产环境规模的数据(最好能模拟生产环境的行为)上进行测试以确保执行计划和性能都能符合你的期望。
基于参数嗅探的原因,你需要做类似下面的测试:
EXEC你的SP@custno = 123 EXEC你的SP@natregno = '1234567890' EXEC你的SP@custname = 'ABC' EXEC sp_recompile你的SP -- 清空存储过程缓存 EXEC你的SP@natregno = '1234567890' EXEC你的SP@custno = 123 EXEC你的SP@custname = 'ABC' EXEC sp_recompile你的SP EXEC你的SP@custname = 'ABC' EXEC你的SP@custno = 123 EXEC你的SP@natregno = '1234567890'
也就是说,你需要测试所有参数在单独作为首次执行计划生成时的参数的情况,如果参数很多,你能想象需要测试的次数也很多。注意上面的sp_recompile,是为了通过重编译清空计划缓存以便减少计划缓存带来的影响。
在上面例子中,如果用户使用@custname参数传入的字符串中添加了%,这种情况下,扫描可能是更好的方式。如果你需要支持查询字符串前面有%的查询,最好的方式是拆成两个分支,如:
IF left(@custname, 1) <> '%' -- 上面的查询 ELSE -- 带有OPTIMIZE FOR的查询语句
小结:
目前为止,我们看到使用IF来实现一定程度的动态查询。这种方式不是很适合条件非常多的情况,正如前面所示,你要测试和编写的条件非常多,可能导致代码庞大不便于维护。但是对于简单的情况,比如2、3种参数时,这种方式却很有效,后续会介绍其他情况下的应对措施。
本文出处:http://blog.csdn.net/dba_huangzj/article/details/49929669
OR:
如果你不想使用多个IF判断,并且可以忽略前面提到的字符串前加%的情况,那么语句完全可以改写成类似下面的方式:
SELECT TOP 200 ... FROM customers WHERE (custno = @custno AND @custno IS NOT NULL) OR (natregno = @natregno AND @natregno IS NOT NULL) OR (custname LIKE @custname + '%' AND @custname IS NOT NULL) ORDER BY custname
这里的WHERE子句实际上是:
custno = @custno OR natregno = @natregno OR custname LIKE@custname + '%'
但是分别加上IS NOT NULL是有目的的,加上之后,优化器可以把三个条件上的索引通过索引连接的方式来生成执行计划。由于IS NOT NULL条件,SQL Server可以在运行时添加一个名为“启动表达式(startup expression)”的筛选操作符,这个操作符可以根据实际情况仅访问所需的索引,而不会像代码中的那样。
不过这种策略需要所有的查询条件都作用在一个表上,并且有相对合理的索引。如果查询条件涉及不同的表,那么性能上并不一定满足你的期望。
小结:
很多编码和优化资料上显示不要过多使用OR,因为会导致非SARG的出现。从而影响性能。但是在上面情况中,OR表现得还可以,所以我们不要因为某些“铁律”、“军规”而不去考虑和尝试其他方式。最后还要强调一下,不要盲目使用这种策略,你需要验证每种策略的执行计划和性能是否满足期望。
本文出处:http://blog.csdn.net/dba_huangzj/article/details/49929669
全文检索:
除了上面两种方式之外,当你需要查找一个表或者一个固定集合(也就是说不会根据条件动态添加移除数据表)进行不同条件的查询时,使用大量的索引来支持各种查询往往不能得到什么好处。
从SQL 2005开始引入了全文检索(Fulltext),从一定程度上解决了这种问题。但是从应用经验上来说,由于计算机和SQL Server这类RDBMS是老外开发的,所以对中文的支持并不如人意。所以这里只是提一下,对于英文环境的系统,这种方式是可以考虑的。
本文出处:http://blog.csdn.net/dba_huangzj/article/details/49929669
总结:
本文演示了对于一些简单的关键字查询的处理方案。主要使用了IF/OR两种方式,正如文中多处写到的,任何一种方式都应该做充分的验证和测试,特别是在一定数据量下,否则你在小数据库上运行得很好,说不定到正式环境下就奔溃了。
下一篇将专门介绍静态SQL的相关知识。T-SQL动态查询(3)——静态SQL
T-SQL动态查询(2)——关键字查询的更多相关文章
- php单条件查询,关键字查询
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- 10月30日下午 PHP精确查询(模糊查询、模糊+关键字共同查询)
1.一个条件的模糊查询 <body> <br /> <form action="main.php" method="post"&g ...
- python3开发进阶-Django框架中的ORM的常用操作的补充(F查询和Q查询,事务)
阅读目录 F查询和Q查询 事务 一.F查询和Q查询 1.F查询 查询前的准备 class Product(models.Model): name = models.CharField(max_leng ...
- Linq to Sql : 动态构造Expression进行动态查询
原文:Linq to Sql : 动态构造Expression进行动态查询 前一篇在介绍动态查询时,提到一个问题:如何根据用户的输入条件,动态构造这个过滤条件表达式呢?Expression<Fu ...
- Sql动态查询拼接字符串的优化
Sql动态查询拼接字符串的优化 最原始的 直接写:string sql="select * from TestTables where 1=1";... 这样的代码效率很低的,这样 ...
- 使用mybatis提供的各种标签方法实现动态拼接Sql。这里演示where标签和if标签实现使用姓名的模糊查询和性别查询用户列表,当用户没有选择姓名以及性别时查询出所有的记录。
1.需求: 使用姓名的模糊查询和性别查询用户列表,当用户没有选择姓名以及性别时查询出所有的记录. 2.在UserMapper接口中定义方法: public List<User> findU ...
- Mybatis 使用Mapper接口的Sql动态代码方式进行CURD和分页查询
1.Maven的pom.xml 2.配置文件 2.1.db.properties 2.2.mybatis.xml 2.3.log4j.xml 3.MybatisUtil工具类 4.Mapper映射文件 ...
- ajax基础语法、ajax做登录、ajax做用户名验证是否可用、ajax做关键字查询动态显示、ajax做用表格显示数据并增加操作列
AJAX: AJAX 是一种用于创建快速动态网页的技术. 通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新.这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新. ...
- c#Winform程序调用app.config文件配置数据库连接字符串 SQL Server文章目录 浅谈SQL Server中统计对于查询的影响 有关索引的DMV SQL Server中的执行引擎入门 【译】表变量和临时表的比较 对于表列数据类型选择的一点思考 SQL Server复制入门(一)----复制简介 操作系统中的进程与线程
c#Winform程序调用app.config文件配置数据库连接字符串 你新建winform项目的时候,会有一个app.config的配置文件,写在里面的<connectionStrings n ...
随机推荐
- [NOIp 2009]靶形数独
Description 小城和小华都是热爱数学的好学生,最近,他们不约而同地迷上了数独游戏,好胜的他们想用数独来一比高低.但普通的数独对他们来说都过于简单了,于是他们向 Z 博士请教,Z 博士拿出了他 ...
- [bzoj1488][HNOI2009]图的同构——Polya定理
题目大意 求两两互不同构的含n个点的简单图有多少种. 简单图是关联一对顶点的无向边不多于一条的不含自环的图. a图与b图被认为是同构的是指a图的顶点经过一定的重新标号以后,a图的顶点集和边集能完全与b ...
- USACO 2017 February Gold
那天打cf前无聊练手 T1.Why Did the Cow Cross the Road 题目大意:N*N的矩阵,从左上角走到右下角,走一步消耗T,每走3步消耗当前所在位置上的权值,求最小消耗 思路: ...
- hdu 5317 合数分解+预处理
RGCDQ Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submi ...
- 51 nod 1188 最大公约数之和 V2
1188 最大公约数之和 V2 题目来源: UVA 基准时间限制:2 秒 空间限制:262144 KB 分值: 160 难度:6级算法题 给出一个数N,输出小于等于N的所有数,两两之间的最大公约数 ...
- hihocoder #1142 : 三分·三分求极值
时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 这一次我们就简单一点了,题目在此: 在直角坐标系中有一条抛物线y=ax^2+bx+c和一个点P(x,y),求点P到抛物线的 ...
- BZOJ3817 Sum(类欧几里得算法)
设$t=\sqrt r$,原题转化为$\sum_{x=1}^n(4*\lfloor\frac{tx}2\rfloor-2*\lfloor tx\rfloor+1)$考虑如何求$\sum_{x=1}^n ...
- Ubuntu 16.04 Vim安装及配置【转】
转自:http://www.cnblogs.com/ace-wu/p/6273031.html 安装VIM 默认已经安装了VIM-tiny acewu@acewu-computer:~$ locate ...
- AQS
AQS介绍 AQS,即AbstractQueuedSynchronizer, 队列同步器,它是Java并发用来构建锁和其他同步组件的基础框架. AQS的核心思想是基于volatile int stat ...
- js删除数组中的元素delete和splice的区别
例如有一个数组是 :var textArr = ['a','b','c','d']; 这时我想删除这个数组中的b元素: 方法一:delete 删除数组 delete textArr[1] 结果为: ...