9

抱歉,如果这不合适,但实际上这是“为什么”而不是“如何”。不确定这是否合适,但不知道有更好的地方问,我想不出如何用谷歌来表达我想要的东西。

    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 子句中的某些内容。

干杯。

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

    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
4

4 回答 4

7

我的猜测是,log(0)由于常数折叠而过早地进行了有效评估,而1/0不是,因为它的基数估计或者更可能是 ANSI_WARNINGS 设置会影响除以零的预期结果(溢出与 NULL)。

于 2012-05-22T14:29:55.393 回答
2

If I attempt to run it I am expecting SQL to jump past the IF clause and move to the end.

When you run your batch three things happen

  1. Your SQL is parsed

  2. Your SQL is compiled

  3. Your SQL is executed

What's unfortunate is that both compile and execution errors in a batch in SQL server result in the same "Query Completed with errors" message. So lets use a Procedure where its easier to see the difference

Consider the following

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

That procedure parses fine. However it can't be compiled unless we remove the first thre SELECTs because we have some constants that are invalid as parameters. It would be nice if SELECT 1/0 also caused a compile time error instead of a run time error but as @Alex K points out the behavior is based on ANSI_WARNINGS so its not a compile time error.

So that's why we see differences between when the first two. It also explains why TRY CATCH didn't work since its a compile time error.

Now why does SQL server compile unreachable code. Because in general in order to know that it's unreachable requires a solution to the halting problem. You can solve it for some cases but then this ...


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

would have different behavior which is even more confusing.

if (1=0)
   SomeCompiletime error
于 2012-05-22T15:19:27.433 回答
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

但是我不得不想知道,为永远不会成立的条件设置脚手架并发布您知道会出错的声明有什么意义?

于 2012-05-22T14:18:21.717 回答
0

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

这是一个猜测。这也意味着这种差异是不确定的。您必须弄清楚在特定脚本中哪个工作或不工作 - 并且行为可能会在数据库版本之间发生变化。

于 2012-05-22T14:24:23.767 回答