热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

T-SQL动态查询(2)关键字查询_MySQL

接上文:T-SQL动态查询(1)——简介前言:
接上文:T-SQL动态查询(1)——简介

前言:


在开发功能的过程中,我们常常会遇到类似以下情景:应用程序有一个查询功能,允许用户在很多查询条件中选择所需条件。这个也是本系列的关注点。

但是有时候你也许会发现,有些条件或多或少是互相排斥的。比如用户通过下面其中一个条件查找信息:

1. 客户名

2. 客户ID

3. 客户身份标识号(如国内身份证、美国社保号等)。

并且这三列上都有适当的索引。本系列主要研究动态SQL和OPTION(RECOMPILE)查询提示来处理需求,但是前面已经提到过,频繁编译、重编译会给服务器带来严重压力,特别是对于那些操作特别频繁的功能。另外对于一些简单的问题使用动态SQL也略微浪费。

针对上面的问题,我们可以使用一个较为轻量的方法:使用IF语句。

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   ...


这种写法有一个潜在的性能问题,不管用户如何选择查询条件,我们都希望优化器能使用查询列上的索引。但是由于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) <> &#39;%&#39;
   -- 上面的查询
ELSE
   -- 带有OPTIMIZE FOR的查询语句


小结:

目前为止,我们看到使用IF来实现一定程度的动态查询。这种方式不是很适合条件非常多的情况,正如前面所示,你要测试和编写的条件非常多,可能导致代码庞大不便于维护。但是对于简单的情况,比如2、3种参数时,这种方式却很有效,后续会介绍其他情况下的应对措施。

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 + &#39;%&#39; AND @custname IS NOT NULL)
ORDER  BY custname


这里的WHERE子句实际上是:

custno = @custno OR natregno = @natregno OR custname LIKE@custname + &#39;%&#39;


但是分别加上IS NOT NULL是有目的的,加上之后,优化器可以把三个条件上的索引通过索引连接的方式来生成执行计划。由于IS NOT NULL条件,SQL Server可以在运行时添加一个名为“启动表达式(startup expression)”的筛选操作符,这个操作符可以根据实际情况仅访问所需的索引,而不会像代码中的那样。

不过这种策略需要所有的查询条件都作用在一个表上,并且有相对合理的索引。如果查询条件涉及不同的表,那么性能上并不一定满足你的期望。

小结:

很多编码和优化资料上显示不要过多使用OR,因为会导致非SARG的出现。从而影响性能。但是在上面情况中,OR表现得还可以,所以我们不要因为某些“铁律”、“军规”而不去考虑和尝试其他方式。最后还要强调一下,不要盲目使用这种策略,你需要验证每种策略的执行计划和性能是否满足期望。

全文检索:


除了上面两种方式之外,当你需要查找一个表或者一个固定集合(也就是说不会根据条件动态添加移除数据表)进行不同条件的查询时,使用大量的索引来支持各种查询往往不能得到什么好处。

从SQL 2005开始引入了全文检索(Fulltext),从一定程度上解决了这种问题。但是从应用经验上来说,由于计算机和SQL Server这类RDBMS是老外开发的,所以对中文的支持并不如人意。所以这里只是提一下,对于英文环境的系统,这种方式是可以考虑的。

总结:


本文演示了对于一些简单的关键字查询的处理方案。主要使用了IF/OR两种方式,正如文中多处写到的,任何一种方式都应该做充分的验证和测试,特别是在一定数据量下,否则你在小数据库上运行得很好,说不定到正式环境下就奔溃了。

以上就是T-SQL动态查询(2)关键字查询_MySQL的内容,更多相关内容请关注PHP中文网(www.php1.cn)!

推荐阅读
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 本文详细介绍了SQL日志收缩的方法,包括截断日志和删除不需要的旧日志记录。通过备份日志和使用DBCC SHRINKFILE命令可以实现日志的收缩。同时,还介绍了截断日志的原理和注意事项,包括不能截断事务日志的活动部分和MinLSN的确定方法。通过本文的方法,可以有效减小逻辑日志的大小,提高数据库的性能。 ... [详细]
  • 搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的详细步骤
    本文详细介绍了搭建Windows Server 2012 R2 IIS8.5+PHP(FastCGI)+MySQL环境的步骤,包括环境说明、相关软件下载的地址以及所需的插件下载地址。 ... [详细]
  • PHP设置MySQL字符集的方法及使用mysqli_set_charset函数
    本文介绍了PHP设置MySQL字符集的方法,详细介绍了使用mysqli_set_charset函数来规定与数据库服务器进行数据传送时要使用的字符集。通过示例代码演示了如何设置默认客户端字符集。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文由编程笔记小编整理,介绍了PHP中的MySQL函数库及其常用函数,包括mysql_connect、mysql_error、mysql_select_db、mysql_query、mysql_affected_row、mysql_close等。希望对读者有一定的参考价值。 ... [详细]
  • 本文介绍了Python高级网络编程及TCP/IP协议簇的OSI七层模型。首先简单介绍了七层模型的各层及其封装解封装过程。然后讨论了程序开发中涉及到的网络通信内容,主要包括TCP协议、UDP协议和IPV4协议。最后还介绍了socket编程、聊天socket实现、远程执行命令、上传文件、socketserver及其源码分析等相关内容。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • 本文介绍了在Hibernate配置lazy=false时无法加载数据的问题,通过采用OpenSessionInView模式和修改数据库服务器版本解决了该问题。详细描述了问题的出现和解决过程,包括运行环境和数据库的配置信息。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • Metasploit攻击渗透实践
    本文介绍了Metasploit攻击渗透实践的内容和要求,包括主动攻击、针对浏览器和客户端的攻击,以及成功应用辅助模块的实践过程。其中涉及使用Hydra在不知道密码的情况下攻击metsploit2靶机获取密码,以及攻击浏览器中的tomcat服务的具体步骤。同时还讲解了爆破密码的方法和设置攻击目标主机的相关参数。 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 本文介绍了使用PHP实现断点续传乱序合并文件的方法和源码。由于网络原因,文件需要分割成多个部分发送,因此无法按顺序接收。文章中提供了merge2.php的源码,通过使用shuffle函数打乱文件读取顺序,实现了乱序合并文件的功能。同时,还介绍了filesize、glob、unlink、fopen等相关函数的使用。阅读本文可以了解如何使用PHP实现断点续传乱序合并文件的具体步骤。 ... [详细]
  • 本文介绍了高校天文共享平台的开发过程中的思考和规划。该平台旨在为高校学生提供天象预报、科普知识、观测活动、图片分享等功能。文章分析了项目的技术栈选择、网站前端布局、业务流程、数据库结构等方面,并总结了项目存在的问题,如前后端未分离、代码混乱等。作者表示希望通过记录和规划,能够理清思路,进一步完善该平台。 ... [详细]
author-avatar
股海一游6888
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有