sql - 为什么即使 `if exists` 返回 false,SQL 也会评估 if contains 构造的 true 部分中的语句?

标签 sql sql-server

(对于糟糕的解释,我提前表示歉意,但是如果您运行下面的查询,您应该明白我的意思!)

为什么 MSSQL 会计算 if isn't 构造的 true 部分中的语句,即使 ifexists 返回 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 中测试了这一点。

最佳答案

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

假设您有这样的代码:

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

并假设 SomeType 包含名为 FunTimes 的公共(public)方法。尽管我已经编写了一个保护程序来尝试调用 FunTimes 方法,但我还是收到了错误。具体来说,我遇到了编译时错误 - C# 编译器甚至无法生成代码,更不用说接近运行代码、从 GetMethod() 获取结果了并决定不在嵌套 block 中运行代码。

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

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 。基本上,当丢失的对象是表(或 View ,因为在找到对象之前两者是无法区分的)时,有一个特殊的规则适用。在该特定实例中,SQL Server 不会立即发出编译错误信号。

在延迟名称解析下,执行将开始,如果某些原因导致架构更改(例如通过添加缺少的表/ View ),这将导致系统重新编译其余代码。

关于sql - 为什么即使 `if exists` 返回 false,SQL 也会评估 if contains 构造的 true 部分中的语句?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34309721/

相关文章:

mysql - 需要有关多个数据库插入操作的建议

sql - 在 select 语句中选择多个模式

php - Codeigniter 查询到 mssql 查询

sql-server - 使用 terraform 添加 azure SQL Server 登录名

php - 只需要来自 PHP/MySQL 查询的逗号分隔列表

mysql - 如何使用 mysql join 执行此查询?

php - 如何单击表中的单选按钮来处理多条记录?

c# - 将参数传递到 SqlCommand 时出现问题

sql-server - RESEED 数据库上的标识列

SQL 存储过程问题 - 十进制值转换问题