0

Using SQL Server in C# is it worth saying:

IF COL_LENGTH('MyTable','MyColumn') IS NULL
 BEGIN
 ALTER TABLE MyTable ADD MyColumn INT
 END

because I could more easily put a catch around the call:

try
{
Db.ExecuteNonQuery("ALTER TABLE MyTable ADD MyColumn INT");
}
catch(Exception)
{
}

and just let if fail all the time (except when running on an old database) ... or is that naughty/slow/etc?

noelicus
  • 14,468
  • 3
  • 92
  • 111
  • IMO Option #1 is correct. Your script should do its own checking whenever that is possible. – Mike C. Mar 11 '13 at 13:15
  • IMO Option #1 is *better*, but you are still not checking for the existance of the column. You should better use a query of form `IF NOT EXISTS (SELECT * FROM sys.columns....` – Dan Puzey Mar 11 '13 at 13:20
  • @DanPuzey, there is an answer to [this question](http://stackoverflow.com/questions/133031/how-to-check-if-column-exists-in-sql-server-table) that disagrees with you 169 times...! – noelicus Mar 11 '13 at 13:25
  • @noelicus: hadn't come across that before! I do think that it's less readable, but it does seem that your syntax works. Apologies! – Dan Puzey Mar 11 '13 at 13:44

5 Answers5

9

Exceptions should be "exceptional", as in the exception to the rule. Do you plan on running this code, and 90% of the time, the column exists?

Then that is not "exceptional".

Do not use exception catching as normal logic flow.

Here are some more pointers by someone smarter than me:

http://blogs.msdn.com/b/kcwalina/archive/2005/03/16/396787.aspx

"Do not use exceptions for normal flow of control. "


Here is my typical idempotent add column tsql.

IF EXISTS (    SELECT TABLE_SCHEMA , TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'Categories' and TABLE_SCHEMA = 'dbo'    )
    BEGIN

        IF NOT EXISTS 
        (
            SELECT * 
                FROM [INFORMATION_SCHEMA].[COLUMNS] 
            WHERE   
                TABLE_NAME = 'Categories' 
                AND TABLE_SCHEMA = 'dbo'
                AND COLUMN_NAME = 'CategoryName'
        )
            BEGIN
                print 'Adding the column dbo.Categories.*CategoryName*'

                ALTER TABLE [dbo].[Categories] ADD [CategoryName] nvarchar(15) NOT NULL

            END
        ELSE
            BEGIN
                print 'The column dbo.Categories.*CategoryName* already exists.'
            END

    END
granadaCoder
  • 26,328
  • 10
  • 113
  • 146
  • Wow! Isn't that a bit verbose? – noelicus Mar 11 '13 at 14:30
  • 2
    Verbose is in the eye of the beholder. 10 years from now, someone can look at my code and know exactly what was going on. My goal is to write maintainable code, not concise code. But there is alot of grey area here. – granadaCoder Mar 11 '13 at 14:36
  • Fair enough! Thinking about it, I really mean: is there something wrong with the much smaller code quoted in my question, which I picked up from [http://stackoverflow.com/questions/133031/how-to-check-if-column-exists-in-sql-server-table](http://stackoverflow.com/questions/133031/how-to-check-if-column-exists-in-sql-server-table)? By putting different code in your answer it implies the original example isn't good enough! – noelicus Mar 11 '13 at 14:44
  • There are different ways to skin a cat. And my answer isn't (only) for you, it is for the entire developer community. And my answer is based on ANSI standards and therefore should work on all ANSI compliant databases. (Except for maybe the datatype). – granadaCoder Mar 11 '13 at 14:54
  • Sure. I realise that (it's for everybody). Good to know it's generic. Thanks for your answer. Much appreciated, along with everybody else's :) – noelicus Mar 11 '13 at 15:04
  • I just relooked at this. Your "concise" code is ok, it is just very "proprietary". I prefer to go with the "multiple RDBMS available" places to check. See this for a brief looksie : http://www.sqlusa.com/bestpractices/information_schema_views/ – granadaCoder May 21 '13 at 13:21
  • And a stackoverflow question along the same lines: http://stackoverflow.com/questions/3653637/sql-server-should-i-use-information-schema-tables-over-sys-tables – granadaCoder May 21 '13 at 13:22
7

Just don't catch the Exception exception this way: you'll never know if it is thrown:

  • because the table doesn't exists
  • because the column already exists
  • because the connection string is wrong
  • because of a network error
  • any other reason

If you want to create a column only if it doesn't already exists, do write the SQL that checks the presence of the column before adding it.

ken2k
  • 48,145
  • 10
  • 116
  • 176
2

Does speed really matter in this case? How often do you update the database?

However, are you really sure that you want to silently eat ALL exceptions? The problem can be that you do not have DDL (modify db) permissions or that the column exists but with a different column type.

jgauffin
  • 99,844
  • 45
  • 235
  • 372
  • Speed may matter if in a few years if I've got loads of columns to update, I suppose? I'm just curious, really! – noelicus Mar 11 '13 at 14:33
0

It's pretty standard to perform this type of check in your script because the script may need to be run more than once. For example, other table changes may get added to it. You don't want your script to cause errors if someone has to re-run it.

As others have noted, exceptions should be reserved for truly exceptional conditions.

But there's an additional problem in doing this in your C# code: you're mixing database maintenance stuff into what is (presumably) your application code. Your code is going to be much more readable and maintainable if you follow the principle of separation of concerns.

David
  • 2,226
  • 32
  • 39
0

Your C# code flow is just a shallow shim around the back end, where the real work happens. As such, obvious questions arise:

WHY was an exception raised?

Reasons include, but are not limited to:

  • you do not have permission to alter the table
  • you timed out trying to obtain an SCH-M lock on the table
  • the new column triggers a size-of-data operation that runs out of log space
  • a custom DDL trigger prevents execution
  • a policy-based declarative management rule prevents execution
  • the IO subsystem just collapsed and database is offline
  • the column already exists (which seems to be the only case you thought of)

You are willing to silently swallow and silence the exception because you can only think at one possible reason to fail. When dealing with databases you are always in need of serious logging, otherwise you're just shooting yourself in the foot.

Is the exception free?

There is a long debate whether SEH is cheap or not, but this is completely beyond the point when talking about DDL in a database. You are requesting a SCH-M lock on an object and thus will block everybody until you get it. Even if your DDL throws and you handle the exception, you have already dipped the throughput to 0 (zero) while you waited for the uber lock to be granted. And you may had been waiting hours, you know...

Remus Rusanu
  • 288,378
  • 40
  • 442
  • 569