我试图理解这种奇怪的行为。
- 这批选择1,然后失败,因为表不存在。这就说得通了。
SELECT 1;
SELECT 1
FROM NonexistentTable;
- 如果我正在尝试编写万无一失的脚本,我或许可以先检查表是否存在。这会选择 1 并在不引发任何错误的情况下完成。到目前为止,一切都很好。
SELECT 1;
IF EXISTS (
SELECT 1
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME = 'NonExistentTable'
)
BEGIN
SELECT 1
FROM NonExistentTable;
END
- 这批选择 1,然后失败,因为过程不存在。这是合理的。
SELECT 1;
EXECUTE sp_nonexisting_procedure;
- 该批处理拒绝直接执行,这与之前观察到的情况不一致。为什么?
SELECT 1;
SELECT NonexistentColumn
FROM sys.databases;
- 在脚本中更令人不安,我在其中明确尝试确定该列的存在。让我们为实验创建表:
CREATE TABLE ExistingTable(Id INT, [Name] NVARCHAR(255));
GO
然后运行这个↓↓↓↓。这完全失败了,甚至没有选择 1 。
SELECT 1;
IF EXISTS (
SELECT 1
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'ExistingTable'
AND COLUMN_NAME = 'NonexistingColumn'
)
BEGIN
SELECT NonexistingColumn
FROM ExistingTable;
END
GO
为什么在发送查询后立即检查列的存在,甚至没有尝试先执行查询的有效部分?
让我更加困惑的是,只有当执行到达时才会检查表。(天真地,我希望表格更重要,因此得到更严格/更早的检查。)
作为参考,您要问的是运行时错误和编译/解析时错误之间的区别。列存在性检查在此处的编译/解析时进行。
我猜测直到运行时才检查不存在的表的一个原因是因为在 T-SQL 中指定和引用尚不存在的临时表是有效的,在某些上下文中,例如在过程中。这是因为临时表的范围仅限于创建它们的会话,因此在编译时可能不存在,但可以在运行时由另一个源创建,例如调用引用所述临时表的子过程的另一个父过程。