我们拥有的一些存储过程包含条件逻辑,如下所示:
Create Procedure dbo.DoSomething(Some Parameters)
As
...
If (Some Condition) Begin
Set @SomeVariable = SomeValue
End
...
Select ...
当这样的存储过程用作 MS Access 表单的记录源,并且用户尝试使用表单的内置排序/过滤功能时,MS Access 会尝试以 FMTONLY 模式执行存储过程(显然,寻找存储过程提供的行集)。
大多数人都知道(现在包括我们自己:-),当 FMTONLY 设置为 ON 时,SQL Server 会忽略条件语句。在下面所示的示例中,Set @SomeVariable = SomeValue
无论是否执行语句Some Condition
确实如此,这显然给我们带来了一些麻烦。
-- EXAMPLE
-- -------
Create Procedure dbo.DoSomething(..., @vcSomeDate as VarChar(50), ...)
As
...
Declare @dtSomeDate As Datetime
If (IsDate(@vcSomeDateOrAgeInDays)) Begin
-- The next statement fails miserably when FMTONLY=ON
Set @dtSomeDate = @vcSomeDateOrAgeInDays
End Else Begin
...
End
...
为了规避这个问题,我们“包装”条件逻辑(或受 FMTONLY 影响的任何其他代码片段),如下所示:
Create Procedure dbo.DoSomething(Some Parameters)
As
...
-- HACK: Protection from unexpected FMTONLY mode
Declare @wasFmtonlyOn As Bit; If (0 = 1) Set @wasFmtonlyOn = 1; SET FMTONLY OFF
...
If (Some Condition) Begin
Set @SomeVariable = SomeValue
End
...
-- /HACK: Protection from unexpected FMTONLY mode
If (@wasFmtonlyOn = 1) SET FMTONLY ON
...
Select ...
(“保护代码”的这种丑陋的一行格式是故意的:我们认为解决一些奇怪问题所需的黑客不值得正确的格式;恰恰相反,我们认为它们应该适合尽可能少的代码行.:-)
无论如何,这种“保护”效果很好,但它有点过于冗长,并且没有我们希望的那么封装。例如,我们肯定更愿意隐藏黑客的实际逻辑 - 例如隐藏在标量 UDF 后面,如下所示:
Create Procedure dbo.DoSomething(Some Parameters)
As
...
declare @wasFmtonlyOn as bit; set @wasFmtonlyOn = dbo.SetFmtonly(0)
...
If (Some Condition) Begin
Set @SomeVariable = SomeValue
End
...
dbo.SetFmtonly(@wasFmtonlyOn)
...
Select ...
不幸的是,这似乎不起作用——无论是对于标量 UDF,还是另一个存储过程。看起来 FMTONLY 可以防止从任何地方返回任何数据。那么,来了主要问题:
如果您也必须处理这个问题(SQL Server 在 FMTONLY 模式下忽略条件),您是否能够想出比上述更好的“保护习惯用法”?
顺便说一句,我仍然不明白一件事:这个问题是 SQL Server 2005 中的错误还是功能?如果它是一个功能,那么它的充分理由是什么?
谢谢你!