I have a table Members with existing data, I want to add a non-nullable bit column called 'IsOnlineUser', I want all my existing rows to be set to false. I have a set of scripts that run each time I deploy so I need a check to see if the table
The first SQL I tried was
SET #ColumnExists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'Member' AND COLUMN_NAME = 'IsOnlineUser');
IF (#ColumnExists = 0)
BEGIN
ALTER TABLE Member ADD IsOnlineUser bit NULL;
UPDATE Member SET IsOnlineUser= 0;
ALTER TABLE Member ALTER COLUMN IsOnlineUser bit NOT NULL;
END
GO
But that gives me
Invalid column name 'IsOnlineUser'
. Assumedly this is because the UPDATE fails to find the created column so I thought if I put a 'GO' between the two statements it would help so I did the following:
SET #ColumnExists = (SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'Member' AND COLUMN_NAME = 'IsOnlineUser');
IF (#ColumnExists = 0)
BEGIN
ALTER TABLE Member ADD IsOnlineUser bit NULL;
END
GO
IF (#ColumnExists = 0)
BEGIN
UPDATE Member SET IsOnlineUser= 0;
ALTER TABLE Member ALTER COLUMN IsOnlineUser bit NOT NULL;
END
GO
However this says
Must declare the scalar variable "#ColumnExists".
Assumedly this is because of the GO stopping me access the scalar variable between the two.
It seems like a fairly common use case, so I assume I am just missing something, any help would be much appreciated
You could add the column as not null with a default constraint:
alter table Member add IsOnlineUser bit not null default 0;
Optionally, you can give the constraint a specific name at the same time like so:
alter table member
add IsOnlineUser bit not null
constraint df_Member_IsOnlineUser default 0;
To simplify your if, you skip the variable and use not exists() like so:
if not exists (
select 1
from information_schema.columns
where table_name = 'Member'
and column_name = 'IsOnlineUser'
)
begin;
alter table member
add IsOnlineUser bit not null
constraint df_Member_IsOnlineUser default 0;
end;
If you just want to make the existing code work while maintaining your current logic, you can execute sql strings instead of inline code with exec or sp_executesql.
...
if (#ColumnExists = 0)
begin;
exec sp_executesql N'alter table Member add IsOnlineUser bit null';
exec sp_executesql N'update Member set IsOnlineUser= 0;';
exec sp_executesql N'alter table Member alter column IsOnlineUser bit not null;';
end;
go
You can check if column does not exist and add it if needed. Put GO and go for UPDATE and ALTER after that. It will run correctly in both the cases wherever column exist or not. No need for variables.
IF NOT EXISTS (SELECT * FROM sys.columns WHERE name = 'IsOnlineUser' AND object_id = OBJECT_ID('Member'))
ALTER TABLE Member ADD IsOnlineUser bit NULL;
GO
UPDATE Member SET IsOnlineUser= 0;
ALTER TABLE Member ALTER COLUMN IsOnlineUser bit NOT NULL;
GO
Note: I prefer the use of SQL Server's sys views rather than ANSI INFORMATION.SCHEMA, and wanted to show that alternative, but that's just me. You can keep INFORMATION.SCHEMA if you like.
Edit-PS: Variable exist only within the one batch. After you use GO, you can't use variables declared prior to.
Related
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')
I am attempting to run the following query on SQL Server 2008 in SSMS, however it constantly fails on the third query stating:
Invalid column name 'hasCodeMappingDefaults'.
BEGIN
ALTER TABLE [SIntegrationProvider]
ADD [hasCodeMappingDefaults] BIT NULL;
END
BEGIN
ALTER TABLE [SIntegrationProvider]
ADD CONSTRAINT [DF_SIntegrationProvider_hasCodeMappingDefaults] DEFAULT ((1)) FOR [hasCodeMappingDefaults];
END
BEGIN
UPDATE [SIntegrationProvider]
SET [hasCodeMappingDefaults] = 1
WHERE [provider] = 'EmployeeNavigator';
END
When each query is run individually in order they work, I just cannot figure out why I cannot run all three combined (this needs to be done as part of a much larger script being rolled out).
Actually I think I may have found the problem, doing this seems to have resolved the issue:
ALTER TABLE [SIntegrationProvider]
ADD [hasCodeMappingDefaults] BIT NULL
GO
ALTER TABLE [SIntegrationProvider]
ADD CONSTRAINT [DF_SIntegrationProvider_hasCodeMappingDefaults] DEFAULT ((1)) FOR [hasCodeMappingDefaults]
GO
-- Set the new value for ENav
IF EXISTS(SELECT COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'SIntegrationProvider'
AND COLUMN_NAME = 'hasCodeMappingDefaults')
UPDATE [SIntegrationProvider]
SET [hasCodeMappingDefaults] = 1
WHERE [provider] = 'EmployeeNavigator';
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.
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.
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.