简体   繁体   中英

SQL Server stored procedure - invalid column name

I create a staging table in C#, fill it from a file, and then (in C# using SqlCommand ) run MyStoredProcedure which cleans and validates the data before adding it to the database tables.

The staging table that gets created is slightly different each time, because I only declare columns that exist in the file, so each time it has different fields depending on input. The stored procedure deals with it by running each piece of validation in a statement like this:

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 

In order to compile and run the stored procedure the first time (ie when I'm working on it), I drop stagingTable and create it with ALL the possible columns, then run MyStoredProcedure once. After that, even when I drop stagingTable and recreate it with some columns left out, MyStoredProcedure still runs successfully - assuming this is because it compiled once and the IF statements are doing their job.

However, if I try doing this from my C# application, it throws an exception

Invalid Column Name

on the columns that don't exist in the current version of stagingTable . This seems strange - it overlooks the missing column names when it runs in SQL Server Management Studio, so long as it successfully compiled the first time after it was altered.

But from C#, it seems to be running a fresh version and checking each column name, therefore giving the "Invalid column name" error.

Why would it run just fine in SSMS, ignoring the invalid columns due to the if, and call this error when run from C#? Something fundamental about the compilation that I'm not understanding?

It is odd that the behaviour is different between C# and SSMS, as you state. I would actually expect it to always fail if the column doesn't exist at the time the server is parsing the query.

I assume that if you drop and recreate the stored procedure on a "clean" version of the staging table, it fails when run in SSMS?

One way around the compiler is to run the "dynamic" bit of the SQL as dynamic sql.

EXEC sp_executesql N'update stagingTable set FirstName = upper(FirstName)'

This is only a last resort though, and doesn't explain why you would be seeing the difference between the 2 environments.

Yes, this is a known behavior. When a row source (table or view) is referenced but missing as a whole, the parser will tolerate it; when a table or view does exist but is missing a column referenced anywhere in the statement, it will abort with an error. There is no clean way to turn deferred name resolution on for just a part of a conditional statement. Some tricks do exist; it is a matter of opinion if its worth going that extra mile, or just use dynamic (literal) commands instead:

The safe way home:

Use the EXEC('command-as-a-string') syntax as a workaround, for the part inside the EXISTS clause, doubling any single quotes inside:

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 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM