How do I get rid of an orphaned default constraint in T-SQL? - sql

I am trying to create a script in order to create a table in SQL Express 2008 R2, like so:
IF (EXISTS (SELECT * FROM sys.tables WHERE name='Table' AND [type]='U')) BEGIN
DROP TABLE Table
END
GO
CREATE TABLE Table (
col DATETIMEOFFSET CONSTRAINT DF_Table_col DEFAULT GETUTCDATE()
)
GO
I am trying to follow the practice of creating a script that can be re-executed for this release cycle of my project.
But the problem is that I forgot to drop the default constraint DF_Table_col before dropping the table "Table", which I would write as:
IF (EXISTS (SELECT * FROM sys.default_constraints WHERE name='DF_Table_col')) BEGIN
ALTER TABLE Table DROP CONSTRAINT DF_Table_col
END
GO
Now if I try to re-run the script, I get the following error:
Msg 2714, Level 16, State 5, Line 1
There is already an object named 'DF_Table_col' in the database.
Msg 1750, Level 16, State 0, Line 1
Could not create constraint. See previous errors.
How do I get rid of the orphaned constraint DF_Table_col?
I tried to bind this orphan back to a new table by the same name, like so:
exec sp_bindefault 'DF_Table_col', 'Table.col'
but it gives me the following error:
Msg 15050, Level 11, State 1, Procedure sp_bindefault, Line 108
Cannot bind default 'DF_Table_col'. The default must be created using the CREATE DEFAULT statement.

as far as DF_Table_col will be deleted if 'Table' is deleted and it belong to 'Table' there might be 'DF_Table_col'. on an other Table.
If you don't care about the name you can use
CREATE TABLE Table (
col DATETIMEOFFSET DEFAULT GETUTCDATE()
)
GO

Related

how to generate a script for the entire database

I've attempted to generate scripts. I've used both of these options:
When attempting to execute the generated script, it works on the initial database creation.
All subsequent attempts to run the generated script result in the following error:
Msg 1781, Level 16, State 1, Line 8739
Column already has a DEFAULT bound to it.
Msg 1750, Level 16, State 0, Line 8739
Could not create constraint or index. See previous errors.
This code seems to work on initial database creation but not on subsequent exections:
IF NOT EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[cfg].[DF__LookupCol__IsPri__46DD686B]') AND type = 'D')
BEGIN
ALTER TABLE [cfg].[LookupColumns] ADD DEFAULT (CONVERT([bit],(0))) FOR [IsPrimaryKey]
END
GO
I've attempted it this as well with the same result:
ALTER TABLE [cfg].[LookupColumns] ADD CONSTRAINT LookupColumnsIsPrimaryKeyConstraint DEFAULT (CONVERT([bit],(0))) FOR [IsPrimaryKey]
What am I doing wrong? What is the meaning of this error? Column already has a DEFAULT bound to it?
This is indeed because you didn't name your constraints, so the second time it attempts to drop the old name then add an additional constraint.
The first time you run the script it checks for a default constraint called DF__LookupCol__IsPri__46DD686B, which of course does not exist.
Then it runs
ALTER TABLE [cfg].[LookupColumns] ADD DEFAULT (CONVERT([bit],(0))) FOR [IsPrimaryKey]
Which creates a new constraint with a new auto-generated name. Next time you run the script, it tries to drop DF__LookupCol__IsPri__46DD686B again, which still doesn't exist, and then tries to add a new constraint, which fails.
So to be able to re-run this script, you should name all your constraints and indexes before generating the scripts.
You can find the system-named default constraints like this:
select name, object_name(object_id) table_name
from sys.default_constraints
where is_system_named = 1
select name, object_name(object_id) table_name
from sys.check_constraints
where is_system_named = 1
select name, object_name(object_id) table_name
from sys.key_constraints
where is_system_named = 1
etc
Or you can look for the __ in the names, but you would want to manually review those of course.

SQL Code Evaluation stopping a valid transaction

As part of the company I am working for at the moment I need to create some database upgrade scripts to replace some work of a previous contractor.
The code before the following block runs, creates the new ID column, and then this script looks to populate the values and then drop some columns.
IF EXISTS (
SELECT *
FROM sys.columns
WHERE object_id = OBJECT_ID(N'[Central].[Core.Report].[ReportLessonComp]')
AND name = 'Name')
and
EXISTS (
SELECT *
FROM sys.columns
WHERE object_id = OBJECT_ID(N'[Central].[Core.Report].[ReportLessonComp]')
AND name = 'Code')
BEGIN
UPDATE
[Central].[Core.Report].[ReportLessonComp]
SET
CompetencyId = rc.Id
FROM
[Central].[Core.Report].[ReportLessonComp] rlc
INNER JOIN
[Core.Lookup].ReportCompetency rc
ON
rc.Code = rlc.Code and rc.Name = rlc.Name
ALTER TABLE [Central].[Core.Report].[ReportLessonComp] DROP COLUMN CODE
ALTER TABLE [Central].[Core.Report].[ReportLessonComp] DROP COLUMN [Name]
ALTER TABLE [Central].[Core.Report].[ReportLessonComp] DROP COLUMN [Description]
END
GO
When running the if exists \ not exists checks and then select getdate() this works perfeclty fine and gives me the result I expect.
However, when I run the code block above I get error
Msg 207, Level 16, State 1, Line 23
Invalid column name 'Code'.
Msg 207, Level 16, State 1, Line 23
Invalid column name 'Name'.
This script it part of a larger upgrade script and is used in a system calle RoundHouse https://github.com/chucknorris/roundhouse which is the system chosen by the company.
Prior to the above if exists check,
IF (SELECT COUNT(1) FROM sys.columns
WHERE OBJECT_ID = OBJECT_ID('[Central].[Core.Report].[ReportLessonComp]')
AND Name in ('Name','Code')) = 2
which also gave the same issue. I have five tables that I need to update and this is going to stop the team from working if I cant resolve this at my next PR
What can I do in order to stop this from causing the upgrade scripts to fail?
EDIT -- The reason I am linking on varchar fields also is because the previous developer did not create relationships between tables, and was just inserting strings into tables rather than relating by ID causing the potential for unlinked \ inconsistent data.
The table edit prior to this creates the new id column, and this script is getting the value and dropping columns that are no longer needed
SQL Server will parse the whole of the statement prior to execution, so the exists check does not protect you from the update being parsed. If the column has already been dropped, that makes the statement invalid and you get a parse error. The update statement would have to be executed as dynamic SQL, sp_execute basically so that the varchar of the update is not directly parsed.
For SQL Server 2016 and above the drop column can be protected a bit more as well:
ALTER TABLE [Central].[Core.Report].[ReportLessonComp] DROP COLUMN IF EXISTS CODE

set column to 'not null' in SQL Server

I have a column in a table in a sql server database that I want to set to 'not null'. However, when I execute the query
ALTER TABLE mytable ALTER COLUMN mycolumn INT NOT NULL
I get an
ALTER TABLE ALTER COLUMN mycolumn failed because one or more objects access this column.
How can I find out what kind of object that is and how do I drop it? The database is very simple and is not supposed to make use of foreign keys, triggers etc.
You could search the sys tables to find out what is dependent on the column. This has its limitations, naturally, but by-and-large should give you some indication of what is dependent on your column:
SELECT
OBJECT_NAME(D.Object_ID) AS [Dependent]
,D.Object_ID
FROM sys.sql_dependencies D
INNER JOIN sys.Columns C
ON C.object_id = D.referenced_major_id
AND C.column_id = D.referenced_minor_id
WHERE OBJECT_NAME(C.object_id) = 'MyTable'
AND C.name = 'MyColumn'
;
Once you have the name and object_id of the dependent (it might be a function, a stored procedure, or any number of things) you can go from there.
You might have to drop constraints on the column first,before altering this table.This may be a constraint,index or any thing..below is a small demo showing the same
create table #test
(
id int,
id1 int
)
create index nci_t on #test(id1)
include(id)
alter table #test
alter column id1 varchar(10)
this is the error i got
Msg 5074, Level 16, State 1, Line 2
The index 'nci_t' is dependent on column 'id1'.
Msg 4922, Level 16, State 9, Line 2
ALTER TABLE ALTER COLUMN id1 failed because one or more objects access this column.
also please paste entire error message ,don't strip it

Trouble updating new column after adding it

Given the following SQL:
IF EXISTS (SELECT * FROM sys.columns WHERE name = 'NewFieldName' AND object_id = OBJECT_ID('dbo.MyTableName'))
RETURN
-- Add NewFieldName column to part of the Summer 2012 release cycle.
ALTER TABLE dbo.[MyTableName] ADD
[NewFieldName] SmallINT NOT NULL
CONSTRAINT DF_MyTableName_NewFieldName DEFAULT (2)
UPDATE [MyTableName] SET NewFieldName = 1 WHERE [Name] = 'FindMe' --Update one specific value
Produces the following error message:
Msg 207, Level 16, State 1, Line 10 Invalid column name
'NewFieldName'.
I'm sure I'm missing something basic, but trying to put "GO" after the alter makes the UPDATE run everytime and I don't want to do that.
How can I structure this statement so that it will check to see if the column exists and, if it doesn't add it and then set the values as stated in my UPDATE statements?
You need the statement referencing the new column to be compiled after the new column is added. One way of doing this is to run it as a child batch with EXEC.
IF NOT EXISTS (SELECT *
FROM sys.columns
WHERE name = 'NewFieldName'
AND object_id = OBJECT_ID('dbo.MyTableName'))
BEGIN
-- Add NewFieldName column to part of the Summer 2012 release cycle.
ALTER TABLE dbo.[MyTableName]
ADD [NewFieldName] SMALLINT NOT NULL
CONSTRAINT DF_MyTableName_NewFieldName DEFAULT (2)
EXEC(' UPDATE [MyTableName] SET NewFieldName = 1 WHERE [Name] = ''FindMe''')
END
The reason it worked for you originally is presumably because the table itself did not exist when the batch was compiled thus meaning that all statements in it referencing the table are subject to deferred compile.

Stored Procedure consist Add column, Update data for that column, and Select all data from that table

I've written a stored procedure as following:
CREATE PROC spSoNguoiThan
#SNT int
AS
begin
IF not exists (select column_name from INFORMATION_SCHEMA.columns where
table_name = 'NhanVien' and column_name = 'SoNguoiThan')
ALTER TABLE NhanVien ADD SoNguoiThan int
else
begin
UPDATE NhanVien
SET NhanVien.SoNguoiThan = (SELECT Count(MaNguoiThan)FROM NguoiThan
WHERE MaNV=NhanVien.MaNV
GROUP BY NhanVien.MaNV)
end
SELECT *
FROM NhanVien
WHERE SoNguoiThan>#SNT
end
GO
Then I get the error :
Server: Msg 207, Level 16, State 1, Procedure spSoNguoiThan, Line 12
Invalid column name 'SoNguoiThan'.
Server: Msg 207, Level 16, State 1, Procedure spSoNguoiThan, Line 15
Invalid column name 'SoNguoiThan'.
Who can help me?
Thanks!
When the stored proc is parsed during CREATE the column does not exist so you get an error.
Running the internal code line by line works because they are separate. The 2nd batch (UPDATE) runs because the column exists.
The only way around this would be to use dynamic SQL for the update and select so it's not parsed until EXECUTE time (not CREATE time like now).
However, this is something I really would not do: DDL and DML in the same bit of code
I ran into this same issue and found that in addition to using dynamic sql I could solve it by cross joining to a temp table that had only one row. That caused the script compiler to not try to resolve the renamed column at compile time. Below is an example of what I did to solve the issue without using dynamic SQL
select '1' as SomeText into #dummytable
update q set q.ValueTXT = convert(varchar(255), q.ValueTXTTMP) from [dbo].[SomeImportantTable] q cross join #dummytable p