How to add column to if not exist and update value - sql

I have this script
ALTER TABLE [dbo].TableName ADD ColumnName BIT DEFAULT(1)
GO
UPDATE [dbo].TableName SET ColumnName = 1
I have to add condition to check is column already exist, but then there is error with go
This script is also not working
IF EXISTS(SELECT 1 FROM sys.columns
WHERE Name = N'ColumnName'
AND Object_ID = Object_ID(N'dbo.TableName'))
BEGIN
ALTER TABLE [dbo].TableName ADD ColumnName BIT DEFAULT(1)
UPDATE [dbo].TableName SET ColumnName = 1
END
There must be IF condition. How to fix that?

Your IF EXISTS should be an IF NOT EXISTS. More importantly, you need to execute the ALTER and the UPDATE separately, like so:
IF NOT EXISTS(SELECT 1 FROM sys.columns
WHERE Name = N'ColumnName'
AND Object_ID = Object_ID(N'dbo.TableName'))
BEGIN
EXEC sys.sp_executesql N'ALTER TABLE [dbo].TableName ADD ColumnName BIT DEFAULT(1)';
EXEC sys.sp_executesql N'UPDATE [dbo].TableName SET ColumnName = 1';
END

You should check with IF NOT EXISTS, you are missing the NOT.
And I expect you want to UPDATE the column outside the IF statement, because it won't do anything on a new column with that same default value.
The Go statement is used to split one request into multiple smaller requests. That is why you cannot split within an IF statement.
IF NOT EXISTS(SELECT 1 FROM sys.columns
WHERE Name = N'ColumnName'
AND Object_ID = Object_ID(N'dbo.TableName'))
BEGIN
ALTER TABLE [dbo].TableName ADD ColumnName BIT DEFAULT(1)
END
GO
UPDATE [dbo].TableName SET ColumnName = 1

Related

SQL - Add a new column with a default value to all tables in the DB

I have the following statement:
EXEC sp_MSforeachtable '
DECLARE #defaultId integer;
SET #defaultId = 13;
IF EXISTS(SELECT name FROM sys.sysobjects WHERE Name = N''?'' AND xtype = N''U'')
BEGIN
IF COL_LENGTH(''[?]'',''NewColumn'') IS NULL
BEGIN
ALTER TABLE [?] ADD NewColumn integer null
UPDATE [?]
SET NewColumn = #defaultId
END
END
'
I'm trying to add NewColumn column to all the tables in my DB that don't have it and set the default value to 13. I execute it and it says it completed successfully, but nothing has changed. What am I doing wrong here?
Note: I got a table called User hence the "[]". I don't know if this adds extra complexity to it.
I don't particulary like debugging sp_MSforeachtable code so I suggest you this method:
DECLARE #Name AS VARCHAR(128)
DECLARE C_Table CURSOR FOR
SELECT sys.objects.Name
FROM sys.objects
WHERE sys.objects.type = 'U' AND
sys.objects.Name NOT IN (
SELECT DISTINCT sys.objects.Name
FROM sys.objects INNER JOIN
sys.columns ON sys.objects.object_id = sys.columns.object_id
WHERE sys.objects.type = 'U' AND
sys.columns.Name = 'NewColumn'
)
OPEN C_Table
FETCH NEXT FROM C_Table INTO #Name
WHILE ##FETCH_STATUS = 0
BEGIN
EXEC('ALTER TABLE ' + #Name + ' ADD NewColumn INTEGER NULL')
EXEC('ALTER TABLE ' + #Name + ' ADD CONSTRAINT DF_' + #Name + '_A DEFAULT 13 FOR NewColumn')
EXEC('UPDATE ' + #Name + ' SET NewColumn = 13')
FETCH NEXT FROM C_Table INTO #Name
END
CLOSE C_Table
DEALLOCATE C_Table
This is an alternative solution using sp_MSforeachtable and your syntax; please note that:
you need to PARSENAME the name provided by sp_MSforeachtable (i.e. [yourschema].[yourtable]) because sys.objects.name contains only table name (i.e.: yourtable)
you have to use 2 step because it seems that is not possible to use GO inside a sp_MSforeachtable
you can't use variable in ALTER TABLE but you must specify a fixed DEFAULT
This is the code:
EXEC sp_MSforeachtable '
IF EXISTS(SELECT * FROM sys.sysobjects WHERE Name = PARSENAME(''?'', 1) AND xtype = N''U'')
BEGIN
IF COL_LENGTH(PARSENAME(''?'', 1), ''NewColumn'') IS NULL
BEGIN
ALTER TABLE ? ADD NewColumn INT NULL DEFAULT(13)
END
END
'
EXEC sp_MSforeachtable '
DECLARE #defaultId INT
SELECT #defaultId = 13
IF EXISTS(SELECT * FROM sys.sysobjects WHERE Name = PARSENAME(''?'', 1) AND xtype = N''U'')
BEGIN
IF NOT COL_LENGTH(''?'', ''NewColumn'') IS NULL
BEGIN
UPDATE ? SET NewColumn = #defaultId
END
END
'
Most likely that NewColumn is not recognized. try
EXEC sp_MSforeachtable '
DECLARE #defaultId integer;
SET #defaultId = 13;
IF EXISTS(SELECT name FROM sys.sysobjects WHERE Name = PARSENAME(''?'',1) AND xtype = N''U'')
BEGIN
IF COL_LENGTH(''?'',''NewColumn'') IS NULL
BEGIN
ALTER TABLE ? ADD NewColumn integer null
EXEC(CONCAT(''UPDATE ? SET NewColumn = '', #defaultId))
END
END'
You need to do this in two steps.
The update statement is evaluated before execution and will throw an error (as there is no column at this moment)
Step 1 (alter table):
EXEC sp_MSforeachtable '
IF EXISTS(SELECT * FROM sys.sysobjects WHERE Name = parsename(N''?'', 1) AND xtype = N''U'')
BEGIN
IF COL_LENGTH(''[?]'',''NewColumn'') IS NULL
BEGIN
ALTER TABLE [?] ADD NewColumn integer null
END
END
'
Step 2 (set value):
EXEC sp_MSforeachtable '
DECLARE #defaultId integer;
SET #defaultId = 13;
IF EXISTS (SELECT * FROM sys.sysobjects WHERE Name = parsename(N''?'', 1) AND xtype = N''U'')
BEGIN
IF COL_LENGTH(''[?]'',''NewColumn'') IS not NULL
BEGIN
UPDATE [?]
SET NewColumn = #defaultId
END
END
'
You mentioned a default value. You only updated the values of the new column with a fixed value without defining a default. This might be an difference although not important for this issue.

SQL: multiple statements within a BEGIN and END

I'm trying to add a new column to an existing table which will be populated by a unique Guid value. I'm trying the following code:
IF NOT EXISTS(select * from sys.columns
where Name = N'Product_GUID' and Object_ID = Object_ID(N'dbo.Product'))
BEGIN
PRINT 'Creating new GUID column in dbo.Product table'
ALTER TABLE dbo.Product
ADD Product_GUID uniqueidentifier NULL
UPDATE dbo.Product
SET Product_Guid=NEWID()
ALTER TABLE dbo.Product
ALTER COLUMN Product_Guid uniqueidentifier NOT NULL
END
This won't work becasue the second statement doesn't recognise the new column name. I can't put GO or ; at the end of each statement though, presumably because I'm in the middle of the BEGIN/END block.
What's the best way of solving this dilemma?
The statement doing the update must be compiled after the column is added.
The way this is usually done is wrapping the statements in an EXEC:
EXEC(' UPDATE dbo.Product
SET Product_Guid = NEWID()
ALTER TABLE dbo.Product
ALTER COLUMN Product_Guid uniqueidentifier NOT NULL
')
It seems like you want to set the default value and have the column be not null. You'll get the same effect if you just set the default value to NEWID()
IF NOT EXISTS(select * from sys.columns
where Name = N'Product_GUID' and Object_ID = Object_ID(N'dbo.Product'))
BEGIN
PRINT 'Creating new GUID column in dbo.Product table'
ALTER TABLE dbo.Product
ADD Product_GUID uniqueidentifier NOT NULL DEFAULT NEWID()
END
If you need to remove the constraint after, you can create the DEFAULT constraint after you define the column in the alter statement and then drop the named constraint right after. If you don't name the constraint you'll have to get the name from sys.objects and then do dynamic sql to remove it.
IF NOT EXISTS(select * from sys.columns
where Name = N'Product_GUID' and Object_ID = Object_ID(N'dbo.Product'))
BEGIN
PRINT 'Creating new GUID column in dbo.Product table'
ALTER TABLE dbo.Product
ADD Product_GUID uniqueidentifier NOT NULL,
CONSTRAINT Default_Product_GUID DEFAULT NEWID() FOR Product_GUID;
ALTER TABLE dbo.Product DROP CONSTRAINT Default_Product_GUID
END
You could just update the table afterwards and then alter it in another code block, a little something like this:
IF NOT EXISTS(select * from sys.columns
where Name = N'Product_GUID' and Object_ID = Object_ID(N'dbo.Product'))
BEGIN
PRINT 'Creating new GUID column in dbo.Product table'
ALTER TABLE dbo.Product
ADD Product_GUID uniqueidentifier NULL
END
GO
UPDATE dbo.Product
SET Product_Guid=NEWID()
Where Product_Guid is null
if ##ROWCOUNT <> 0
Begin
ALTER TABLE dbo.Product
ALTER COLUMN Product_Guid uniqueidentifier NOT NULL
End

Replace empty cells with NULL values in large number of columns

I have SQL table that has a large number of columns. For some reason, some columns have empty cells instead of NULL cells. I would like to make all empty cells in all the columns to be NULL.
I know that the way to go for a single column is:
UPDATE your_table SET column = NULL WHERE column = ''
However, I am not sure how to execute a similar logic efficiently for all columns without having to write the column names one by one.
Thanks,
Run the following query:
SELECT 'UPDATE yourtable SET ' + name + ' = NULL WHERE ' + name + ' = '''';'
FROM syscolumns
WHERE id = object_id('yourtable')
AND isnullable = 1;
The output of this query will be a chunk of SQL script like this:
UPDATE yourtable SET column1 = NULL WHERE column1 = '';
UPDATE yourtable SET column2 = NULL WHERE column2 = '';
UPDATE yourtable SET column3 = NULL WHERE column3 = '';
-- etc...
Copy and paste that SQL script into a new query and run it to update all your columns.
You could do a query on syscolumns to get a list of columns, and use the results to construct your query.
select quotename(name) + ' = nullif (' + quotename(name)+ ','''')'
from syscolumns
where id = object_id('yourtable')
Additionally, if you write your query as
update yourtable
set
yourcolumn=nullif(yourcolumn, ''),
yourcolumn2=nullif(yourcolumn2, ''),
...
then you can do it in a single query without a where clause
I actually use Robert N's answer above daily when I'm importing flat file data sets, so I put it into a stored procedure that I could pass a table name to. It just populates a temp table with the update statements, then executes each row in the table.
USE [master]
GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author: LikeableBias
-- Create date: 2016-06-27
-- Description: Finds and NULLs all blank values in table where column allows nulls
-- =============================================
CREATE PROCEDURE [dbo].[sproc_NullBlanks]
#tablename NVARCHAR(MAX)
AS
BEGIN
SET NOCOUNT ON;
--------Insert update statements to temp table for execution
DECLARE #statements TABLE (statement NVARCHAR(MAX))
INSERT INTO #statements
( statement )
SELECT ('UPDATE '+#tablename+' SET [' + name + '] = NULL WHERE ' + name + ' = '''';')
FROM syscolumns
WHERE id = OBJECT_ID(#tablename)
AND isnullable = 1;
--------Open cursor, execute statements, then close cursor
DECLARE #statement NVARCHAR(MAX)
DECLARE cur CURSOR LOCAL FOR
SELECT statement FROM #statements
OPEN cur
FETCH NEXT FROM cur INTO #statement
WHILE ##FETCH_STATUS = 0 BEGIN
EXEC sys.sp_executesql #statement
FETCH NEXT FROM cur INTO #statement
END
CLOSE cur
DEALLOCATE cur
END
GO

Why does SQL Server keep creating a DF constraint?

I'm trying to create upgrade and backout scripts in SQL. The upgrade script adds a column like so:
IF NOT EXISTS (SELECT * FROM sys.columns WHERE Name = N'ColumnName'
AND object_id = OBJECT_ID(N'[dbo].[TableName]'))
ALTER TABLE TableName
ADD ColumnName bit NOT NULL DEFAULT(0)
The backout script removes the column like so:
IF EXISTS (SELECT * FROM sys.columns WHERE Name = N'ColumnName'
AND object_id = OBJECT_ID(N'[dbo].[TableName]'))
ALTER TABLE TableName
DROP COLUMN ColumnName
However, the backout script throws this error:
Msg 5074, Level 16, State 1, Line 5
The object 'DF__TableName__ColumnName__1BF3D5BD' is dependent on column 'ColumnName'.
Msg 4922, Level 16, State 9, Line 5
ALTER TABLE DROP COLUMN ColumnName failed because one or more objects access this column.
I know how to drop the constraint, but the constraint's name changes everytime (the suffix changes). I either need SQL Server to stop creating this randomly-named constraint OR I need to be able to remove the constraint in my script using wild-card characters, since the name changes.
This is the default constraint that is added because of the DEFAULT(0) in your newly added column.
You can name this yourself so it has a known fixed name rather than relying on the auto name generation.
ALTER TABLE TableName
ADD ColumnName bit NOT NULL CONSTRAINT DF_Some_Fixed_Name DEFAULT(0)
Then to remove the column and constraint together
ALTER TABLE dbo.TableName
DROP CONSTRAINT DF_Some_Fixed_Name, COLUMN ColumnName
Run this:
declare #name as nvarchar(255);
SELECT #name = name FROM dbo.sysobjects
WHERE name LIKE 'DF__XXX__YYY__%' and type = 'D'
IF #name IS NOT NULL BEGIN
EXEC('ALTER TABLE XXX DROP CONSTRAINT ' + #name);
END
Run this if you want remove constraint:
DECLARE #tableName NVARCHAR(255) = '[INSERT]';
DECLARE #first5CharsFromColumnName NVARCHAR(255) = '[INSERT]';
DECLARE #name NVARCHAR(255);
SELECT #name = d.name FROM dbo.sysobjects d
INNER JOIN dbo.sysobjects t ON t.id = d.parent_obj
WHERE d.name LIKE '%'+#first5CharsFromColumnName+'%' AND d.type = 'D' AND t.name = #tableName
IF #name IS NOT NULL BEGIN
EXEC('ALTER TABLE '+#tableName+' DROP CONSTRAINT ' + #name);
END

Drop column and all dependent objects using data definition language

I need to remove a column from a table, but when I try to remove it:
The object 'object_name' is dependent
on column 'column_name'.
ALTER TABLE DROP COLUMN column_name failed because
one or more objects access this
column.
I can look for the dependency in the system tables and remove it manually, but I need to do a migration (using SQL DDL) so all others members of the team just do the update, run the migration and donĀ“t have to mess up up system objects.
Try this code:
Declare #TABLENAME varchar(max), #COLUMN varchar(max)
SET #TABLENAME = 'YOURTableName'
SET #COLUMN = 'YOURColumnName'
Declare #CONSTRAINT varchar(max)
set #CONSTRAINT ='ALTER TABLE '+#TABLENAME+' DROP CONSTRAINT '
set #CONSTRAINT = #CONSTRAINT + (select SYS_OBJ.name as CONSTRAINT_NAME
from sysobjects SYS_OBJ
join syscomments SYS_COM on SYS_OBJ.id = SYS_COM.id
join sysobjects SYS_OBJx on SYS_OBJ.parent_obj = SYS_OBJx.id
join sysconstraints SYS_CON on SYS_OBJ.id = SYS_CON.constid
join syscolumns SYS_COL on SYS_OBJx.id = SYS_COL.id
and SYS_CON.colid = SYS_COL.colid
where
SYS_OBJ.uid = user_id() and SYS_OBJ.xtype = 'D'
and SYS_OBJx.name=#TABLENAME and SYS_COL.name=#COLUMN)
exec(#CONSTRAINT)
and then run your regular alter table:
ALTER TABLE YOURTABLENAME
DROP COLUMN YOURCOLUMNNAME
With the first code you remove all the dependencies on that column and then you can remove it without problems.
EDIT - Removing Default Values Constraints:
The code above does not seems to remove DEFAULT_CONSTRAINTS so, in that case you must also use:
DECLARE #ConstraintName nvarchar(200)
SELECT #ConstraintName = Name FROM SYS.DEFAULT_CONSTRAINTS WHERE PARENT_OBJECT_ID = OBJECT_ID('__TableName__') AND PARENT_COLUMN_ID = (SELECT column_id FROM sys.columns WHERE NAME = N'__ColumnName__' AND object_id = OBJECT_ID(N'__TableName__'))
IF #ConstraintName IS NOT NULL
EXEC('ALTER TABLE __TableName__ DROP CONSTRAINT ' + #ConstraintName)