set column to 'not null' in SQL Server - sql

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

Related

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

change column name in sql server without using sp_rename

I want to change my column name in sql server using a query, without using sp_rename command. Can you please tell me if anybody know it?
I tried as:
alter table table_name alter column 'column1' to 'column2'
and it gave an error message as:
Msg 102, Level 15, State 1, Line 1
Incorrect syntax near 'column1'.
It is not possible to rename a column using the ALTER TABLE statement in SQL Server. Reference.
Use sp_rename instead.
OR
You can right click on the table -> select rename -> type new name.
And example is here.
alter table table_name add column 'column2'
update table_name set column2 = column1
alter table table_name drop column column1
PSEUDOCODE, need data types, likely to turn off triggers, etc. Quite ugly, but does "get the job done".
NOT very good for large tables, of course...

How do I get rid of an orphaned default constraint in T-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

Invalid column name on sql server update after column create

Does anyone see what's wrong with this code for SQL Server?
IF NOT EXISTS(SELECT *
FROM sys.columns
WHERE Name = 'OPT_LOCK'
AND object_ID = Object_id('REP_DSGN_SEC_GRP_LNK'))
BEGIN
ALTER TABLE REP_DSGN_SEC_GRP_LNK
ADD OPT_LOCK NUMERIC(10, 0)
UPDATE REP_DSGN_SEC_GRP_LNK
SET OPT_LOCK = 0
ALTER TABLE REP_DSGN_SEC_GRP_LNK
ALTER COLUMN OPT_LOCK NUMERIC(10, 0) NOT NULL
END;
When I run this, I get:
Msg 207, Level 16, State 1, Line 3
Invalid column name 'OPT_LOCK'.
on the update command.
Thanks.
In this case you can avoid the problem by adding the column as NOT NULL and setting the values for existing rows in one statement as per my answer here.
More generally the problem is a parse/compile issue. SQL Server tries to compile all statements in the batch before executing any of the statements.
When a statement references a table that doesn't exist at all the statement is subject to deferred compilation. When the table already exists it throws an error if you reference a non existing column. The best way round this is to do the DDL in a different batch from the DML.
If a statement both references a non existing column in an existing table and a non existent table the error may or may not be thrown before compilation is deferred.
You can either submit it in separate batches (e.g. by using the batch separator GO in the client tools) or perform it in a child scope that is compiled separately by using EXEC or EXEC sp_executesql.
The first approach would require you to refactor your code as an IF ... cannot span batches.
IF NOT EXISTS(SELECT *
FROM sys.columns
WHERE Name = 'OPT_LOCK'
AND object_ID = Object_id('REP_DSGN_SEC_GRP_LNK'))
BEGIN
ALTER TABLE REP_DSGN_SEC_GRP_LNK
ADD OPT_LOCK NUMERIC(10, 0)
EXEC('UPDATE REP_DSGN_SEC_GRP_LNK SET OPT_LOCK = 0');
ALTER TABLE REP_DSGN_SEC_GRP_LNK
ALTER COLUMN OPT_LOCK NUMERIC(10, 0) NOT NULL
END;
The root cause of the error is the newly added column name is not reflected in the sys.syscolumns and sys.columns table until you restart SQL Server Management Studio.
For your information,you can replace the IF NOT EXISTS with the COL_LENGTH function. It takes two parameters,
Table Name and
Column you are searching for
If the Column is found then it returns the range of the datatype of the column Ex: Int (4 bytes), when not found then it returns a NULL.
So, you could use this as follows and also combine 3 Statements into one.
IF (SELECT COL_LENGTH('REP_DSGN_SEC_GRP_LNK','OPT_LOCK')) IS NULL
BEGIN
ALTER TABLE REP_DSGN_SEC_GRP_LNK
ADD OPT_LOCK NUMERIC(10, 0) NOT NULL DEFAULT 0
END;
Makes it simpler.

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.