Add column to existing table and default value to another column without dynamic sql - sql

For Sql Server 2005 and 2008 I want to check if a column already exists on a given table and create it if it doesn't. This new column should have a default value of an ExistingColumn. Currently I need to use dynamic sql to fill the new column because sql server will complain of a syntax error.
Here is the current sql server code:
IF NOT EXISTS (SELECT TOP 1 1 FROM sys.columns WHERE [name] = N'NewColumn' AND OBJECT_ID = OBJECT_ID(N'ExistingTable'))
BEGIN
ALTER TABLE [dbo].[ExistingTable] ADD [NewColumn] VARCHAR(50) NULL;
exec sp_executesql N'UPDATE [dbo].[ExistingTable] SET NewColumn = ExistingColumn'
ALTER TABLE [dbo].[ExistingTable] ALTER COLUMN [NewColumn] VARCHAR(50) NOT NULL
END
GO
Is there any other way to solve this problem without resorting to dynamic sql?

Since you're creating the column regardless, you could do two separate batches.
IF NOT EXISTS (SELECT TOP 1 1 FROM sys.columns WHERE [name] = N'NewColumn' AND OBJECT_ID = OBJECT_ID(N'ExistingTable'))
BEGIN
ALTER TABLE [dbo].[ExistingTable] ADD [NewColumn] VARCHAR(50) NULL;
END
GO
IF EXISTS (SELECT TOP 1 1 FROM sys.columns WHERE [name] = N'NewColumn' AND OBJECT_ID = OBJECT_ID(N'ExistingTable'))
BEGIN
IF EXISTS (SELECT 1 FROM [dbo].[ExistingTable] WHERE NewColumn IS NULL)
BEGIN
UPDATE [dbo].[ExistingTable] SET NewColumn = ExistingColumn
ALTER TABLE [dbo].[ExistingTable] ALTER COLUMN [NewColumn] VARCHAR(50) NOT NULL
END
END
GO

SQL Server is parsing your statement before your ALTER runs, and saying "Hey, no such column." The parser doesn't understand IF and other branching and can't follow the sequence of events when you mix DDL and DML - or predict the sequence the events will take and what branching will happen at runtime.
Deferred name resolution allows you to access objects that don't exist yet, but not columns that don't exist yet on objects that do.
So, dynamic SQL seems like the way you'll have to do it.

Related

SQL Server - Check column if exists >> rename and change type

SQL Server:
Check column if exists when
If True : (Change/Modify) column_name and dataType
If False : Create
Schema name : Setup
Code:
IF EXISTS (SELECT 1 FROM sys.columns
WHERE Name = N'bitIntialBalance'
AND Object_ID = Object_ID(N'Setup.LeaveVacationsSubType'))
BEGIN
ALTER TABLE [Setup].[LeaveVacationsSubType]
ALTER COLUMN intIntialBalance INT NULL;
EXEC sp_RENAME 'Setup.LeaveVacationsSubType.bitIntialBalance', 'intIntialBalance', 'COLUMN';
--ALTER TABLE [Setup].[LeaveVacationsSubType] MODIFY [intIntialBalance] INT; not working
END
GO
IF NOT EXISTS(SELECT 1 FROM sys.columns
WHERE Name = N'intIntialBalance'
AND Object_ID = Object_ID(N'Setup.LeaveVacationsSubType'))
BEGIN
ALTER TABLE [Setup].[LeaveVacationsSubType]
ADD intIntialBalance INT NULL;
END
GO
If I guess correctly, the problem is that query plan is made for the whole script, and SQL Server also checks that it can actually perform all the operations, even if it is inside an if statement. That's why you'll get an error, even if in the reality that statement would never be executed.
One way to get around this issue is to make all those statements dynamic, something like this:
execute ('ALTER TABLE [Setup].[LeaveVacationsSubType] MODIFY [intIntialBalance] INT')

Update a Column that has been added in the same script

I have a deployment script that needs to add a column, and then populate it with some data. I check if the column exists - if it doesn't I add it, and attempt to change the value.
IF NOT EXISTS (SELECT * FROM sys.columns WHERE object_id = OBJECT_ID(N'[dbo].[MyTable]') AND name = 'MyColumn')
BEGIN
ALTER TABLE [dbo].MyTable
ADD MyColumn INT NULL
...
UPDATE MyTable SET MyColumn = MyValue
END
However, the script fails (on pre-compile?) as it says that MyColumn doesn't exist.
The only way I can think of fixing this, is to change the UPDATE statement to dynamic SQL, and EXEC it that way.
Is there a better way to do this?
This is tricky because of the compilation. One solution is dynamic SQL:
exec sp_executesql 'UPDATE MyTable SET MyColumn = MyValue';
If you take this path, then you should pass in the value as a parameter.
you should put your update statement out side of the IF NOT EXISTS condition.
Reason : If you have column already present in your table, then it will exit the condition and execute the update statement, else it will add the column and then perform the update. have a look at below code:
IF NOT EXISTS (SELECT * FROM sys.columns WHERE object_id =
OBJECT_ID(N'[dbo].[MyTable]') AND name = 'MyColumn')
BEGIN
ALTER TABLE [dbo].MyTable
ADD MyColumn INT NULL
END
GO
UPDATE MyTable SET MyColumn = 1
GO

Can't assign value to column that was just created in table

For some reason when I run this, it says Invalid column name 'col3'.:
IF NOT EXISTS (SELECT name FROM SYS.COLUMNS WHERE name = 'col3' AND object_id IN (SELECT object_id
FROM SYS.TABLES WHERE name = 'table1')) BEGIN
ALTER TABLE table1 ADD col3 int
UPDATE table1 SET col3=col1+col2
END
But if I alter the table first and after the END of the IF I try to update the value of col3 like this, it just works:
IF NOT EXISTS (SELECT name FROM SYS.COLUMNS WHERE name = 'col3' AND object_id IN (SELECT object_id
FROM SYS.TABLES WHERE name = 'table1')) BEGIN
ALTER TABLE table1 ADD col3 int
END
UPDATE table1 SET col3=col1+col2
Why can't I update it when I create it?
This is a parse-time error - SQL Server is trying to validate the entire batch as a single, atomic operation. It doesn't see that you are going to add a column with that name, it just knows that there isn't a column with that name now - it evaluates this independently from all of the other statements in the batch. For the same reason you can't do this:
IF 1 = 1
CREATE TABLE #t(i INT);
ELSE
CREATE TABLE #t(y INT);
Obviously you and I know that only one of those branches will ever execute, but the error message you get from SQL Server (there is already an object named #t) hints that it doesn't understand branching or sequencing.
Two ways to circumvent this:
Issue the two commands in separate batches. If you are using Management Studio, simply put a GO between the ALTER and the UPDATE. This will force Management Studio to evaluate the batches in dependency order. Or even more simply - highlight the ALTER, and run that, then highlight the UPDATE, and run that.
Execute the update using dynamic SQL.
IF NOT EXISTS (blah blah)
BEGIN
ALTER TABLE dbo.table1 ADD col3 INT;
END
EXEC dbo.sp_executesql N'UPDATE dbo.table1 SET col3 = col1 + col2;';

SQL Column Not Found: Added earlier in program

I'm using SQL Server 2008. I have a stored procedure with code that looks like this:
if not exists (select column_name from INFORMATION_SCHEMA.columns
where table_name = 'sample_table' and column_name = 'sample_column')
BEGIN
ALTER TABLE Sample_Table
ADD Sample_Column NVARCHAR(50)
END
Update dbo.Sample_Table
SET Sample_Column = '1'
When I execute, I get a "Column Not Found" error because the column doesn't originally exist in Sample_Table-it's added in the procedure. What's the correct way to get around this?
My workaround (below) is to wrap the update statement in an EXEC statement, so that it is forced to create the code and execute after the ALTER TABLE step. But is there a better method?
EXEC ('
Update dbo.Sample_Table
SET Sample_Column = ''1'' ')
If you do not really like your workaround, the only other option seems to be separating your DDL logic from the DML one, i.e. one SP will check/create the column (maybe other columns too, as necessary), another SP sets the value(s).
On the other hand, it looks like you are using your UPDATE statement merely as a means of providing a default value for the newly created column. If that is the case, you might consider an entirely different solution: creating a DEFAULT constraint (no need for the UPDATE statement). Here:
if not exists (select column_name from INFORMATION_SCHEMA.columns
where table_name = 'sample_table' and column_name = 'sample_column')
BEGIN
ALTER TABLE Sample_Table
ADD Sample_Column NVARCHAR(50) NOT NULL
CONSTRAINT DF_SampleTable_SampleColumn DEFAULT ('1');
ALTER TABLE Sample_Table
ALTER COLUMN Sample_Column NVARCHAR(50) NULL;
END
The second ALTER TABLE command is there only to drop the NOT NULL restriction, as it seems like you didn't mean your column to have it. But if you are fine with NOT NULL, then just scrap the second ALTER TABLE.

sql server drop default syntax error

I am trying to drop the default value on a bit column, I have set it wrong and I want to do it again correctly. However, when I write:
ALTER TABLE Person
ALTER COLUMN tsHomePref DROP DEFAULT;
I get a 'incorrect syntax near keyword default error' and I don't know why
I want to drop the column and then build it again
ALTER TABLE Person
ADD COLUMN tsHomePref bit NOT NULL DEFAULT 0;
So, why won't it let me 'drop' the default value?
Thanks R.
You would need to do
ALTER TABLE Person
DROP CONSTRAINT DF__Person__tsHomePr__05BA7BDB
It helps if you use a consistent naming convention for these so you don't have to look in the system tables to get the name first.
If you don't know the name of the constraint ( as I didn't know today when searching for a solution to the same problem ), you can use the following for Ms Sqlserver:
DECLARE #defname VARCHAR(100), #cmd VARCHAR(1000)
SET #defname =
(SELECT name FROM sysobjects so
JOIN sysconstraints sc ON so.id = sc.constid
WHERE object_name(so.parent_obj) = 'TableName'
AND sc.colid =
(SELECT colid FROM syscolumns WHERE id = object_id('dbo.TableName') AND name = 'FieldName'))
SET #cmd = 'ALTER TABLE TableName DROP CONSTRAINT ' + #defname
EXEC(#cmd)
GO
This worked in my case. Hope it helps others as well.