Writing alter table query inside if statement - sql

I have a table Person which has a column Status. Following code exists in the sqlt file which i am trying to execute
if not exists(select 1 from sysobjects, syscolumns where sysobjects.id = syscolumns.id and sysobjects.name = 'Person' and syscolumns.name = 'Status')
begin
print "Inside the if statement"
end
go
This works fine and nothing is printed as the table and the column both exist.
If i change the file to following
if not exists(select 1 from sysobjects, syscolumns where sysobjects.id = syscolumns.id and sysobjects.name = 'Person' and syscolumns.name = 'Status')
begin
print "Inside the if statement"
alter table Person
add Status char(5) DEFAULT 'INVLD' NOT NULL
end
go
This does not work and i get following error message:
Msg 2705, Level 16, State 3
Server 'NEXUS', Line 6
Column names in each table must be unique. Column name 'Status' in table
'Person' is specified more than once.
Why does it go inside if in this code? What is the solution for this?

I know you are not looking for this answer, but here goes: Don't do that.
Reason is, a structure change is part of a general upgrade. For a given project upgrades happen more than once (like all the time), and usually involve more than one column in more than one table. Therefore, you make (or use) a tool that just modifies your structure and handle that problem separately from the business logic that is making use of a particular column.

Related

Check if column exists then alter column from the table?

I want to write sql script that should check if column exists in the table, and then remove the column if previous statement is true. The database I use is Sybase ASE, and this is the code that I tried to use:
IF EXISTS (SELECT 1 FROM syscolumns WHERE id = object_id('users') AND name = 'maiden_name')
BEGIN
ALTER TABLE security DROP maiden_name
END
The code above executed successfully first time I run it. The second time I goth the error:
Invalid column name 'maiden_name'
If column does not exist the ALTER TABLE block of code shouldn't run. Is there a way to achieve this is Sybase? Thank you.
You can use dynamic SQL:
IF EXISTS (SELECT 1 FROM syscolumns WHERE id = object_id('users') AND name = 'maiden_name')
BEGIN
EXEC('ALTER TABLE security DROP maiden_name')
END;
The problem is that the parser is trying to parse the ALTER during the compilation phase, and it gets an error if the column does not exist.

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

SQL SERVER: I would like to transfer my data to another column of the same table before droping the first one

I am having problems with some of my SQL scripts on SQL SERVER, indeed I am trying to transfer data from a column A to a column B of the same table and then drop the column B,
However my problem is that I have to check for the existence of A beforehand because the code is meant to be executed on a server where I don't have access (I work as a third party developper on a professionnal app)
Here is my code:
-- Export the data from the column name
-- Drop the column name
USE p_mynacellelocation_db_ChecklistWPF
GO
IF COL_LENGTH('model_trolley_part','name') IS NOT NULL
BEGIN
UPDATE model_trolley_part
SET name_en=[name];
ALTER TABLE model_trolley_part
DROP COLUMN [name];
END
In the case of the column name being non existent I would like not to do anything
However on execution of the code in a database where the column name is non existent SQL Server returns me:
Msg 207, Level 16, State 1, Line 12 Invalid column name 'name'.
Instead of jumping through all these hoops simply rename your original column.
https://learn.microsoft.com/en-us/sql/relational-databases/system-stored-procedures/sp-rename-transact-sql?view=sql-server-2017
exec sp_rename 'model_trolley_part.name', 'name_en', 'COLUMN'
You need to use dynamic SQL for this. The reason is that the parser will try to validate your code, even the code that won't be executed because its IF condition wouldn't be met.
The parser is smart enough to see there is no table named name, but it's not smart enough to realize that the code shouldn't get executed because of the IF, so it raises a parsing error.
Using dynamic SQL in the BEGIN..END block after the IF hides this from the parser so it will execute.
Try this:
IF EXISTS(SELECT *
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'myTableName'
AND COLUMN_NAME = 'ColumnA')
BEGIN
// your update and drop code goes here
END
you might want to check your account privileges if you are modifying table structure etc..

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.

SQL Server invalid column name

If I try to execute the following code, I get the errors
Msg 207, Level 16, State 1, Line 3 Invalid column name 'Another'. Msg
207, Level 16, State 1, Line 4 Invalid column name 'Another'.
even though the predicate for both IF statements always evaluates to false.
CREATE TABLE #Foo (Bar INT)
GO
IF (1=0)
BEGIN
SELECT Another FROM #Foo
END
GO
IF (1=0)
BEGIN
ALTER TABLE #Foo ADD Another INT
SELECT Another FROM #Foo
END
GO
DROP TABLE #Foo
This is probably over-simplified for the sake of the example; in reality what I need to do is select the values from a column, but only if the column exists. If it doesn't exist, I don't care about it. In the problem that drove me to ask this question, my predicate was along the lines of EXISTS (SELECT * FROM sys.columns WHERE object_id = #ID AND name = #Name). Is there a way to achieve this without resorting to my arch-enemy Dynamic SQL? I understand that my SQL must always be well-formed (i.e. conform to grammar) - even within a block that's never executed - but I'm flabbergasted that I'm also being forced to make it semantically correct too!
EDIT:
Though I'm not sure the code below adds much to the code above, it's a further example of the problem. In this scenario, I only want to set the value of Definitely (which definitely exists as a column) with the value from Maybe (which maybe exists as a column) if Maybe exists.
IF EXISTS (SELECT * FROM sys.columns WHERE object_id = OBJECT_ID('dbo.TableName', 'U') AND name = 'Maybe')
BEGIN
UPDATE dbo.TableName SET Definitely = Maybe
END
SQL Server doesn't execute line by line. It isn't procedural like .net or Java code. So there is no "non-executed block"
The batch is compiled in one go. At this point, the column doesn't exist but it knows the table will be. Table does not have a column called "Another". Fail.
Exactly as expected.
Now, what is the real problem you are trying to solve?
Some options:
2 tables or one table with both columns
use Stored procedures to decouple scope
not use temp tables (maybe not needed; it could be your procedural thinking...)
dynamic SQL (from Mitch's deleted answer)
Edit, after comment;
Why not hide schema changes behind a view, rather than changing all code to work with columns that may/may not be there?
You can use EXEC to handle it. It's not really dynamic SQL if the code never actually changes.
For example:
CREATE TABLE dbo.Test (definitely INT NOT NULL)
INSERT INTO dbo.Test (definitely) VALUES (1), (2), (3)
IF EXISTS (SELECT *
FROM sys.columns
WHERE object_id = OBJECT_ID('dbo.Test', 'U') AND
name = 'Maybe')
BEGIN
EXEC('UPDATE dbo.Test SET definitely = maybe')
END
SELECT * FROM dbo.Test
ALTER TABLE dbo.Test ADD maybe INT NOT NULL DEFAULT 999
IF EXISTS (SELECT *
FROM sys.columns
WHERE object_id = OBJECT_ID('dbo.Test', 'U') AND
name = 'Maybe')
BEGIN
EXEC('UPDATE dbo.Test SET definitely = maybe')
END
SELECT * FROM dbo.Test
DROP TABLE dbo.Test
You can also try Martin Smith's "Workaround" using a non-existing table to get "deferred name resolution" for columns.
I had the same issue.
We are creating a script for all changes for years and this is the first time that we have this issue.
I've tried all your answers and didn't find the issue.
In my case it was because of temporary table within the script that I'm using also within a stored procedure, although every sentence has go.
I've found that if I'm adding if exists with drop to the temporary table after the script is using the temporary table, it is working correctly.
Best regards,
Chen
Derived from the answer by #gbn.
What i did to solve the issue was to use 'GO' between the ALTER query and the query that uses the column added by ALTER. This will make the 2 queries to be run as separate batches thereby ensuring your 'Another' column is there before the SELECT query.