浅析Sql Server参数化查询

SQL Server中参数化查询的挑战与解决方案 参数化查询在SQL Server开发中是一项重要的技术,可以提高查询的安全性、性能和可维护性。通过克服查询字符串拼接的风险,使用强制参数化和辅助工具来提高性能和可维护性,开发人员可以更好地应用参数化查询,并有效地解决相关的挑战。通过启用此选项,SQL Server会自动将查询中的常量值参数化,从而提高查询性能和缓存的复用性。在不使用参数化查询的情况下,查询字符串通常需要通过拼接来构建,将用户的输入直接嵌入到查询中。参数化查询通过将查询字符串和参数分离,将参数的值作为查询的输入,从而消除了SQL注入的风险。 阅读详情
浅析Sql Server参数化查询


错误认识1.不需要防止sql注入的地方无需参数化
  参数化查询就是为了防止SQL注入用的,其它还有什么用途不知道、也不关心,原则上是能不用参数就不用参数,为啥?多麻烦,我只是做公司内部系统不用担心SQL注入风险,使用参数化查询不是给自己找麻烦,简简单单拼SQL,万事OK

错误认识2.参数化查询时是否指定参数类型、参数长度没什么区别
  以前也一直都觉的加与不加参数长度应该没有什么区别,仅是写法上的不同而已,而且觉得加参数类型和长度写法太麻烦,最近才明白其实两者不一样的,为了提高sql执行速度,请为SqlParameter参数加上SqlDbType和size属性,在参数化查询代码编写过程中很多开发者忽略了指定查询参数的类型,这将导致托管代码在执行过程中不能自动识别参数类型,进而对该字段内容进行全表扫描以确定参数类型并进行转换,消耗了不必要的查询性能所致。根据MSDN解释:如果未在size参数中显式设置Size,则从dbType参数的值推断出该大小。如果你认为上面的推断出该大小是指从SqlDbType类型推断,那你就错了,它实际上是从你传过来的参数的值来推断的,比如传递过来的值是"username",则size值为8,"username1",则size值为9。那么,不同的size值会引发什么样的结果呢?且经测试发现,size的值不同时,会导致数据库的执行计划不会重用,这样就会每次执行sql的时候重新生成新的执行计划,而浪费数据库执行时间。

下面来看具体测试

首先清空查询计划

DBCC FREEPROCCACHE

传值username,不指定参数长度,生成查询计划

复制代码
using (SqlConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    SqlCommand comm = new SqlCommand();
    comm.Connection = conn;
    comm.CommandText = "select * from Users where UserName=@UserName";
    //传值 username,不指定参数长度
    //查询计划为(@UserName varchar(8))select * from Users where UserName=@UserName
    comm.Parameters.Add(new SqlParameter("@UserName", SqlDbType.VarChar) { Value = "username" });
    comm.ExecuteNonQuery();
}
复制代码

 

传值username1,不指定参数长度,生成查询计划

复制代码
using (SqlConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    SqlCommand comm = new SqlCommand();
    comm.Connection = conn;
    comm.CommandText = "select * from Users where UserName=@UserName";
    //传值 username1,不指定参数长度
    //查询计划为(@UserName varchar(9))select * from Users where UserName=@UserName
    comm.Parameters.Add(new SqlParameter("@UserName", SqlDbType.VarChar) { Value = "username1" });
    comm.ExecuteNonQuery();
}
复制代码

传值username,指定参数长度为50,生成查询计划

复制代码
using (SqlConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    SqlCommand comm = new SqlCommand();
    comm.Connection = conn;
    comm.CommandText = "select * from Users where UserName=@UserName";
    //传值 username,指定参数长度为50
    //查询计划为(@UserName varchar(50))select * from Users where UserName=@UserName
    comm.Parameters.Add(new SqlParameter("@UserName", SqlDbType.VarChar,50) { Value = "username" });
    comm.ExecuteNonQuery();
}
复制代码

 

传值username1,指定参数长度为50,生成查询计划

复制代码
using (SqlConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    SqlCommand comm = new SqlCommand();
    comm.Connection = conn;
    comm.CommandText = "select * from Users where UserName=@UserName";
    //传值 username1,指定参数长度为50
    //查询计划为(@UserName varchar(50))select * from Users where UserName=@UserName
    comm.Parameters.Add(new SqlParameter("@UserName", SqlDbType.VarChar,50) { Value = "username1" });
    comm.ExecuteNonQuery();
}
复制代码

 

使用下面语句查看执行的查询计划

SELECT cacheobjtype,objtype,usecounts,sql FROM sys.syscacheobjects 
WHERE sql LIKE '%Users%'  and sql not like '%syscacheobjects%'

结果如下图所示

 可以看到指定了参数长度的查询可以复用查询计划,而不指定参数长度的查询会根据具体传值而改变查询计划,从而造成性能的损失。

这里的指定参数长度仅指可变长数据类型,主要指varchar,nvarchar,char,nchar等,对于int,bigint,decimal,datetime等定长的值类型来说,无需指定(即便指定了也没有用),详见下面测试,UserID为int类型,无论长度指定为2、20、-1查询计划都完全一样为(@UserIDint)select*from Users where UserID=@UserID

复制代码
using (SqlConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    SqlCommand comm = new SqlCommand();
    comm.Connection = conn;
    comm.CommandText = "select * from Users where UserID=@UserID";
    //传值 2,参数长度2
    //执行计划(@UserID int)select * from Users where UserID=@UserID
    comm.Parameters.Add(new SqlParameter("@UserID", SqlDbType.Int, 2) { Value = 2 });
    comm.ExecuteNonQuery();
}
using (SqlConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    SqlCommand comm = new SqlCommand();
    comm.Connection = conn;
    comm.CommandText = "select * from Users where UserID=@UserID";
    //传值 2,参数长度20
    //执行计划(@UserID int)select * from Users where UserID=@UserID
    comm.Parameters.Add(new SqlParameter("@UserID", SqlDbType.Int, 20) { Value = 2 });
    comm.ExecuteNonQuery();
}
using (SqlConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    SqlCommand comm = new SqlCommand();
    comm.Connection = conn;
    comm.CommandText = "select * from Users where UserID=@UserID";
    //传值 2,参数长度-1
    //执行计划(@UserID int)select * from Users where UserID=@UserID
    comm.Parameters.Add(new SqlParameter("@UserID", SqlDbType.Int, -1) { Value = 2 });
    comm.ExecuteNonQuery();
}
复制代码

这里提一下,若要传值varchar(max)或nvarchar(max)类型怎么传,其实只要设定长度为-1即可

复制代码
using (SqlConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    SqlCommand comm = new SqlCommand();
    comm.Connection = conn;
    comm.CommandText = "select * from Users where UserName=@UserName";
    //类型为varchar(max)时,指定参数长度为-1
    //查询计划为 (@UserName varchar(max) )select * from Users where UserName=@UserName
    comm.Parameters.Add(new SqlParameter("@UserName", SqlDbType.VarChar,-1) { Value = "username1" });
    comm.ExecuteNonQuery();
}
复制代码

当然了若是不使用参数化查询,直接拼接SQL,那样就更没有查询计划复用一说了,除非你每次拼的SQL都完全一样

总结,参数化查询意义及注意点

1.可以防止SQL注入

2.可以提高查询性能(主要是可以复用查询计划),这点在数据量较大时尤为重要

3.参数化查询参数类型为可变长度时(varchar,nvarchar,char等)请指定参数类型及长度,若为值类型(int,bigint,decimal,datetime等)则仅指定参数类型即可

4.传值为varchar(max)或者nvarchar(max)时,参数长度指定为-1即可

5.看到有些童鞋对于存储过程是否要指定参数长度有些疑惑,这里补充下,若调用的是存储过程时,参数无需指定长度,如果指定了也会忽略,以存储过程中定义的长度为准,不会因为没有指定参数长度而导致重新编译,不过还是建议大家即便时调用存储过程时也加上长度,保持良好的变成习惯

 

原文:http://www.cnblogs.com/lzrabbit/archive/2012/04/21/2460978.html

嵌入式开发必看:STM32 DMA双缓冲配置全攻略(含CubeMX教程) 本文详细解析了STM32 DMA双缓冲机制的原理与优势,并提供了基于CubeMX的完整配置教程。通过硬件自动切换两个缓冲区,实现DMA传输与CPU处理的真正并行,有效降低中断频率,提升高速数据采集、音频流处理等场景下的系统效率和实时性。 阅读详情

相关推荐

告别Windows!Oculus Quest 2在Mac M1上的串流实战(scrcpy+SideQuest避坑指南)

本文为Mac M1用户提供了Oculus Quest 2在macOS上实现高清低延迟串流的实战指南。通过使用开源工具scrcpy和SideQuest,绕开官方限制,详细讲解了从环境准备、无线调试到画质与延迟优化的完整步骤,重点解决了Apple Silicon芯片Mac的连接与性能问题,助你告别Windows依赖。

weixin_29224309的博客 307

【C#】Sql Server 设置IN查询方法内的参数,固定参数、动态参数以及通过分隔含有逗号隔开的字符串转数据集

在平时使用sql语句查询时,in查询肯定少不了,多数用于一些数据统计或者测试类。in查询并不建议放到实际常用的查询列表里。 此篇文章主要简单聊聊,in查询方法内的参数设置方式,以及通过分隔函数将含有逗号隔开的字符串转为数据集进行查询

小5聊的博客 8063

步进电机驱动算法——梯形加减速算法

目录步进电机梯形加减速梯形加减速算法原理分析算法实现 步进电机梯形加减速 电机的控制方式一般分为开环控制与闭环控制两种控制方式,其中开环控制原理框图如下: 这种种控制方式的特点是:控制简单、实现容易、价格较低,这种开环控制方式,负载位置对控制电路没有反馈。 对于步进电机,控制脉冲的输入并不依赖于转子的位置,而是按一固定的规律发出控制脉冲,如果励磁变化太快,电机不能移动到新的位置,那么实际负载位置与理想位置就会产生一个偏差,就有可能出现失步或者过冲现象,所以需要使用加减速算法。 梯形加减速算法,S加减速算法等

aaa376070331的博客 3万+

sql查询时添加一列为固定值

一、实现效果 二、实现的sql语句 //sql查询添加一列固定值 SELECT 表字段1,表字段2,表字段3,'固定值字符串' AS FixedStr FROM 表名称

提供C#相关开发 、云计算、运维测试、网络安全相关技术分享与服务,有意向可联系。 1万+

RAG-Anything 实战:构建企业级多模态知识图谱问答系统

本文详细介绍了如何利用RAG-Anything构建企业级多模态知识图谱问答系统,解决跨模态文档处理的痛点。通过架构设计解析、部署实战指南和典型业务场景解决方案,帮助企业实现高效的知识管理和智能检索,提升决策效率。

weixin_29089057的博客 385

用扩展点为 Fiori Elements 添加自定义动作:从原理到落地的完整实践

本文介绍了如何在基于OData V4的Fiori Elements应用中通过Extension Points添加自定义Action按钮。主要内容包括: 扩展点动作与标准注解动作的区别:扩展点用于非标准前端流程、组合操作等场景,需通过extensionAPI与框架交互。 扩展点动作的插入位置:可放置在List Report表格工具栏、Object Page页眉/页脚/Section工具栏等位置,并说明了相应的manifest.json配置节点。 配置模型与处理函数契约:详细说明了press、visible/e

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 248

SQLServer 参数化查询经验分享

<br /> 什么是参数化查询? <br />   一个简单理解参数化查询的方式是把它看做只是一个T-SQL查询,它接受控制这个查询返回什么的参数。通过使用不同的参数,一个参数化查询返回不同的结果。要获得一个参数化查询,你需要以一种特定的方式来编写你的代码,或它需要满足一组特定的标准。 <br />  有两种不同的方式来创建参数化查询。第一个方式是让查询优化器自动地参数化你的查询。另一个方式是通过以一个特定方式来编写你的T-SQL代码,并将它传递给

lionzl的专栏 937

深度学习100例-卷积神经网络(CNN)彩色图片分类 | 第2天

文章目录前期工作设置GPU导入数据标准化可视化构建CNN网络编译训练模型预测模型评估 前期工作 设置GPU 如果使用的是CPU可以忽略这步 import tensorflow as tf gpus = tf.config.list_physical_devices("GPU") if gpus: gpu0 = gpus[0] #如果有多个GPU,仅使用第0个GPU tf.config.experimental.set_memory_growth(gpu0, True) #设置GPU显存用量

“365天深度学习训练营”报名进行中~ 7853
上一篇: 一个在线测试Html、脚本代码的网站,留作备份
下一篇: C#实现压缩与解压缩
Python
博客等级 码龄26年 1973粉丝 1567原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值