为什么SQL在if exists结构的true部分中计算语句,即使`if exists`返回false?

时间:2015-12-16 10:36:13

标签: sql sql-server

(我为这个可怕的解释提前道歉,但是如果你运行下面的查询,你应该明白我的意思!)

为什么MSSQL会评估true构造的if exists部分中的语句,即使if exists返回false,也会导致错误?

例如,在下面的两个查询中,第一个检查表是否存在(它确实存在),并检查该表是否具有某些列。出于某种原因,运行此查询会引发以下错误,因为表存在,但列不存在。

Msg 207, Level 16, State 1, Line 21
Invalid column name 'colB'.
Msg 207, Level 16, State 1, Line 21
Invalid column name 'colC'.
Msg 207, Level 16, State 1, Line 21
Invalid column name 'colA'.

我期望这里的行为是让SQL移动到构造的falsepart,而不会抛出错误。 (与下一个查询一样)。

但是,第二个脚本(相同的条形表名称)成功执行。这是因为查询正在搜索的表不存在。

--Scripts to setup the example.
CREATE DATABASE TEST 
GO
USE TEST
GO
CREATE TABLE t1 (colD VARCHAR(255)) --Create a table with the correct name, but incorrect column names.
GO

--This query fails, because t1 exists, even though the columns in t1 don't.
IF EXISTS (select * from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1' AND COLUMN_NAME IN ('colA','colB','colC'))
BEGIN
    SELECT colA FROM t1 WHERE colB = 0 AND colC = 1
END
ELSE BEGIN
    SELECT 'FALSE'
END

GO

--This query executes ok, because t2 does not exist.
IF EXISTS (select * from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't2' AND COLUMN_NAME IN ('colA','colB','colC'))
BEGIN
    SELECT colA FROM t2 WHERE colB = 0 AND colC = 1
END
ELSE BEGIN
    SELECT 'FALSE'
END

当第二个查询运行正常时,是否有人能够向我解释为什么第一个查询错误?

到目前为止,我只是设法在Microsoft SQL Server 2012中测试它。

2 个答案:

答案 0 :(得分:4)

回答这个问题的第一部分。假设熟悉具有某种形式的运行时类型检查的语言(例如C#)(例如,反射)。

假设你有这样的代码:

SomeType t = GetSomeTypeFromSomewhere();
if(t.GetType().GetMethod("FunTimes")!=null)
{
     t.FunTimes();
}

并假设SomeType 包含名为FunTimes的公共方法。尽管我已经写了一个试图调用FunTimes方法的警卫,但我收到了一个错误。而且,具体来说,我得到一个编译时间错误 - C#编译器甚至无法生成代码,更不用说接近运行代码,从GetMethod()获取结果并决定不运行嵌套块中的代码。

要切换回您的代码,完全相同的分析类型适用于此处:

IF EXISTS (select * from INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 't1' AND COLUMN_NAME IN ('colA','colB','colC'))
BEGIN
    SELECT colA FROM t1 WHERE colB = 0 AND colC = 1
END
ELSE BEGIN
    SELECT 'FALSE'
END

SQL Server尝试编译此批处理并失败。它永远不会执行代码,所以它永远不会决定采用哪个分支(IFELSE)。

所以,如果以上所有都是真的,为什么第二段代码会起作用呢?这是因为T-SQL的一个特殊功能称为Deferred Name Resolution。基本上,有一个特殊的规则适用于缺少的对象是(或视图,因为这两个是无法区分的,直到可以找到对象)。在该特定实例中,SQL Server不会立即发出编译错误信号。

在延迟名称解析下,执行将开始,如果某些内容导致架构更改(例如通过添加缺少的表/视图),则会导致系统重新编译剩余的代码。

答案 1 :(得分:0)

我认为你正在评估错误的结果(并且这不是你的错恕我的意思)。

EXISTS部分在两种情况下都返回FALSE。但是,SQL查询解析器很有趣,它解析内部表达式并在执行语句之前给出错误,只有在缺少列时,如果缺少表,它不会给出错误。

在您的第一个查询似乎正在评估为TRUE时,尝试将表名更改为t2之类的内容,您会看到它在两者中运行并评估为FALSE。