3.3 解析
创建存储过程时,SQL Server先分析代码以检查语法错误。如果代码通过语法检查,SQL Server将尝试解析它所包含的名称。解析过程会验证对象名称和列名称是否存在。如果引用的对象存在,解析过程将被完整地执行。也就是说,它还会继续检查引用的列名称是否存在。
如果对象名称存在,但其中的列不存在,解析过程将生成一个错误,而该存储过程将不会被创建。然而,如果对象根本不存在,SQL Server却可以创建存储过程,并且把解析过程推迟到调用存储过程时再执行。当然,如果在执行存储过程时引用的对象或列还不存在,执行将失败。把名称解析推迟到运行时再执行的过程称为延迟名称解析(deferred name resolution)。
下面我来演示前面描述的解析过程。运行下面的代码以确保tempdb中不存在存储过程usp_Proc1、usp_Proc2和表T1。
USE tempdb;
GO
IF OBJECT_ID('dbo.usp_Proc1') IS NOT NULL
DROP PROC dbo.usp_Proc1;
GO
IF OBJECT_ID('dbo.usp_Proc2') IS NOT NULL
DROP PROC dbo.usp_Proc2;
GO
IF OBJECT_ID('dbo.T1') IS NOT NULL
DROP TABLE dbo.T1;
运行下面的代码创建存储过程usp_Proc1,它引用表T1,而该表不存在。
CREATE PROC dbo.usp_Proc1
AS
SELECT col1 FROM dbo.T1;
GO
因为T1表不存在,解析将被推迟到运行时执行,存储过程可以顺利地被创建。如果调用该存储过程时T1不存在,运行时将失败。运行下面的代码。
EXEC dbo.usp_Proc1;
你会收到下面的错误:
Msg 208, Level 16, State 1, Procedure usp_Proc1, Line 6
Invalid object name 'dbo.T1'.
接下来创建T1表,其中包含col1列。
CREATE TABLE dbo.T1(col1 INT);
INSERT INTO dbo.T1(col1) VALUES(1);
再次调用该存储过程:
EXEC dbo.usp_Proc1;
这次可以正确执行。
然后,尝试创建存储过程usp_Proc2,引用已存在的T1表中不存在的col2列。
CREATE PROC dbo.usp_Proc2
AS
SELECT col2 FROM dbo.T1;
GO
这样,解析过程不会被推迟到运行时再执行,因为T1表已经存在。但存储过程无法被创建,你会收到下面的错误。
Msg 207, Level 16, State 1, Procedure usp_Proc2, Line 4
Invalid column name 'col2'.
完成后,运行下面的代码进行清理。
USE tempdb;
GO
IF OBJECT_ID('dbo.usp_Proc1') IS NOT NULL
DROP PROC dbo.usp_Proc1;
GO
IF OBJECT_ID('dbo.usp_Proc2') IS NOT NULL
DROP PROC dbo.usp_Proc2;
GO
IF OBJECT_ID('dbo.T1') IS NOT NULL
DROP TABLE dbo.T1;