我在C#中创建一个临时表,从文件中填充它,然后(在C#中使用SqlCommand)运行MyStoredProcedure,该操作在将数据添加到数据库表之前先对其进行清理和验证。每次创建的登台表都略有不同,因为我只声明文件中存在的列,因此每次它都有不同的字段(取决于输入)。存储过程通过在以下语句中运行每个验证来处理它:
IF EXISTS(SELECT 1 from sys.columns where Object_ID = Object_ID(N'stagingTable') and Name = N'FirstName')
BEGIN
update stagingTable set errors = isnull(errors, '') + 'First name column is blank.' where NULLIF(FirstName, '') is null
update stagingTable set FirstName = upper(FirstName)
END
为了第一次编译和运行存储过程(即,当我在使用它时),我删除了stagingTable并使用所有可能的字段创建它,然后运行MytoredProcedure。之后,即使当我删除stagingTable并在不包含某些字段的情况下重新创建它时,MyStoredProcedure仍然可以成功运行-假定这是因为它只编译一次并且IF语句正在执行其工作。
但是,如果我尝试从C#应用程序执行此操作,则会在当前版本的stagingTable中不存在的字段上引发无效列名异常。这看起来很奇怪-只要它在更改后第一次成功编译,它就会在Management Studio中运行时忽略缺少的列名称。但是从C#来看,它似乎正在运行一个新版本并检查每个列名称,因此出现“无效的列名称错误”。
为什么它会在SSMS中正常运行,而忽略由于if导致的无效列,并从C#运行时调用此错误?我不了解的有关编译的基本知识吗?
您声明,C#和SSMS的行为不同是很奇怪的。如果服务器在解析查询时该列不存在,我实际上希望它总是失败。
我假设如果您在暂存表的“干净”版本上删除并重新创建存储过程,则在SSMS中运行该存储过程会失败?
围绕编译器的一种方法是将SQL的“动态”位作为动态sql运行。
EXEC sp_executesql N'update stagingTable set FirstName = upper(FirstName)'
虽然这只是不得已,并且没有解释为什么您会看到两种环境之间的差异。
是,这是已知的行为(相关:SQL Server reports 'Invalid column name', but the column is present and the query works through management studio)
对于EXEC('command-as-a-string')
子句中的部分,使用EXISTS
语法作为解决方法,将其中的任何单引号加倍:
IF EXISTS(SELECT 1 from sys.columns where Object_ID = Object_ID(N'stagingTable') AND [Name] = N'FirstName')
BEGIN
EXEC('
UPDATE stagingTable SET errors = ISNULL(errors, '''') + ''First name column is blank.'' WHERE NULLIF(FirstName, '''') IS NULL
UPDATE stagingTable SET FirstName = UPPER(FirstName)
')
END