Is it possible to tell SSMS not to check if a column exists in a t-sql script? - sql

I tried to google it, but din't find a way
I have a t-sql script that adds a new column to a table, then fills that columns with values depending on some other columns in the same table and finally removes some columns. This all works fine.
The problem occures when I want to run the script again. I have a if clause that checks if the missing columns exists, but SSMS still complains and displays error messaged even though the code inside the if clause if not run. The script must be able to run more then once, and I don't want the error messages to be displayed!
In code (obviously test code, don't want to dump production code here...):
create table test (
Name text,
Switch int,
ValueA int,
ValueB int)
go
insert into test values ('Name', 0, 5, 10)
if not exists (select 1 from INFORMATION_SCHEMA.COLUMNS
where COLUMN_NAME = 'ValueC' and TABLE_NAME = 'test')
begin
alter table test
add ValueC int
end
go
-- This batch rasies error when run more then once!
if exists (select 1 from INFORMATION_SCHEMA.COLUMNS
where COLUMN_NAME = 'ValueA' and TABLE_NAME = 'test')
begin
update test
set ValueC = (select case Switch
when 0 then (select (ValueA - ValueB))
when 1 then (select (ValueB - ValueA))
end)
end
go
if exists (select 1 from INFORMATION_SCHEMA.COLUMNS
where COLUMN_NAME = 'ValueA' and TABLE_NAME = 'test')
begin
alter table test drop column ValueA
end
go
select * from test
--Name 0 10 -5
Here is the error message:
Msg 207, Level 16, State 1, Line 6
Invalid column name 'ValueA'.
Msg 207, Level 16, State 1, Line 7
Invalid column name 'ValueA'.
Cheers
--Jocke

Yes it is possible without dynamic SQL but with a bit of a kludgey workaround. I would just use EXEC for this.
The behaviour in SQL 2000 is explained here
Erland Sommarskog mentions "once all tables in a query exist, SQL Server performs full checks on the query."
So by adding a no-op reference in the query to a table that doesn't exist compilation can be deferred. With this adjustment the script below can be run multiple times without getting the error.
insert into test values ('Name', 0, 5, 10)
if not exists (select 1 from INFORMATION_SCHEMA.COLUMNS
where COLUMN_NAME = 'ValueC' and TABLE_NAME = 'test')
begin
alter table test
add ValueC int
end
go
create table #dummy
(i int)
-- This batch raised error when run more then once!
if exists (select 1 from INFORMATION_SCHEMA.COLUMNS
where COLUMN_NAME = 'ValueA' and TABLE_NAME = 'test')
begin
update test
set ValueC = (select case Switch
when 0 then (select (ValueA - ValueB))
when 1 then (select (ValueB - ValueA))
end) where not exists(select * from #dummy)
end
drop table #dummy
go
if exists (select 1 from INFORMATION_SCHEMA.COLUMNS
where COLUMN_NAME = 'ValueA' and TABLE_NAME = 'test')
begin
alter table test drop column ValueA
end
go
select * from test
--Name 0 10 -5

why don't you jsut use a temp table or variable table, add the last column to the declaration, and then you wouldn't have this problem?

I had this exact problem and the only thing that worked for me was to save the script. Close it. Then open it again in and run it in the query window.
Also, it looks like you have the proper GOs, but I found that if I was missing the GO after the check to add the column then not even re-opening the script worked.

Bit late to the party but I ran into this same scenario when trying to do conditional checks based on what version of SQL Server. I took the EXEC route mentioned above. In the below example as inline T-SQL, the SELECT against sys.tables would result in an invalid column name if ran on an earlier version of SQL Server that didn't have the column available.
To work around it, I put the SQL inside a variable and EXEC() it as part of a INSERT INTO to populate a table variable.
DECLARE #Status TABLE (
Result bit
)
DECLARE #Result bit
IF #SQLVer >= 11
SET #SQL='SELECT 1 FROM sys.tables WHERE object_id=' + CONVERT(varchar,#CurrTableObjID) + ' AND is_filetable=1'
DELETE FROM #Status
INSERT INTO #Status
EXEC (#SQL)
SELECT #Result=Result FROM #Status
IF IsNULL(#Result,0) = 1
BEGIN
PRINT 'Table ' + #CurrSchemaName + '.' + #CurrTableName + ' is a filetable'
SET #BadTables=1
END

Related

Update a Column that has been added in the same script

I have a deployment script that needs to add a column, and then populate it with some data. I check if the column exists - if it doesn't I add it, and attempt to change the value.
IF NOT EXISTS (SELECT * FROM sys.columns WHERE object_id = OBJECT_ID(N'[dbo].[MyTable]') AND name = 'MyColumn')
BEGIN
ALTER TABLE [dbo].MyTable
ADD MyColumn INT NULL
...
UPDATE MyTable SET MyColumn = MyValue
END
However, the script fails (on pre-compile?) as it says that MyColumn doesn't exist.
The only way I can think of fixing this, is to change the UPDATE statement to dynamic SQL, and EXEC it that way.
Is there a better way to do this?
This is tricky because of the compilation. One solution is dynamic SQL:
exec sp_executesql 'UPDATE MyTable SET MyColumn = MyValue';
If you take this path, then you should pass in the value as a parameter.
you should put your update statement out side of the IF NOT EXISTS condition.
Reason : If you have column already present in your table, then it will exit the condition and execute the update statement, else it will add the column and then perform the update. have a look at below code:
IF NOT EXISTS (SELECT * FROM sys.columns WHERE object_id =
OBJECT_ID(N'[dbo].[MyTable]') AND name = 'MyColumn')
BEGIN
ALTER TABLE [dbo].MyTable
ADD MyColumn INT NULL
END
GO
UPDATE MyTable SET MyColumn = 1
GO

Not able to update a table script with a columnstore index (sql)

When i try to update this statement getting error:
QUERY:
IF EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[TBL]') AND name = N'INDX NAME')
BEGIN
--DROP INDEX [INDX] ON [dbo].[TBL]
ALTER INDEX [INDEX] ON [dbo].[TBL] DISABLE
END
UPDATE dbo.TBL
SET Organization_Id= CASE Org_Id WHEN #O_hf THEN #HF WHEN #O_hg THEN #HG ELSE Orga_ID END
where Org_Id in (#O_hf,#O_hg)
IF EXISTS (SELECT * FROM sys.change_tracking_tables where object_id=OBJECT_ID(N'[dbo].[TBL]'))
ALTER TABLE dbo.TBL
DISABLE CHANGE_TRACKING;
IF EXISTS (SELECT * FROM sys.indexes WHERE object_id = OBJECT_ID(N'[dbo].[TBL]') AND name = N'INDX NAME')
BEGIN
ALTER INDEX [INDEX] ON [dbo].[TBL] REBUILD
END
It will throw following error
Msg 35330, Level 15, State 1, Line 2 UPDATE statement failed because
data cannot be updated in a table with a columnstore index. Consider
disabling the columnstore index before issuing the UPDATE statement,
then rebuilding the columnstore index after UPDATE is complete.
you will simply need to add a OPTION (RECOMPILE) clause to your statement, like this:
UPDATE dbo.TBL
SET Organization_Id= CASE Org_Id WHEN #O_hf THEN #HF WHEN #O_hg THEN #HG ELSE Orga_ID END
where Org_Id in (#O_hf,#O_hg)
OPTION (RECOMPILE);
This happens because when script is analysed before execution your table still has Nonclustered Columnstore, preventing any data manipulation.
I have more details on this in my blog post: http://www.nikoport.com/2016/03/07/columnstore-indexes-part-79-loading-data-into-non-updatable-nonclustered-columnstore/
Best regards,
Niko Neugebauer
If you are getting error immidiately after running the script, it means this happens because of SQL interpretation (compile error).
If so, You need to use dynamic SQL for updating, something like below (you may need to use some cast if you get type mismatch error)
declare #txt varchar(max) =
'UPDATE dbo.TBL ' +
'SET Organization_Id= CASE Org_Id WHEN #O_hf THEN #HF WHEN #O_hg THEN #HG ELSE ' +
'Orga_ID ' +
'END ' +
'where Org_Id in (#O_hf,#O_hg) '
exec (#txt)

Database Upgrade - Invalid column name

I am trying to migrate data during a database upgrade and I don't understand why scenario 1 is working but scenario 2 is throwing an exception:
Scenario 1
Migrate data from ObsoleteTable into NewTable
Works if ObsoleteTable and NewTable both exists
No error if ObsoleteTable has been removed already
Query Scenario 1:
-- NewTable already created
IF EXISTS(select * from sys.tables where name = 'NewTable')
AND EXISTS(select * from sys.tables where name = 'ObsoleteTable')
BEGIN
UPDATE newTable
SET newTable.Name = obsoleteTable.Name
FROM dbo.NewTable newTable
INNER JOIN dbo.ObsoleteTable obsoleteTable
ON obsoleteTable.Id = newTable.Id
END
-- ObsoleteTable will be removed after this step
Scenario 2
Migrate from ObsoleteColumn to NewColumn
Works if ObsoleteColumn and NewColumn both exists
Gives error!!! if ObsoleteColumn is removed already
Query Scenario 2:
-- NewColumn has been created
IF EXISTS(select * from sys.columns where object_id = object_id('MyTable') AND name = 'ObsoleteColumn')
AND EXISTS(select * from sys.columns where object_id = object_id('MyTable') AND name = 'NewColumn')
BEGIN
UPDATE MyTable
SET NewColumn = ObsoleteColumn
END
-- Obsolete Column will be removed after this step
Msg 207, Level 16, State 1, Line 4
Invalid column name 'ObsoleteColumn'.
Both scenarios are basically the same right? Only do the migration to the new structure if the obsolete Table/Column exists. Otherwise ignore.
If I execute scenario one while ObsoleteTable is already removed. it won't fail. So why does scenario 2 fail?
It is a classic error, the column ObsoleteColumn does not exists. Which means the query will fail even when if the programflow never hits that row
This will also fail:
CREATE TABLE #xxx(a int)
IF 1 = 2
SELECT xyz FROM #xxx
To avoid it, you can use EXECUTE:
IF 1 = 2
EXEC('UPDATE MyTable SET NewColumn = ObsoleteColumn')

Can't assign value to column that was just created in table

For some reason when I run this, it says Invalid column name 'col3'.:
IF NOT EXISTS (SELECT name FROM SYS.COLUMNS WHERE name = 'col3' AND object_id IN (SELECT object_id
FROM SYS.TABLES WHERE name = 'table1')) BEGIN
ALTER TABLE table1 ADD col3 int
UPDATE table1 SET col3=col1+col2
END
But if I alter the table first and after the END of the IF I try to update the value of col3 like this, it just works:
IF NOT EXISTS (SELECT name FROM SYS.COLUMNS WHERE name = 'col3' AND object_id IN (SELECT object_id
FROM SYS.TABLES WHERE name = 'table1')) BEGIN
ALTER TABLE table1 ADD col3 int
END
UPDATE table1 SET col3=col1+col2
Why can't I update it when I create it?
This is a parse-time error - SQL Server is trying to validate the entire batch as a single, atomic operation. It doesn't see that you are going to add a column with that name, it just knows that there isn't a column with that name now - it evaluates this independently from all of the other statements in the batch. For the same reason you can't do this:
IF 1 = 1
CREATE TABLE #t(i INT);
ELSE
CREATE TABLE #t(y INT);
Obviously you and I know that only one of those branches will ever execute, but the error message you get from SQL Server (there is already an object named #t) hints that it doesn't understand branching or sequencing.
Two ways to circumvent this:
Issue the two commands in separate batches. If you are using Management Studio, simply put a GO between the ALTER and the UPDATE. This will force Management Studio to evaluate the batches in dependency order. Or even more simply - highlight the ALTER, and run that, then highlight the UPDATE, and run that.
Execute the update using dynamic SQL.
IF NOT EXISTS (blah blah)
BEGIN
ALTER TABLE dbo.table1 ADD col3 INT;
END
EXEC dbo.sp_executesql N'UPDATE dbo.table1 SET col3 = col1 + col2;';

MS SQL IF Statement Executing IF and ELSE Blocks

All, I have the following query
IF NOT EXISTS (SELECT name
FROM sys.databases
WHERE name = N'Report')
BEGIN
DECLARE #DatabasePath NVARCHAR(1000);
SET #DatabasePath = (SELECT ResultMessage + '\'
FROM [Admin]..[Process]);
EXEC ispCREATEDB N'Report', #DatabasePath, N'10MB', N'20%'
END
ELSE
BEGIN
IF EXISTS (SELECT *
FROM Report.sys.objects
WHERE name = N'FatalErrSumm' AND type = N'U')
BEGIN
DROP TABLE [Report]..[FatalErrSumm];
CREATE TABLE [Report]..[FatalErrSumm]
(
[MDF] NVARCHAR(255) NULL,
[Error] INT NULL,
);
END
END
This checks if Report exists from a different databse; if it does not exist it creates it, if it does, it checks if table FatalErrSumm exists and if it does it drops and recreates it.
The problems is that it seems to be executing both possiblities of the IF NOT EXISTS block and giving the error
Msg 2702, Level 16, State 2, Line 24
Database 'Report' does not exist.
when the database Report does not exist. So it should never be entering the ELSE block, however it seems to be. This is very basic stuff, but I cannot for the life of me spot the error, What am I doing wrong here?
Thanks for your time.
You should bypass it using a dynamic sql
IF NOT EXISTS (SELECT name
FROM sys.databases
WHERE name = N'Report')
BEGIN
DECLARE #DatabasePath NVARCHAR(1000);
SET #DatabasePath = (SELECT ResultMessage + '\'
FROM [Admin]..[Process]);
EXEC ispCREATEDB N'Report', #DatabasePath, N'10MB', N'20%'
END
ELSE IF DB_ID('Report') IS NOT NULL
EXEC
(
'BEGIN
IF EXISTS (SELECT *
FROM Report.sys.objects
WHERE name = N''FatalErrSumm'' AND type = N''U'')
BEGIN
DROP TABLE [Report]..[FatalErrSumm];
CREATE TABLE [Report]..[FatalErrSumm]
(
[MDF] NVARCHAR(255) NULL,
[Error] INT NULL,
);
END
END'
);
I think dynamic sql is the good solution for that. because in compile time compiler checked that the database "report" is not exist in you server.
If the report is offline I think this failes, check if the database is online /attached. There is flag for this in the sys.databases table.
Also do not put your statements in the ELSE. If you enter the 'THEN' part you create the database. After that check if it is created. Than ALWAYS check for you FATALERRSUMM table and not from the IF.
pseudo code:
if (not exists database) -- watch it not exists is really NOT EXISTS not just not online
create the database
if (exists database and not online)
put online the database
if (not exists database or not online database)
throw error
if (exists table fatalerrsum)
drop table
create table