轉自:http://www.netfocus.cn/peoplearticle994.html

先站在应用程序的角度说说它们的不同。

1、 直接拼SQL

就像大家了解的那样,直接拼SQL带来了SQL注入攻击,带来了拼时些许的性能损失,但是拼不用添加SqlParameter,会少写很多代码——很多人喜欢直接拼,也许就因为这点。这种做法会把你拼好的SQL原样直接发送到DB服务器去执行。(注意类似”exec yourproc ‘param1’, 12”的语句不在此范畴,这是调用存储过程的一种方式)

2、 参数化SQL

所谓的“参数化SQL”就是在应用程序侧设置SqlCommand.CommandText的时候使用参数(如:@param1),然后通过SqlCommand.Parameters.Add来设置这些参数的值。这种做法会把你准备好的命令通过sp_executesql系统存储过程来执行。通过参数化SQL,和直接拼SQL相比,最直接的好处就是没有SQL注入攻击了。

3、 调用存储过程

直接调用存储过程其实和参数化SQL非常相似。唯一的本质不同在于你发送到DB服务器的指令不再是sp_executesql,而是直接的存储过程调用而已。

很多人非常非常厌恶在应用程序中使用存储过程,而宁愿使用拼SQL或者参数化SQL,理由是它们提供了更好的灵活性——这个理由其实非常非常的发指(俺现在喜欢上这个词了)。

现在做设计,一般都是从上到下来,重心都在业务逻辑上。传说中的领域模型设计完,测试用例都通过之后,才会考虑数据持久化方式。数据持久化是系统的一部分,但绝对不是最重要的部分,设计应该围绕业务逻辑开展,持久化应该仅仅是个附件。至少,高层应用应该尽可能的不关心处于最底层的物理存储结构(如:表)和数据持久、反持久方式(是拼SQL还是存储过程),所以用不用存储过程根本不重要。很多人害怕存储过程,其实是害怕存储过程中包括业务逻辑——真实情况是,如果存储过程中包含了业务逻辑,那一定最初需求分析不够导致用例提取不足,导致测试用例覆盖不够,导致领域模型设计不充分,要不就是偷懒。

=====

站在DB角度讨论它们的不同,主要从cpu、内存方面来考虑,其他诸如安全性,msdn上都有,google也能拿到一堆资料,不再赘述。

首先是查询计划。

SQL编译完一条SQL之后,会把它缓存起来(可以通过sys.syscacheobjects系统视图查看),以后再有相同的查询过来(注意sys.syscacheobjects视图中的sql字段,和它存储的东西完全一样才能称为“相同的查询”),会直接使用缓存,而不再重新编译。

Ø 存储过程,伊只编译一遍(如果没有指定with recompile选项的话,如果指定了,根本就不会生成计划缓存)。

Ø 参数化SQL,和存储过程基本一样,只要是相同的查询,也都是只编译一次,以后重用(当然,指定了option(recompile)的除外)。这里不得不提.NET SqlClient组件的一个龌龊:如果你的参数中包含varchar或者char类型的参数,你在Parameters.Add的时候又没有指定长度,它都会根据你实际传入的字符串长度(假设是n)给你重新定义成nvarchar(n)。如:select * from mytable where col1 = @p1,你设置@p1为’123456’,实际传到sql这边的命令是:exec sp_executesql N'select * from mytable where col1 = @p1',N'@p1 nvarchar(6)',@p1=N'123456'。这样,系统缓存中实际存储的sql是:(@p1 nvarchar(6))select * from mytable where col1 = @p1。看到了吧?如果你的输入参数变动比较多,那么看起来同样的一条语句,会被编译很多次,在缓存中存储很多份。cpu和内存都浪费了。这也是在《写有效率的SQL查询IV》中建议的使用最强类型参数匹配的原因之一。

Ø 拼SQL。到这里不说大家也猜的出来,拼SQL要浪费大量的cpu进行编译,浪费大量缓存空间来存储只用一次的查询计划。

服务器的物理内存有限,SQLServer的缓存空间也有限。有限的空间应该被充分利用。通过性能计数器SQL Server:Buffer Manager"Buffer Cache hit ratio来观察缓存命中率。如果它小于百分之90,你就得研究研究了。关注一把诸如sys.dm_os_memory_cache_counters、sys.dm_os_memory_cache_entries、sys.dm_os_memory_cache_hash_tables、sys.syscacheobjects等视图,基本可以确定问题出在哪儿。

cpu方面需要关注三个性能计数器:SQLServer:SQL Statistics"Batch Requests/Sec、SQLServer:SQL Statistics"SQLCompilations/sec、SQLServer:SQL Statistics"SQL Re-Compilations/sec。如果compilations数目超过batch请求数目的百分之10,或者recompilations数目超过compilations数目的百分之10,那基本可以说明cpu消耗了太多在编译查询计划上面。

最后,我的建议是:
    1、DB中的所有操作都尽可能的使用存储过程,哪怕只是一句简单的select。
    2、鄙视拼SQL。

btw:MSDN中对拼SQL称为"ad hoc",呵呵。

==================
补充一点,说明一下N'@p1 nvarchar(6)'换成N'@p1 nvarchar(30)'会重新编译:)。
程序代码如下:

1//
2SqlCommand cmd = new SqlCommand("select * from myt where data = @d", conn);
3cmd.Parameters.Add(new SqlParameter("@d", "1234567890"));
4cmd.ExecuteNonQuery();
5
6cmd = new SqlCommand("select * from myt where data = @d", conn);
7cmd.Parameters.Add(new SqlParameter("@d", "123"));
8cmd.ExecuteNonQuery();
9

执行完这段程序,可以观察观察sys.syscacheobjects:

   
上图中的5、6行标记了缓存的查询计划。
=======

另外,再来说个更应该注意的地方:

 1//
 2SqlCommand cmd = new SqlCommand("select * from myt where data = @d", con);
 3cmd.Parameters.Add(new SqlParameter("@d", "1234567890"));
 4cmd.ExecuteNonQuery();
 5
 6cmd = new SqlCommand("select * from myt where data = @d", con);
 7cmd.Parameters.Add(new SqlParameter("@d", "123"));
 8cmd.ExecuteNonQuery();
 9
10cmd = new SqlCommand("select * from myt where data = @a", con);
11cmd.Parameters.Add(new SqlParameter("@a", "123"));
12cmd.ExecuteNonQuery();
13

注意,上述代码中最后一次操作我把@d参数重命名成了@a,然后再来看看sys.syscacheobjects里面有啥:

注意第六行。

================
稍微提一下“简单参数化”(SQL2k中称为自动参数化)和“强制参数化”。在简单参数化下,SQL会试图参数化你的语句,以减少查询计划编译和重编译,但是可以被参数化的语句非常有限。这个东东可以通过一条简单的insert语句测试到,偶就不贴图了。简单参数化是SQLServer的默认行为。

强制参数化可以通过设置库的属性PARAMETERIZATION为FORCED实现。强制参数化会在很大程度上参数化你的语句。但是它有很多的限制(见MSDN)。

但是要注意,由于查询计划不会有两种和两种以上的副本,所以SQL可能会选择一个不合适的计划来执行你的查询。这也是偶一再的说,如果你的输入参数引起选择性剧烈变化,最好指定recompile选项的原因。

写有效率的SQL查询(V)相关推荐

  1. 写有效率的SQL查询(IV)

    轉自:http://www.netfocus.cn/peoplearticle995.html 本文主要介绍写SQL的另外两个误区: 1. 存储过程中使用局部变量而不使用参数变量(就是存储过程输入参数 ...

  2. 写有效率的SQL查询(I)

    大型系统的生产环境,一般情况下,我们评价一条查询是否有效率,更多的是关注逻辑IO(至于为什么,回头补一篇).我们常说,"要建彪悍的索引"."要写高效的SQL", ...

  3. 关于SQL查询效率,100w数据,查询只要1秒

    1.关于SQL查询效率,100w数据,查询只要1秒,与您分享: 机器情况 p4: 2.4 内存: 1 G os: windows 2003 数据库: ms sql server 2000 目的: 查询 ...

  4. SQL查询效率:100w数据查询只需要1秒钟

    机器情况 p4: 2.4 内存: 1 G os: windows 2003 数据库: ms sql server 2000 目的: 查询性能测试,比较两种查询的性能 SQL查询效率 step by s ...

  5. exists查询慢_8个SQL查询效率优化原则

    点击上方"Java秃头哥",选择"星标" 每天分享优质干货 1.对查询进行优化,应尽可能避免全表扫描 首先应考虑在 where 及 order by 涉及的列上 ...

  6. 通过v$sqlarea,v$sql查询最占用资源的查询

    http://blog.sina.com.cn/s/blog_6ceed3280100x0q5.html 通过v$sqlarea,v$sql查询最占用资源的查询 (2011-09-01 22:22:0 ...

  7. SQL查询优化方法 提高SQL查询效率 数据库的哪些字段适合添加索引

    如何提高sql的查询效率 在正确的字段上创建索引. 优化查询sql的写法(特别是where语句的写法). 一.数据库的哪些字段适合添加索引 表的某个字段值得离散度越高,该字段越适合选作索引的关键字.主 ...

  8. sql如何遍历几百万的表_关于SQL查询效率,100w数据,查询只要1秒

    1.关于SQL查询效率,100w数据,查询只要1秒,与您分享: 机器情况 p4: 2.4 内存: 1 G os: windows 2003 数据库: ms sql server 2000 目的: 查询 ...

  9. mysql 索引未命中_联合索引命中率问题导致SQL查询效率慢的问题

    执行 MySQL DumpSlow 结果是:Count: 1358  Time=0.33s (448s)  Lock=0.00s (0s)  Rows=2.5 (3343) Count:出现次数 Ti ...

最新文章

  1. python从控制台读取数据
  2. 【VMware vSAN 6.6】8.2.合规性:我们有软硬件项目解决方案
  3. LLC 逻辑链路控制
  4. 边缘计算:安全保护 快速响应
  5. ES6 Generator async
  6. EasyUI datagrid : 启用行号、固定列及多级表头后,头部行号位置单元格错位的问题...
  7. 求二叉树中节点的最大距离
  8. ubuntu c++检测usb口事件变化_拆解报告:美式双USBA口充电插座
  9. Linux 中文无法显示或显示方块
  10. 【Warning】Unity2021.1将不兼容之前版本ECS
  11. NSTimer实现读秒、倒计时等周期性操作
  12. Spring AOP中declare-parents为特定的类增加新的功能
  13. luogu P1020 导弹拦截
  14. 绘制卡方分布的概率密度函数 matlab,MATLAB如何使用chi2pdf函数计算卡方分布的概率密度...
  15. micropython logging文档
  16. Subscription expires on 2017/2/25. Usage of PhpStorm will no longer be possible.
  17. 中标麒麟操作系统V5.0(龙芯)kubernetes源码编译
  18. Beamforming and Antenna Pattern 2021-11-05
  19. Scratch 游戏项目学习法 —— 接苹果(十)接住苹果
  20. 选字验证码破解思路:CTPN - 自然场景文本检测

热门文章

  1. cygwin中安装软件
  2. 自定义eachFile遍历文件夹文件
  3. 重新想,重新看——CSS3变形,过渡与动画①
  4. Linux内核如何装载和启动一个可执行程序
  5. [模仿微软Live.cn]JavaScript输入邮箱自动提示
  6. 乘法器之五(混和式乘法器(Hybrid multiplication))
  7. memcached全面剖析–3.memcached的删除机制和发展方向
  8. python变量区分大小写吗_Python变量名是不是区分大小写的
  9. 三级工作台抽奖出啥_【早早聊】如何落地一体化运营工作台
  10. leetcode算法题--链表中倒数第k个节点