Why does SQL Server keep creating a DF constraint? - sql

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

Related

Multiple IF NOT EXISTS with one wrapper?

I am wondering if there is any way to do this. We have a program that does data modeling for us (IDA), and it will generate at times hundreds of different alter/delete/update statements for us. The script works, except it does not meet the requirement of being able to be ran multiple times, which we sometimes need due to devops work. I haven't been able to find a way for the modeler to automatically add an IF NOT EXISTS to each statement, and so this means manually needing to add it to each one.
Is there a way to wrap the entire script in one IF NOT EXISTS? or handle this with some other kind of loop or flag I don't know about?
Example:
Currently we would have to do this:
IF NOT EXISTS (
SELECT * FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'USERS' AND COLUMN_NAME = 'LASTNAME'
)
BEGIN
ALTER TABLE DBO.USERS ADD LASTNAME CHAR(2) NULL
END;
GO
IF NOT EXISTS (
SELECT * FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'ASSETS' AND COLUMN_NAME = 'ASSETTYPE'
)
BEGIN
ALTER TABLE DBO.ASSETS ADD ASSETTYPE CHAR(2) NULL
END;
GO
IF NOT EXISTS (
SELECT * FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'ADDRESS' AND COLUMN_NAME = 'LINE3'
)
BEGIN
ALTER TABLE DBO.ADDRESS ADD LINE3 CHAR(2) NULL
END;
GO
Whereas I'd like to be able to not need to add the IF NOT EXISTS to every select, just something to indicate for the script to automatically check if it exists first.
Any thoughts? Thanks.
You could try:
IF COL_LENGTH('table_name','column_name') IS NULL
BEGIN
/* ALTER TABLE .... */
END
This was obtained from this forum: How to check if a column exists in a SQL Server table? (Got 958 up votes) or
Add a column to a table, if it does not already exist
You would need to load all the TableName and ColumnName combination into the #TEMP1
through which you need to loop it. Also you can change the data type of the column to a variable as well. Let me know if you need help adapting the code to your needs. You can scale that code to add DB_NAME and different datatypes. But you would need different versions of the code for update and delete
SELECT TABLENAME, COLNAME
INTO #TEMP1
FROM MYTABLE
DECLARE #TABLE_NAME VARCHAR(100)
DECLARE #COL_NAME VARCHAR(100)
DECLARE #SQL VARCHAR(MAX)
WHILE EXISTS (SELECT * FROM #TEMP1)
BEGIN
SELECT TOP 1 #TABLE_NAME = TABLENAME,
#COL_NAME = COLNAME
FROM #TEMP1
SET #SQL = '
IF NOT EXISTS (
SELECT * FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = '+#TABLE_NAME+' AND COLUMN_NAME = '+#COL_NAME+'
)
BEGIN
ALTER TABLE DBO.'+#TABLE_NAME+' ADD '+#COL_NAME+' CHAR(2) NULL
END'
EXEC #SQL
DELETE FROM #TEMP1
WHERE TABLENAME = #TABLE_NAME AND COLNAME = #COL_NAME
END

How to add column to if not exist and update value

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

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

Drop a column with a default constraint in SQL Server (IF EXISTS)

I am writing a sql script for dropping column and a default constraint. The following script works fine but i like to know if it is a right way of doing it.
Can i drop a default constraint with a column in one statement instead of using two separate ones?
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[DF_Employees_EmpID]') AND type = 'D')
BEGIN
ALTER TABLE [dbo].[Employees] DROP CONSTRAINT [DF_Employees_EmpID]
END
GO
BEGIN
ALTER TABLE [dbo].[Employees] DROP COLUMN [EmpID]
END
In SQL Server 2005 upwards you can drop both the constraint and the column in one statement.
The syntax is
ALTER TABLE [ database_name . [ schema_name ] . | schema_name . ] table_name
DROP { [ CONSTRAINT ] constraint_name | COLUMN column } [ ,...n ]
The emphasis is on [ ,...n ], indicating multiple terms.
NB! Since the terms are processed sequentially, if the column being dropped is part of the constraint being dropped, then the constraint must be the first term, followed by the column term.
In your example:
ALTER TABLE [dbo].[Employees] DROP CONSTRAINT [DF_Employees_EmpID], COLUMN [EmpID]
So your code would be:
IF EXISTS (SELECT * FROM dbo.sysobjects WHERE id = OBJECT_ID(N'[DF_Employees_EmpID]') AND type = 'D')
BEGIN
ALTER TABLE [dbo].[Employees] DROP CONSTRAINT [DF_Employees_EmpID], COLUMN [EmpID]
END
GO
In SQL Server 2016 they have introduced the IF EXISTS clause which removes the need to check for the existence of the constraint first e.g.
ALTER TABLE [dbo].[Employees] DROP CONSTRAINT IF EXISTS [DF_Employees_EmpID], COLUMN IF EXISTS [EmpID]
Here is another way to drop a column & default constraints with checking if they exist before dropping them:
-------------------------------------------------------------------------
-- Drop COLUMN
-- Name of Column: Column_EmployeeName
-- Name of Table: table_Emplyee
--------------------------------------------------------------------------
IF EXISTS (
SELECT 1
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'table_Emplyee'
AND COLUMN_NAME = 'Column_EmployeeName'
)
BEGIN
IF EXISTS ( SELECT 1
FROM sys.default_constraints
WHERE object_id = OBJECT_ID('[dbo].[DF_table_Emplyee_Column_EmployeeName]')
AND parent_object_id = OBJECT_ID('[dbo].[table_Emplyee]')
)
BEGIN
------ DROP Contraint
ALTER TABLE [dbo].[table_Emplyee] DROP CONSTRAINT [DF_table_Emplyee_Column_EmployeeName]
PRINT '[DF_table_Emplyee_Column_EmployeeName] was dropped'
END
-- ----- DROP Column -----------------------------------------------------------------
ALTER TABLE [dbo].table_Emplyee
DROP COLUMN Column_EmployeeName
PRINT 'Column Column_EmployeeName in images table was dropped'
END
--------------------------------------------------------------------------
-- ADD COLUMN Column_EmployeeName IN table_Emplyee table
--------------------------------------------------------------------------
IF NOT EXISTS (
SELECT 1
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'table_Emplyee'
AND COLUMN_NAME = 'Column_EmployeeName'
)
BEGIN
----- ADD Column & Contraint
ALTER TABLE dbo.table_Emplyee
ADD Column_EmployeeName BIT NOT NULL
CONSTRAINT [DF_table_Emplyee_Column_EmployeeName] DEFAULT (0)
PRINT 'Column [DF_table_Emplyee_Column_EmployeeName] in table_Emplyee table was Added'
PRINT 'Contraint [DF_table_Emplyee_Column_EmployeeName] was Added'
END
GO
How you have it is fine.
An alternative would be
IF OBJECT_ID('DF_Employees_EmpID', 'D') IS NULL
BEGIN
ALTER TABLE dbo.Employees
DROP COLUMN EmpID
END
ELSE
BEGIN
ALTER TABLE dbo.Employees
DROP CONSTRAINT DF_Employees_EmpID,
COLUMN EmpID
END
In the event that the constraint does exist this combines the two operations into a single statement/transaction.
Another solution:
DECLARE #TableName sysname,
#Schema sysname,
#colname sysname,
#sql VARCHAR(1000)
SELECT #Schema = 'dbo',
#TableName = 'mytable',
#colname = 'mycol'
IF COL_LENGTH(#Schema+'.'+#TableName, #colname) IS NULL
BEGIN
PRINT 'Column does not exist!'
END
ELSE
BEGIN
SET #sql = ''
SELECT #sql += N' ALTER TABLE ' + #TableName + ' DROP CONSTRAINT ' + default_constraints.name + ';'
FROM sys.all_columns
INNER JOIN sys.tables
ON all_columns.object_id = TABLES.object_id
INNER JOIN sys.schemas
ON TABLES.schema_id = schemas.schema_id
INNER JOIN sys.default_constraints
ON all_columns.default_object_id = default_constraints.object_id
WHERE schemas.name = #Schema
AND tables.name = #TableName
AND all_columns.name = #colname
SET #sql += N' ALTER TABLE ' + #TableName + ' DROP COLUMN ' + #colname + ';'
PRINT ISNULL(#sql, 'NULL')
EXECUTE(#sql)
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)