【发布时间】:2008-11-19 19:56:34
【问题描述】:
我们的一些存储过程包含条件逻辑,像这样:
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 会忽略条件语句。在下例中,不管Some Condition是否为真,都会执行Set @SomeVariable = SomeValue语句,这显然给我们带来了一些麻烦。
-- 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 ...
(“保护代码”的这种丑陋的单行格式是故意的:我们认为解决一些奇怪问题所需的 hack 不值得正确格式化;恰恰相反,我们认为它们应该适合几行代码尽可能。:-)
无论如何,这种“保护”工作正常,但它有点过于冗长,并且没有我们希望的那样封装。例如,我们肯定更喜欢隐藏 hack 的实际逻辑 - 例如在这样的标量 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 模式下忽略条件),你能想出比上面描述的更好的“保护习语”吗? p>
顺便说一句,我仍然不明白一件事:这个问题是 SQL Server 2005 中的错误还是功能?如果它是一个特性,那么它有什么好的理由呢?
谢谢!
【问题讨论】:
-
@Yarik:你能用问题的形式来表达这个吗? SQL Server 文档明确指出 SET FMTONLY ON 执行所有非条件语句,所以我不确定你的问题是什么?
标签: sql-server ms-access stored-procedures coding-style