Posted on 2006-08-15 17:06
大大毛 阅读(1351)
评论(1) 编辑 收藏 所属分类:
SQL
起因:
在工作中经常都会用到根据一个参数的值来做不同的 Where 条件的情况,例如:
1.在提供给用户的查询界面上提供了多个筛选条件,如果用户选了就使用条件,如果不选就放弃该条件;
2.存储过程的预留扩充功能,如 当前要求返回条件为真的记录,为了扩充,让存储过程根据参数可以返回条件为 真/假/全部 的记录等。
在这些情况下,通常有3种实现方法:
a.在外部(相对于数据库)拼装查询语句,我称它为外部动态SQL,嘻嘻;
b.在内部(同上)拼装查询语句,我称它为内部动态SQL;
c.在存储过程中使用大量的 IF 开关,搭配 ctrl+C,ctrl+V,edit操作,写上N条的SQL语句。
问题的分析:
a 方法,该方法最多见,因为这是非常easy的办法,而且适用面最广,干啥事都直接来个SQL语句发往连接对象即可。当然这种方法在实现时也是N多的 IF,同时这种方法也会遗留下隐患,稍不留神就会成为 SQL注入 的牺牲品。
b 方法,只是在存储过程实现了 a 方法,在减少隐患的同时,缩小了适用面,比如低版本的 mysql 就不支持存储过程。
c 方法,是 b 方法的变种,虽然代码量非常的大,但是结构上很清晰,执行速度快。
解决方法:
在工作中找到的一个方法,适用于一定的问题域,可以将N多的SQL合并。
为了能够清楚的说明,举个例子先。
例(特定值的条件查询):
现在提供一个查询界面给管理员,让他能够对 log表 的查询条件进行定制。
查询条件有:
1.组ID,对应字段gID
2.用户ID,对应字段uID
3.记录类型,对应字段logType
条件的判断为:
如果用户选择了条件,那么就在Where中进行筛选,否则就忽略该条件,例如用户如果选择了 gID=1,那么就只选择组ID=1的用户Log,否则就返回全部的用户。通常是用上IF,这样:
string
sql
=
"
select * from log
"
;
string
where
=
""
;
if
(gID
!=
null
) {
where
=
where
+
"
gID=
"
+
gID
+
"
and
"
;
}
//
更多的IF
//
where字串还要处理一下
sql
+=
where;
现在使用MS SQL 的存储过程可以这样实现:
定值条件查询
--
取得Log
--
gID(int):组ID
--
uID(int):用户ID
--
logType(int):log类型
create
procedure
p_getLog
@gID
int
=
null
,
@uID
int
=
null
,
@logType
int
=
null
as
begin
select
*
from
log
where
gID
=
isNull
(
@gID
,gID)
and
uID
=
isNull
(
@uID
,uID)
and
logType
=
isNull
(
@logType
,logType)
end
仔细分析一下上面的存储过程不难看出问题所在,实际上就是用了Ms SQL的 isNull系统函数,它在判断特征值或列为空时,就用第2参数所示的值或列去取代,否则就取原值,就拿 gID = isNull ( @gID , gID ) 语句来说吧,
@gID 为一个非空值时,如 @gID = 1,那么表达式成为 where gID = 1
@gID的值为空 =null 时 ,那么该表达式实现上就成了 where gID = gID,多了这个不用担心会增加负荷,数据库还是有点智能会忽略掉这个条件的,嘻。
上面的代码是一个比较简单的例子,可以在存储过程的代码区增加对输入参数的过滤,如 if not exists(select 1 from log where gID = @gID) set @gID=null 等。
这样一来,既可以利用存储过程带来的好处,又利用了 isNull 带来的实惠,一举N得啊。
后记:
这个解决方案是存在有局限性的,并不是什么地方都能适用,但是它也不失为一个解决问题的方法。
--是人总要有点自己的想法,呵呵。