Adding an extra column and constraint but then when updating the column it fails - sql

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';

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')

How to write update to add column with value for existing

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.

Add column to existing table and default value to another column without dynamic 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.

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.

Why is test for existing column failing in SQL?

I have this SQL change script that runs as part of my nant orchestrated DB creation or update:
SET XACT_ABORT ON
BEGIN TRANSACTION
PRINT 'Change MyColumn column to MyNewColumn column in MyTable table'
IF EXISTS (SELECT *
FROM sys.columns
WHERE Name = 'MyColumn' AND Object_id = OBJECT_ID('[dbo].[MyTable]'))
BEGIN
PRINT '-> Exists, change it'
/* NOTE THE NEXT LINE */
SET #Value = (SELECT MyColumn FROM [dbo].[MyTable])
ALTER TABLE [dbo].[MyTable]
DROP CONSTRAINT DF_MyTable_MyColumn
ALTER TABLE [dbo].[MyTable]
DROP COLUMN MyColumn
ALTER TABLE [dbo].[MyTable]
ADD MyNewColumn nvarchar(20) NULL
ALTER TABLE [dbo].[MyTable]
ADD CONSTRAINT DF_MyTable_MyNewColumn DEFAULT ('') FOR MyNewColumn
PRINT '-> Add values back into table'
SET #Dynamic_Sql = 'UPDATE [dbo].[MyTable] SET MyNewColumn = ''' + #Value + ''''
EXEC(#Dynamic_Sql)
PRINT '-> Alter to NOT NULL'
ALTER TABLE [dbo].[MyTable]
ALTER COLUMN MyNewColumn nvarchar(20) NOT NULL
END
ELSE
BEGIN
PRINT '-> Does not exist, skip it'
END
I have already ran this update script before and made the changes to the DB (so MyColumn no longer exists). But now I have a new script that comes after this one, but my "build" fails on this line of this script with:
Msg 207, Level 16, State 1, Line 15
Invalid column name 'MyColumn'
where Line 15 is the FROM sys.columns line. But this is actually complaining about the line I have within the IF statement, where I have put in the NOTE comment. Why would this be the behaviour? Of course the column name will be invalid if it no longer exists.
Do you include the GO batch separator after you create all of your columns? If not, the columns won't be created by the time your first query runs, because the query parser parses it all at the same time -- at parse time, the column really doesn't exist.
By adding the GO batch separator, you force it to parse the portions of the query which use your newly created columns after the columns are actually created.
The problem (as Dave Markle alludes to, so feel free to accept his answer) is that SQL Server parses the entire section of the script. It sees that you're referring to MyColumn and that column doesn't exist, so it gives you the error. It doesn't matter that it's within an IF statement.
You can test it easily with this script:
CREATE TABLE dbo.Test (my_id int)
GO
IF (1=0)
SELECT blah FROM Test
If I can find a way to defer the parsing, I'll update this answer, but other than using dynamic SQL I don't think that you can.
EDIT:
Here's one possible solution. I didn't go through Martin's link yet, but that may be another.
CREATE FUNCTION dbo.Get_my_id ()
RETURNS INT
AS
BEGIN
DECLARE #my_id INT
SELECT #my_id = blah FROM dbo.Test
RETURN #my_id
END
GO
CREATE TABLE dbo.Test (my_id INT)
GO
DECLARE #my_id INT
IF (1=0)
SELECT #my_id = dbo.Get_my_id()
GO
BTW, if your table has more than one row in it, you realize that the value of your variable cannot be predicted, correct?