在sql查询中使用局部变量作为开关是不好的做法
Posted
技术标签:
【中文标题】在sql查询中使用局部变量作为开关是不好的做法【英文标题】:Is it bad practice to use local variables as switches in sql queries 【发布时间】:2016-02-23 21:30:58 【问题描述】:我在存储过程中有一个动态 sql 查询,我应该将其转换为表值函数 (TVF)。 正在使用如下代码构建字符串:
Declare @str = 'select * from tableName where table = ''words'''
If @flag = 'ONE'
set @str = @str + 'AND year_id = '+@year_id
ELSE
If @flag = 'TWO'
set @str = @str + 'AND record_id = '+@record_id
set @str = @str + 'order by year_id, record_id'
exec (@str)
在 TVF 中,您无法运行插入语句,因此您无法将此 exec 查询的结果返回给用户(至少据我所知,请随时纠正我)。
为了绕过这个限制,我创建了一个过滤器,该过滤器仅在局部变量具有给定值时应用。
select *
from tableName
where table = 'words'
AND (
(@flag = 'ONE' and year_id = @year_id)
OR (@flag = 'TWO' and record_id = @record_id)
)
Order by year_id, record_id
现在,这似乎适用于更简单的查询,但对于更复杂的查询,您需要为每个可能的动态 sql 字符串组合创建一个条件。
除此限制外,以这种方式编写查询是否存在任何安全和/或性能风险?有没有更简单的方法来为 TVF 编写这样的查询?
【问题讨论】:
为什么不创建两个函数,每个标志一个?这可能会让您获得最佳性能。 我的查询实际上有 8 种不同的可能性。对于大致相同的查询有 8 个不同的函数对我来说似乎很笨重(并且维护容易出错)。现有报表也将使用此查询(最好不要重新设计整个报表以使用多个功能)。本来以为Table-valued-function的性能可能有问题,但经过测试,似乎和存储过程差不多。 在我看来,查询看起来应该很笨重。而且“闻起来”对我来说有点好笑,但到目前为止,我不知道为什么我不应该这样编码(因此提出了问题)。 好吧,OR
可能不允许在您要比较的任何字段上使用索引。您可以在其中添加OPTION(RECOMPILE)
以强制执行仅针对一个条件的执行计划,该条件可以使用任何索引,但重新编译也需要一些时间。无论如何都要检查执行计划。
【参考方案1】:
将动态 SQL 转换为 ITVF 的目的是消除这种废话。只需将 ITVF 写为
create function schemaNme.funcName() returns table return
select * from tableName where table = 'words';
并让客户端直接添加任何所需的过滤条件,如下所示:
select *
from schemaNme.funcName() t
where t.year_id = @year_id;
这隐藏了数据如何从物理表中组装到逻辑视图中的详细信息,同时仍使客户端能够根据需要进行过滤。
更新我的以下补充评论:
ITVF 是一个参数化视图,可能带有缓存的查询计划。像任何其他视图(或表)一样,如果您希望动态修改访问机制,就像这里一样,您将不得不使用动态 SQL。但是,由于查询的内部结构已经为您封装在 ITVF 中,在这种情况下,您可以简单地动态修改 WHERE 子句,这是一项更简单的任务
【讨论】:
你能指出一个地方,ITVF 的这个意图得到了扩展吗? ITVF 是一个参数化视图,可能带有缓存的查询计划。像任何其他视图(或表)一样,如果您希望动态修改访问机制,就像这里一样,您将不得不使用动态 SQL。但是,由于查询的内部结构已经为您封装在 ITVF 中,在这种情况下,您可以简单地动态修改 WHERE 子句,这是一项更简单的任务。 感谢您的帮助彼得!以上是关于在sql查询中使用局部变量作为开关是不好的做法的主要内容,如果未能解决你的问题,请参考以下文章