Adding column to table and then setting value gives error - sql

I am pretty new to SQL. I am working with SQL Server 2012. I need to do the following: add a column to an existing table and fill all rows in that column with the same value. To do this, I have come up with the following based on searching online:
ALTER TABLE myTable ADD myNewColumn VARCHAR(50) NULL
UPDATE myTable SET myNewColumn = 'test'
The problem is that in SQL server, I get the following error for the second statement:
Invalid column name 'myNewColumn
So, my guess is that a new column called myNewColumn wasn't created by the first statement.

You need to perform the update in a separate batch. Otherwise SQL Server tries to parse and validate that the column exists before ever trying to run the ALTER that creates it. You get the invalid column name at parse time, not at run time.
One workaround is to use GO between the two batches:
ALTER TABLE dbo.myTable ADD myNewColumn VARCHAR(50) NULL;
GO
UPDATE dbo.myTable SET myNewColumn = 'test';
(Always use schema prefixes to reference objects and always terminate statements with semi-colons.)
However this only works in Management Studio and other certain client applications, because it is not actually part of the T-SQL language; these client tools see it as a batch separator and it tells them to submit and evaluate these two batches separately. It will NOT work in code blocks submitted to SQL Server in other ways and as a single batch, e.g. in the body of a stored procedure:
CREATE PROCEDURE dbo.foo
AS
BEGIN
SET NOCOUNT ON;
SELECT 1;
GO
SELECT 2;
END
GO
This yields the following errors, because it actually splits the stored procedure into two separate batches:
Msg 102, Level 15, State 1, Procedure foo, Line 8
Incorrect syntax near ';'.
Msg 102, Level 15, State 1, Line 11
Incorrect syntax near 'END'.
What you can do as a different workaround is force the update into its own batch by executing it in dynamic SQL.
DECLARE #sql NVARCHAR(MAX), #value VARCHAR(50) = 'test';
ALTER TABLE dbo.myTable ADD myNewColumn VARCHAR(50) NULL;
SET #sql = N'UPDATE dbo.myTable SET myNewColumn = #value;';
EXEC sp_executesql #sql, N'#value VARCHAR(50)', #value;
(Why you should use EXEC sp_executesql vs. EXEC(#sql).)
Another workaround; perform the add and the update in one step:
ALTER TABLE dbo.myTable ADD myNewColumn VARCHAR(50) DEFAULT 'test' WITH VALUES;
(You can later drop the default constraint if you don't actually want any future rows to inherit that value in circumstances that would cause that behavior.)

Place the word GO after your alter statement

Alter and update cannot be executed at the same time. You need to segregate it using a built-in stored procedure to execute the update statement as under:
ALTER TABLE myTable ADD myNewColumn VARCHAR(50) NULL
Exec sp_executesql N'UPDATE myTable SET myNewColumn = ''test'''
This should definitely solve this problem.

try something like:
ALTER TABLE myTable ADD myNewColumn VARCHAR(50) NULL DEFAULT("Something")
The problem with your code is that the column does not exist until after the query completes. So you can reference it until then.

Related

How to alter table and update column values in the same area / stored procedure?

Sorry for the poor title, I'm just not sure how to explain the issue in a single sentence.
I have this bit of code in a stored procedure:
IF COL_LENGTH('dbo.raw_customer', 'LegalName') IS NULL
BEGIN
ALTER TABLE [raw_customer]
ADD [LegalName] varchar(255)
UPDATE [raw_customer]
SET [LegalName] = [Name]
END
When it runs, I get the following error:
Invalid column name 'LegalName'
and the table is not updated.
I tried splitting into two separate commands:
IF COL_LENGTH('dbo.raw_customer', 'LegalName') IS NULL
ALTER TABLE [raw_customer]
ADD [LegalName] varchar(255)
IF COL_LENGTH('dbo.raw_customer', 'LegalName') IS NULL
UPDATE [raw_customer]
SET [LegalName] = [Name]
I get the same error.
If I do them manually separately they work, but I can't seem to get them to work together in the stored procedure. The best I can think is that the ALTER TABLE needs to be committed before the UPDATE can run, but not clear on how to do that. I can't put a GO in the middle of the stored procedure.
To accomplish what you are trying to do you need to defer the compilation of the update statement.
You can do this using exec sp_executesql, try the following:
IF COL_LENGTH('dbo.raw_customer', 'LegalName') IS NULL
BEGIN
exec sp_executesql N'ALTER TABLE [raw_customer] ADD [LegalName] varchar(255)'
exec sp_executesql N'UPDATE [raw_customer] SET [LegalName] = [Name]'
END

Creating Stored Proc to Insert into a View

I have created some new tables that I need to insert to on a semi-regular basis. Due to normalization I decided to build a view on top of the base tables to make reports more logical for myself and EU's. I got the bright idea to try to use a stored procedure to push inserts into the base tables via a different view. I can run the insert statement in SSMS successfully, but when I try to create it into a stored procedure it will run because it appears to think my insert is a function.
Here is the error:
Msg 215, Level 16, State 1, Procedure jedi.p_ForcePush, Line 12
Parameters supplied for object 'jedi.v_midichlorians' which is not a function. If the parameters are intended as a table hint, a WITH keyword is required.
Here is my script:
CREATE PROCEDURE jedi.p_ForcePush
#Field varchar(25) = NULL,
#Value varchar(250) = NULL
AS
BEGIN
SET NOCOUNT ON;
insert jedi.v_midichlorians (#field) values (#value)
END
GO
I have poured out my petition to the googles but haven't found a good solution. I have tried lots of different combo's in my syntax but nothing doing.
Any help is much appreciated! (ps-SQL 2012)
CREATE PROCEDURE jedi.p_ForcePush
#Field varchar(25) = NULL,
#Value varchar(250) = NULL
AS
BEGIN
SET NOCOUNT ON;
DECLARE #sql NVARCHAR(MAX);
SET #sql = N'INSERT INTO jedi.v_midichlorians (' + QUOTENAME(#field)
+ N') values (#value)'
EXECUTE sp_executesql #sql
, N'#Value varchar(250)'
, #Value
END
GO
When you pass #field param as a parameter sql server treats it as a string not an object name, Using QUOTENAME() wraps the passed column name in [] square brackets telling sql server explicitly that it is an object(Table, column) name.
On a side note If your View has only one underlying table on then use view to insert values other wise use the table name.
If you do have more then one underlying table in your View's definition and you want to insert data using view then you will need to create Instead of triggers.
Best option is to do all insert, update delete operation directly to tables, avoid using views and then triggers for view based on more then one underlying tables.
though you mentioned that insert stmts run fine in ssMS, can you confirm that the same insert stmt you ran in SSMS ? becaus ethere is an error in this stmt.
"insert jedi.v_midichlorians (#field)"
syntex is inccoect and column name should dnot have "#" right?
also is this view based on a single table ?

Alter statement in a Transaction

I execute the code below:
use AdventureWorks2008R2
begin transaction
BEGIN
alter table HumanResources.Department add newcolumn int
update HumanResources.Department set newcolumn=1 where departmentid=1
END
commit
The error I get is:
Invalid column name 'newcolumn'.
Can ALTER statements be included in Transactions like this? If so, how can I prevent this error?
I have researched this online e.g. here. I have not found an answer to my specific question.
Yes, you can include an ALTER in a transaction. The problem is that the parser validates the syntax for your UPDATE statement, and can't "see" that you are also performing an ALTER. One workaround is to use dynamic SQL, so that the parser doesn't inspect your syntax (and validate column names) until runtime, where the ALTER will have already happened:
BEGIN TRANSACTION;
ALTER TABLE HumanResources.Department ADD newcolumn INT;
EXEC sp_executesql N'UPDATE HumanResources.Department
SET newcolumn = 1 WHERE DepartmentID = 1;';
COMMIT TRANSACTION;
Note that indentation makes code blocks much more easily identifiable (and your BEGIN/END was superfluous).
If you check the existence of column, then it should work.
BEGIN TRANSACTION;
IF COL_LENGTH('table_name', 'newcolumn') IS NULL
BEGIN
ALTER TABLE table_name ADD newcolumn INT;
END
EXEC sp_executesql N'UPDATE table_name
SET newcolumn = 1 WHERE DepartmentID = 1;';
COMMIT TRANSACTION;
Aaron has explained everything already. Another alternative that works for ad-hoc scripts in SSMS is to insert the batch separator GO so that the script is sent as two parts to the server. This only works if it is valid to split the script in the first place (you can't split an IF body for example).

Is there a way to persist a variable across a go?

Is there a way to persist a variable across a go?
Declare #bob as varchar(50);
Set #bob = 'SweetDB';
GO
USE #bob --- see note below
GO
INSERT INTO #bob.[dbo].[ProjectVersion] ([DB_Name], [Script]) VALUES (#bob,'1.2')
See this SO question for the 'USE #bob' line.
Use a temporary table:
CREATE TABLE #variables
(
VarName VARCHAR(20) PRIMARY KEY,
Value VARCHAR(255)
)
GO
Insert into #variables Select 'Bob', 'SweetDB'
GO
Select Value From #variables Where VarName = 'Bob'
GO
DROP TABLE #variables
go
The go command is used to split code into separate batches. If that is exactly what you want to do, then you should use it, but it means that the batches are actually separate, and you can't share variables between them.
In your case the solution is simple; you can just remove the go statements, they are not needed in that code.
Side note: You can't use a variable in a use statement, it has to be the name of a database.
I prefer the this answer from this question
Global Variables with GO
Which has the added benefit of being able to do what you originally wanted to do as well.
The caveat is that you need to turn on SQLCMD mode (under Query->SQLCMD) or turn it on by default for all query windows (Tools->Options then Query Results->By Default, open new queries in SQLCMD mode)
Then you can use the following type of code (completely ripped off from that same answer by Oscar E. Fraxedas Tormo)
--Declare the variable
:setvar MYDATABASE master
--Use the variable
USE $(MYDATABASE);
SELECT * FROM [dbo].[refresh_indexes]
GO
--Use again after a GO
SELECT * from $(MYDATABASE).[dbo].[refresh_indexes];
GO
If you are using SQL Server you can setup global variables for entire scripts like:
:setvar sourceDB "lalalallalal"
and use later in script as:
$(sourceDB)
Make sure SQLCMD mode is on in Server Managment Studi, you can do that via top menu Click Query and toggle SQLCMD Mode on.
More on topic can be found here:
MS Documentation
Temp tables are retained over GO statements, so...
SELECT 'value1' as variable1, 'mydatabasename' as DbName INTO #TMP
-- get a variable from the temp table
DECLARE #dbName VARCHAR(10) = (select top 1 #TMP.DbName from #TMP)
EXEC ('USE ' + #dbName)
GO
-- get another variable from the temp table
DECLARE #value1 VARCHAR(10) = (select top 1 #TMP.variable1 from #TMP)
DROP TABLE #TMP
It's not pretty, but it works
Create your own stored procedures which save/load to a temporary table.
MyVariableSave -- Saves variable to temporary table.
MyVariableLoad -- Loads variable from temporary table.
Then you can use this:
print('Test stored procedures for load/save of variables across GO statements:')
declare #MyVariable int = 42
exec dbo.MyVariableSave #Name = 'test', #Value=#MyVariable
print(' - Set #MyVariable = ' + CAST(#MyVariable AS VARCHAR(100)))
print(' - GO statement resets all variables')
GO -- This resets all variables including #MyVariable
declare #MyVariable int
exec dbo.MyVariableLoad 'test', #MyVariable output
print(' - Get #MyVariable = ' + CAST(#MyVariable AS VARCHAR(100)))
Output:
Test stored procedures for load/save of variables across GO statements:
- Set #MyVariable = 42
- GO statement resets all variables
- Get #MyVariable = 42
You can also use these:
exec dbo.MyVariableList -- Lists all variables in the temporary table.
exec dbo.MyVariableDeleteAll -- Deletes all variables in the temporary table.
Output of exec dbo.MyVariableList:
Name Value
test 42
It turns out that being able to list all of the variables in a table is actually quite useful. So even if you do not load a variable later, its great for debugging purposes to see everything in one place.
This uses a temporary table with a ## prefix, so it's just enough to survive a GO statement. It is intended to be used within a single script.
And the stored procedures:
-- Stored procedure to save a variable to a temp table.
CREATE OR ALTER PROCEDURE MyVariableSave
#Name varchar(255),
#Value varchar(MAX)
WITH EXECUTE AS CALLER
AS
BEGIN
SET NOCOUNT ON
IF NOT EXISTS (select TOP 1 * from tempdb.sys.objects where name = '##VariableLoadSave')
BEGIN
DROP TABLE IF EXISTS ##VariableLoadSave
CREATE TABLE ##VariableLoadSave
(
Name varchar(255),
Value varchar(MAX)
)
END
UPDATE ##VariableLoadSave SET Value=#Value WHERE Name=#Name
IF ##ROWCOUNT = 0
INSERT INTO ##VariableLoadSave SELECT #Name, #Value
END
GO
-- Stored procedure to load a variable from a temp table.
CREATE OR ALTER PROCEDURE MyVariableLoad
#Name varchar(255),
#Value varchar(MAX) OUT
WITH EXECUTE AS CALLER
AS
BEGIN
IF EXISTS (select TOP 1 * from tempdb.sys.objects where name = '##VariableLoadSave')
BEGIN
IF NOT EXISTS(SELECT TOP 1 * FROM ##VariableLoadSave WHERE Name=#Name)
BEGIN
declare #ErrorMessage1 as varchar(200) = 'Error: cannot find saved variable to load: ' + #Name
raiserror(#ErrorMessage1, 20, -1) with log
END
SELECT #Value=CAST(Value AS varchar(MAX)) FROM ##VariableLoadSave
WHERE Name=#Name
END
ELSE
BEGIN
declare #ErrorMessage2 as varchar(200) = 'Error: cannot find saved variable to load: ' + #Name
raiserror(#ErrorMessage2, 20, -1) with log
END
END
GO
-- Stored procedure to list all saved variables.
CREATE OR ALTER PROCEDURE MyVariableList
WITH EXECUTE AS CALLER
AS
BEGIN
IF EXISTS (select TOP 1 * from tempdb.sys.objects where name = '##VariableLoadSave')
BEGIN
SELECT * FROM ##VariableLoadSave
ORDER BY Name
END
END
GO
-- Stored procedure to delete all saved variables.
CREATE OR ALTER PROCEDURE MyVariableDeleteAll
WITH EXECUTE AS CALLER
AS
BEGIN
DROP TABLE IF EXISTS ##VariableLoadSave
CREATE TABLE ##VariableLoadSave
(
Name varchar(255),
Value varchar(MAX)
)
END
If you just need a binary yes/no (like if a column exists) then you can use SET NOEXEC ON to disable execution of statements. SET NOEXEC ON works across GO (across batches). But remember to turn EXEC back on with SET NOEXEC OFF at the end of the script.
IF COL_LENGTH('StuffTable', 'EnableGA') IS NOT NULL
SET NOEXEC ON -- script will not do anything when column already exists
ALTER TABLE dbo.StuffTable ADD EnableGA BIT NOT NULL CONSTRAINT DF_StuffTable_EnableGA DEFAULT(0)
ALTER TABLE dbo.StuffTable SET (LOCK_ESCALATION = TABLE)
GO
UPDATE dbo.StuffTable SET EnableGA = 1 WHERE StuffUrl IS NOT NULL
GO
SET NOEXEC OFF
This compiles statements but does not execute them. So you'll still get "compile errors" if you reference schema that doesn't exist. So it works to "turn off" the script 2nd run (what I'm doing), but does not work to turn off parts of the script on 1st run, because you'll still get compile errors if referencing columns or tables that don't exist yet.
You can make use of NOEXEC follow he steps below:
Create table
#temp_procedure_version(procedure_version varchar(5),pointer varchar(20))
insert procedure versions and pointer to the version into a temp table #temp_procedure_version
--example procedure_version pointer
insert into temp_procedure_version values(1.0,'first version')
insert into temp_procedure_version values(2.0,'final version')
then retrieve the procedure version, you can use where condition as in the following statement
Select #ProcedureVersion=ProcedureVersion from #temp_procedure_version where
pointer='first version'
IF (#ProcedureVersion='1.0')
BEGIN
SET NOEXEC OFF --code execution on
END
ELSE
BEGIN
SET NOEXEC ON --code execution off
END
--insert procedure version 1.0 here
Create procedure version 1.0 as.....
SET NOEXEC OFF -- execution is ON
Select #ProcedureVersion=ProcedureVersion from #temp_procedure_version where
pointer='final version'
IF (#ProcedureVersion='2.0')
BEGIN
SET NOEXEC OFF --code execution on
END
ELSE
BEGIN
SET NOEXEC ON --code execution off
END
Create procedure version 2.0 as.....
SET NOEXEC OFF -- execution is ON
--drop the temp table
Drop table #temp_procedure_version

Alter Table and Update column in single stored procedure?

I'm am trying to create a stored procedure that adds a new column then sets the column to a value:
CREATE PROCEDURE alter_then_update
AS
ALTER TABLE table_1
ADD bundle_type NVARCHAR(10);
UPDATE table_1
SET bundle_type = 'Small'
I keep getting an error that I have an invalid column name of bundle_type. I get that the column isn't created yet. Can't I store this in a stored procedure and have the code execute line by line? Doesn't the semi-colon execute in order? I tried using GO but the query started to execute instead.
How can I put both of these statements into one procedure?
The problem is that the procedure is compiled before it is executed. I think the only way around this in a stored procedure is dynamic SQL:
CREATE PROCEDURE alter_then_update AS
BEGIN
ALTER TABLE table_1 ADD bundle_type NVARCHAR(10);
EXEC sp_executesql 'UPDATE table_1 SET bundle_type = ''Small''';
END;