2012-05-22 78 views
9

道歉,如果这不适合,但真的这是'为什么',而不是'如何'。不知道这是否合适,但不知道一个更好的地方要问,我想不出如何说出谷歌来获得我正在寻找的东西。IF条款的SQL评估

IF 'hell' = 'freezing over' 
    BEGIN 
    SELECT log(0) 
    END 

看看那句话。没有哪个世界中的IF条款是真实的。如果我试图运行它,我希望SQL跳过IF子句并移动到最后。相反,我得到:

An invalid floating point operation occurred. 

这是奇怪的。所以我想这就是SQL这样做的方式。除...

IF 'hell' = 'freezing over' 
    BEGIN 
    SELECT 1/0 
    END 

这里没有错误。 IF子句中的语句仍然会产生错误。任何人都可以解释为什么这不会发生?

这是在调试大量SQL计算时使用EXP(SUM(LOG()))在if子句中累积数据的过程。我可以改变代码以阻止这种情况再次发生,但为什么它在未满足的IF子句中评估某些内容。

干杯。

编辑:额外的娱乐。试着抓? Pffft

IF 1=2 
    BEGIN 
     BEGIN TRY 
      SELECT SQRT(-1) 
     END TRY 
     BEGIN CATCH 
     END CATCH 
    END 

非数学:

IF 1=2 
    BEGIN 
    SELECT SUBSTRING('hello',-1,-1) 
    END 

回答

7

我的猜测是,log(0)有效过早评估由于constant-folding1/0不是,要么是由于其基数估计或更可能的事实ANSI_WARNINGS设置将被零(溢出VS影响鸿沟的理想结果空值)。

+0

ANSI警告没有区别,但这是我认为我需要的链接,所以非常感谢:) –

+0

欢迎您。通过ANSI_WARNINGS我的意思是,如果在任何时候发布“set ansi_warnings off”,那么您将看不到除零错误。由于编译器无法预先知道ANSI_WARNINGS标志在编译查询运行时的状态,因此它必须忽略在编译时引发零除异常的任何折叠表达式,因为它无法预测所需的行为。 (如果ansi_warnings保证始终关闭1/0将折叠为NULL) –

2

解析器根本没有的智慧追随你的IF逻辑。请看下面的例子:

IF (1 = 0) 
BEGIN 
    CREATE TABLE #t(x INT): 
END 
ELSE 
BEGIN 
    CREATE TABLE #t(x INT): 
END 

无论你执行它甚至只是解析它,解析器着眼于所有批次的CREATE TABLE语句,并确定您试图创建表两次(第一个副本显然没有按这不会发生)。结果:

消息2714,级别16,状态1,行7
已经有一个数据库中的命名对象 '#T'。

我真的不知道我是否有更好的答案,而不是解析器不如你那么聪明。

您可以通过使用动态SQL将推迟的问题推迟到运行时来击败解析器,例如,

IF 'hell' = 'freezing over' 
BEGIN 
    EXEC sp_executesql N'SELECT log(0);'; 
END 

但后来我不得不怀疑,究竟是建立脚手架,这将永远是真实的情况,并发出你知道是怎么回事错误语句的意义呢?

+0

大约有5 IF条件。其中一项计算是在其他条件下会导致错误的数据上完成的。 这些例子很容易显示行为。它更像是 IF [数据不会导致错误] “Do calc” END –

+0

另外,有趣的是它解析得很好,但不能成功执行 –

+0

是的解析器并不总是提前捕获所有类型的错误,并提前捕获一些在运行时永远不会出错的东西。正如我所说,这不是最聪明的。但它确实有很多复杂的规则和算法来确定它将允许解析的成功与否。这些规则没有记录,所以@GordonLinoff解释说你只需要知道什么样的事情会使解析器发生变化,以及它会让什么样的事情滑动​​。 –

0

显然,SQL编译器试图在编译时与运行时评估一些表达式。我的猜测是,一个部门被认为不太昂贵,所以它推迟到运行时间。另一方面,像log()这样非常复杂的东西价格昂贵,他们希望在编译时做到这一点。

这是一个猜测。这也意味着这种差异是非确定性的。你必须找出哪一个工作或不工作在一个特定的脚本 - 并且行为可能会改变数据库的版本之间。

2

如果我试图运行它我期待SQL跳过去IF子句和移动到结束。

当您运行批处理三件事情发生

  1. 你的SQL解析

  2. 你的SQL编译

  3. 你的SQL执行

什么是不明rtunate是SQL Server中批处理中的编译和执行错误导致相同的“Query Completed with errors”消息。所以让我们用一个程序,其中它更容易看到其中的差别

考虑以下

Create proc compiletime 
as 
SELECT log(0) 
SELECT SQRT(-1) 
SELECT SUBSTRING('hello',-1,-1) 
SELECT 1/0 

该程序解析罚款。然而它不能被编译,除非我们删除第一个三选择,因为我们有一些常量作为参数是无效的。如果SELECT 1/0也会导致编译时错误而不是运行时错误,但是由于@Alex K指出该行为基于ANSI_WARNINGS,所以它不是编译时错误。

所以这就是为什么我们看到当第2之间的差异。它也解释了为什么TRY CATCH从编译时错误起不起作用。

现在为什么SQL服务器编译可达代码。因为一般为了知道它无法访问需要解决停止问题。你可以解决它的一些案件,但随后这个...


DECLARE @x as integer 
SET @x = SomeFunction() 
If (1 = @x) 
    SomeCompiletime error 

会有不同的行为,这是更加混乱。

if (1=0) 
    SomeCompiletime error