一般情况下,SQL查询是相对固定的,一条语句变化的可能只是条件值,比如之前要求查询二年级学生信息,而后面需要查询三年级的信息,这样的查询一般查询的列不变,后面的条件只有值在变化,针对这种查询可以使用参数化查询的方式来提高效率,也可以时SQL操作更加安全,从根本上杜绝SQL注入的问题。

参数化查询的优势:

  1. 提高效率:之前说过,数据库在执行SQL的过程中,每次都会经过SQL的解析,编译,调用对应的数据库组件,这样如果执行多次同样类型的SQL语句,解析,编译的过程明显是在浪费资源,而参数化查询就是使用编译好的过程(也就是提前告诉数据库要调用哪些数据库组件),这样就跳过了对SQL语句的解析,编译过程,提高了效率(这个过程我觉得有点类似于C/C++语言的编译执行与脚本语言的解释执行)。
  2. 更加安全:从安全编程的角度来说,对于防范SQL注入方面,它比关键字过滤更有效,实现起来也更加方便。

科普SQL注入和安全编程

  • 什么是SQL注入:

    所谓SQL注入,就是通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。举个例子来说在用户登录时会输入用户名密码,这个时候在后台就可以执行这样的SQL语句

    select count(*) from user where username = 'haha' and password = '123456'

    只有输入对了用户名和密码才能登录,但是如果没有对用户输入进行校验,当用户输入一些SQL中的语句,而后台直接将用户输入进行拼接并执行,就会发生注入,比如此时用户输入 * ‘haha’ or 1 = 1 – * ,此时再后台执行的sql语句就变成了这样:

    select count(*) from user where username = 'haha' or 1 = 1 -- and password = ''

    这样用户就可以不用密码,直接使用用户名就登录了。而防范这类攻击,一般采用的是关键字过滤的方式,但是关键字过滤并不能杜绝这类工具,当一时疏忽忘记了过滤某个关键字仍然会产生这类问题。而且关键字过滤一般采用正则表达式,而正则表达式并不是一般人可以驾驭的。而防范SQL注入最简单也是最一劳永逸的方式就是参数化查询。

  • 为什么参数化查询能够从根本上解决SQL注入

    发生SQL注入一般的原因是程序将用户输入当做SQL语句的一部分进行执行,但是参数化查询它只是将用户输入当做参数,当做查询的条件,从数据库的层面上来说,它不对应于具体的数据库组件,它只是一组数据,而不会执行。这里可以简单的将传统的SQL拼接方式理解为C语言中的宏,宏也可以有参数,但是它不对参数进行校验,只是简单的进行替换,那么我可以使用一些指令作为参数传入,但是函数就不一样,函数的参数就是具体类型的变量或者常量。所以参数化查询从根本上解决的SQL注入的问题。

参数化查询的使用

前面说了这么多参数化查询的好处,那么到底怎么使用它呢?
在Java等语言中内置了数据库操作,而对于C/C++来说,它并没有提供这方方面的标准。不同的平台有自己独特的一套机制,但是从总体来说,思想是共通的,只是语法上的不同,这里主要是说明OLEDB中的使用方式。
1. 使用“?”符将SQL语句中的条件值常量进行替换,组成一个新的SQL语句,比如上面登录的查询语句可以写成
SQL
select count(*) from user where username = ? and password = ?

2. 调用ICommandText的SetCommandText设置sql语句。
3. 调用ICommandParpare的Prepare方法对含有”?”的语句进行预处理
4. 调用ICommandWithParameters方法的GetParameterInfo方法获取参数详细信息的DBPARAMINFO结构(类似于DBCOLUMNINFO)
5. 分配对应大小的DBBINDING缓冲用来保存每个参数的绑定信息
6. 调用IAccessor的CreateAccessor方法创建对应的访问器
7. 为参数分配缓冲,设置合适的参数后准备DBPARAMS结构
8. 调用ICommandText的Execute方法并将DBPARAMS结构的指针作为参数传入。
9. 操作返回的结果集对象

typedef struct tagDBPROPIDSET {DBPROPID *   rgPropertyIDs;ULONG        cPropertyIDs;GUID         guidPropertySet;
} DBPROPIDSET;

DBPARAMS结构的定义如下:

typedef struct tagDBPARAMS
{void *pData;DB_UPARAMS cParamSets;HACCESSOR hAccessor;
}   DBPARAMS;
  • pData是保存参数信息的缓冲;
  • cParamSets: 表示又多少个参数
  • hAccessor: 之前获取到的绑定结构的访问器句柄

下面是一个使用的例子:

BOOL QueryData(LPOLESTR pQueryStr, IOpenRowset* pIOpenRowset, IRowset* &pIRowset)
{IAccessor *pParamAccessor = NULL; //与参数化查询相关的访问器接口LPOLESTR pSql = _T("Select * From aa26 Where Left(aac031,2) = ?"); //参数化查询语句BOOL bRet = FALSE;DB_UPARAMS uParams = 0;DBPARAMINFO* rgParamInfo = NULL;LPOLESTR pParamBuffer = NULL;DWORD dwOffset = 0;DBBINDING *rgParamBinding = NULL;HACCESSOR hAccessor = NULL;DBPARAMS dbParams = {0};DBBINDSTATUS *pdbBindStatus = NULL;//设置SQLhRes = pICommandText->SetCommandText(DBGUID_DEFAULT, pSql);//预处理SQL命令pICommandPrepare->Prepare(0);hRes = pICommandText->QueryInterface(IID_ICommandPrepare, (void**)&pICommandPrepare);//获取参数信息hRes = pICommandText->QueryInterface(IID_ICommandWithParameters, (void**)&pICommandWithParameters);COM_SUCCESS(hRes, _T("查询接口ICommandWithParameters失败,错误码为:%08x\n"), hRes);hRes = pICommandWithParameters->GetParameterInfo(&uParams, &rgParamInfo, &pParamBuffer);COM_SUCCESS(hRes, _T("获取参数信息失败,错误码为:%08x\n"), hRes);rgParamBinding = (DBBINDING*)MALLOC(sizeof(DBBINDING) * uParams);ZeroMemory(rgParamBinding, sizeof(DBBINDING) * uParams);//绑定参数信息for (int i = 0; i < uParams; i++){rgParamBinding[i].bPrecision = rgParamInfo[i].bPrecision;rgParamBinding[i].bScale = rgParamInfo[i].bScale;rgParamBinding[i].cbMaxLen = 7 * sizeof(WCHAR); //行政区编号最大长度为6rgParamBinding[i].dwMemOwner = DBMEMOWNER_CLIENTOWNED;rgParamBinding[i].dwPart = DBPART_LENGTH | DBPART_VALUE;rgParamBinding[i].eParamIO = DBPARAMIO_INPUT;rgParamBinding[i].iOrdinal = rgParamInfo[i].iOrdinal;rgParamBinding[i].obLength = dwOffset;rgParamBinding[i].obStatus = 0;rgParamBinding[i].obValue = dwOffset + sizeof(ULONG);rgParamBinding[i].wType = DBTYPE_WSTR;dwOffset = dwOffset + sizeof(ULONG) + rgParamBinding[i].cbMaxLen;dwOffset = UPROUND(dwOffset);}//获取访问器pdbBindStatus = (DBBINDSTATUS*)MALLOC(uParams * sizeof(DBBINDSTATUS));ZeroMemory(pdbBindStatus, uParams * sizeof(DBBINDSTATUS))pParamAccessor->CreateAccessor(DBACCESSOR_PARAMETERDATA, uParams, rgParamBinding, dwOffset, &hAccessor, pdbBindStatus);COM_SUCCESS(hRes, _T("获取参数访问器失败,错误码为:%08x\n"), hRes);//准备参数dbParams.pData = MALLOC(dwOffset);ZeroMemory(dbParams.pData, dwOffset);dbParams.cParamSets = uParams;dbParams.hAccessor = hAccessor;for (int i = 0; i < uParams; i++){*(ULONG*)((BYTE*)dbParams.pData + rgParamBinding[i].obLength) = _tcslen(pQueryStr) * sizeof(WCHAR);StringCchCopy((LPTSTR)((BYTE*)dbParams.pData + rgParamBinding[i].obValue), _tcslen(pQueryStr) + 1, pQueryStr);}//执行SQLhRes = pICommandText->Execute(NULL, IID_IRowset, &dbParams, NULL, (IUnknown**)&pIRowset);return bRet;
}

完整代码


OLEDB 参数化查询相关推荐

  1. 关于OLEDB参数化查询【.net】

    使用参数化 DbCommand 的一个缺点是需要参数的代码将仅适用于支持相同语法的提供程序.OLEDB.SqlClient 和 Oracle 提供程序全部使用不同的语法.例如,用于命名和指定参数的 S ...

  2. mysql 查询语句 参数,mysql参数化查询语句有关问题

    mysql参数化查询语句问题 部分代码如下: using (MySqlConnection conn = new MySqlConnection(connectionString)) { conn.O ...

  3. 参数化查询 但未提供该参数(将null插入数据库)

    当你是使用参数化查询时为参数赋值时,比如command.Parameters.Add("@a",SqlDbType.Text,30).Value=a;当a=null是将报错: 错参 ...

  4. SqlParameter参数化查询

    上篇博客写了关于重构代码用到的SQLHelper类,这个类包括四种函数,根据是否含参和是否有返回值各分两种.在这里写写传参过程用到的SqlParameter. 如果我们使用如下拼接sql字符串的方式进 ...

  5. java参数化查询_小博老师解析Java核心技术 ——JDBC参数化查询(二)

    [步骤阅读四]SQL注入 按照以上方式开发,确实已经完成了基本的用户登录业务需求,但是这么做的话可以会出现一个比较严重的问题,那就是容易被SQL注入.所谓SQL注入,就是在需要用户填写信息,并且这些信 ...

  6. C# 中是否支持 Like 和 ln 条件的参数化查询 ?

    咨询区 Tom Ritter .NET 中的参数化查询我一直都像下面这样写. SqlCommand comm = new SqlCommand(@"SELECT * FROM Product ...

  7. php pdo 查询语句,PDO:预处理语句(参数化查询)

    @(PDO(PHP data object/PHP数据对象))[PDO|预处理语句|参数化查询] The database library called PHP Data Objects or PDO ...

  8. pdo 参数化查询 mysql函数_PDO笔记之参数化查询

    参数化查询解释在这里:Wiki参数化查询 (少有的Wiki中文比英文介绍的要详细的编程条目) PDO中参数化查询主要用到prepare()方法,然后这个方法会返回一个PDOStatement对象,也就 ...

  9. Sql Server参数化查询之where in和like实现之xml和DataTable传参

    在上一篇Sql Server参数化查询之where in和like实现详解中介绍了在Sql Server使用参数化查询where in的几种实现方案,遗漏了xml和表值参数,这里做一个补充 文章导读 ...

最新文章

  1. 快速得到两个list中不同部分的list
  2. HDU3068 最长回文
  3. python设置ini文件中的值_PyCharm设置python文件模板,自动读取文件信息。
  4. 数据库-设置mysql编码
  5. 小米平板android最新版本,想要翻身还需努力 小米平板2安卓版评测
  6. 8 年后重登王座,Python 再度成为 TIOBE 年度编程语言
  7. Nacos版本升级1.1.3 >> 1.3.1 —>再升级至1.3.2
  8. 敏捷开发系列学习总结(5)——这几招搞定团队协同Coding
  9. nginx(三)status状态页面的相关信息及配置,以及nginx的访问控制配置
  10. [bzoj1044][HAOI2008]木棍分割
  11. android动态加载.so,实现动态库升级
  12. 台达plc用c语言编程软件,台达plc编程
  13. 玩客云服务器怎么卖,玩客云使用教程;低价NAS怎么打造;玩客云现在还值得入手吗?-聚超值...
  14. 计算机里找不到刚装的固态硬盘,新装的固态硬盘系统里看不见?解决方法来了...
  15. 大数据与人工智能专业都这么火,我们应该怎么选?
  16. 抓住那头牛(宽搜bfs)
  17. 组策略 控制台登录计算机用用户,更方便的管理计算机!Windows组策略应用全攻略一...
  18. Sea.js简单使用
  19. 基于APS的供应链计划管理的类型阐述
  20. 不同类型的电机的工作原理和控制方法汇总

热门文章

  1. 《Python机器学习》基础代码
  2. Android中的Junit单元测试
  3. win11更新安装错误0x80073701解决方法
  4. 3GPP TS 23501-g51 中英文对照 | 4.4.2 SMS over NAS
  5. 如何把密码写入代码,让VBA自动撤销工作表保护 / 工作簿保护(使用VBA代码 保护工作表 / 工作簿 和取消保护工作表 / 工作簿)
  6. win10系统无法正常自动启动服务
  7. java syn包_月薪3K的后端面试点-网络与Java
  8. python系列——多进程之进程池(pool)
  9. 【Matlab】自定义函数的几种方法
  10. 2004数学二真题总结