ALTER PROCEDURE doesn't work within IF statement [duplicate] - sql-server

i have a situation where i want to check a certain column ( like version number) and then apply a bunch of ddl changes
trouble is i am not able to do it with in a IF BEGIN END block, since DDL statements require a GO separator between them, and TSQL wont allow that.
I am wondering if there is any way around to accomplish this

You don't need to use a full block. A conditional will execute the next statement in its entirety if you don't use a BEGIN/END -- including a single DDL statement. This is equivalent to the behavior of if in Pascal, C, etc. Of course, that means that you will have to re-check your condition over and over and over. It also means that using variables to control the script's behavior is pretty much out of the question.
[Edit: CREATE PROCEDURE doesn't work in the example below, so I changed it to something else and moved CREATE PROCEDURE for a more extended discussion below]
If ((SELECT Version FROM table WHERE... ) <= 15)
CREATE TABLE dbo.MNP (
....
)
GO
If ((SELECT Version FROM table WHERE... ) <= 15)
ALTER TABLE dbo.T1
ALTER COLUMN Field1 AS CHAR(15)
GO
...
Or something like that, depending on what your condition is.
Unfortunately, CREATE/ALTER PROCEDURE and CREATE/ALTER VIEW have special requirements that make it much harder to work with. They are pretty much required to be the only thing in a statement, so you can't combine them with IF at all.
For many scenarios, when you want to "upgrade" your objects, you can work it as a conditional drop followed by a create:
IF(EXISTS(SELECT * FROM sys.objects WHERE type='p' AND object_id = OBJECT_ID('dbo.abc')))
DROP PROCEDURE dbo.abc
GO
CREATE PROCEDURE dbo.abc
AS
...
GO
If you do really need conditional logic to decide what to do, then the only way I know of is to use EXECUTE to run the DDL statements as a string.
If ((SELECT Version FROM table WHERE... ) <= 15)
EXECUTE 'CREATE PROC dbo.abc
AS
....
')
But this is very painful. You have to escape any quotes in the body of the procedure and it's really hard to read.
Depending on the changes that you need to apply, you can see all this can get very ugly fast. The above doesn't even include error checking, which is a royal pain all on its own. This is why hordes of toolmakers make a living by figuring out ways to automate the creation of deployment scripts.
Sorry; there is no easy "right" way that works for everything. This is just something that TSQL supports very poorly. Still, the above should be a good start.

GO is recognised by client tools, not by the server.
You can have CREATEs in your stored procedures or ad-hoc queries with no GO's.

Multiple "IF" statements? You can test then for the success of subsequent DDL statements
Dynamic SQL? EXEC ('ALTER TABLE foo WITH CHECK ADD CONSTRAINT ...')?
As mentioned, GO is a client only batch separator to break down a single SQL text block into batches that are submitted to the SQL Server.

Related

What is "dummy" in CREATE PROCEDURE statement

I'm doing investigation of code repo and find one thing that make me confused. SQL Server stored procedures are contained in a repo as a set of queries with following structure:
IF OBJECT_ID(N'[dbo].[sp_ProcTitle]', N'P') IS NULL
BEGIN
EXEC dbo.sp_executeSQL N'CREATE PROCEDURE [dbo].[sp_ProcTitle] AS dummy:;';
END
ALTER PROCEDURE dbo.sp_ProcTitle
#ParamOne int,
#ParamTwo date,
#ParamThree int
AS
SET NOCOUNT ON
-- some procedure body
END
Never before I saw AS dummy:; and now I'm a little confused, I can't find any good explanation what is it and how it works. Could anybody tell me what does it mean this statement? How it works? What is the reason to have it? Any thought would be good to hear. Or, please, advise me some link where I can find good explanation.
This is simply a label, such that could be used in a GOTO statement.
The word "dummy" is unimportant. It's simply trying to create the stored procedure if it doesn't exist, with a minimal amount of text. The content is then filled in with the ALTER.
Conceivably, the dummy text could later be searched for to see if any procedures were created and didn't have their content filled in, to check against failed deployments, etc.
Why do this? Well, it preserve the creation time of the stored procedure in metadata (which can be useful in administration or tracking down problems), and is compatible with versions of SQL Server that lack the CREATE OR ALTER... support.
This might make a little more sense if we add a little formatting to the CREATE:
CREATE PROCEDURE [dbo].[sp_ProcTitle]
AS
dummy:
This is, effectively, an empty procedure with a label called dummy. The user appears to be using this to ensure that the procedure exists first, and the ALTERing it. In older versions of SQL Server, such methods were needed because it didn't support CREATE OR ALTER syntax. As such, if you tried to ALTER a procedure that didn't exist the statement failed, and likewise if you try to CREATE a procedure that already exists it fails.
If you are on a recent version of SQL Server, I'd suggest changing to CREATE OR ALTER and getting rid of the call to sys.sp_executesql.

Can I recreate a temp table after dropping it?

Given:
code inside a stored proc:
select bleh
into #tblTemp
from FunctionThatReturnsTable('some','params')
-- do some stuff
drop table #tblTemp
-- Error on this command:
-- 'There is already an object named '#tblTemp' in the database.'
select bleh
into #tblTemp
from FunctionThatReturnsTable('some','other params')
Problem:
I can't recreate this temp table. My work around is to use #tmpTable1, #tmpTable2, #tempTable3 etc. Is there a way I can get around this? It would be nice just use one temp table each time.
If not, what is the reason for this?
As my comment reflected, I'm going to suggest that the answer is that you use a different #temp table name for each object that you create. It's kind of like saying to the doctor, "it hurts when I do this." His likely response is going to be, "stop doing that!"
The reason this is a problem is that SQL Server's parser attempts to parse the entire batch in one shot. It can clearly see that you are trying to create the same #temp table multiple times, but ignores the DROP command in between (I can't tell you exactly why that is, as I don't have access to the source code). This is the same reason you can't do this:
IF (1=1)
CREATE TABLE #foo(i INT);
ELSE
CREATE TABLE #foo(i VARCHAR(32));
The parser sees the two identical names, but can't really follow the IF/ELSE logic.
In addition to avoiding the problems multiple identically-named #temp tables causes the parser, another benefit to using unique names is that they can be re-used if you don't explicitly drop them. This will lighten the load on tempdb in terms of metadata / locking.
I ran into this problem with deleting+inserting column. The problem is probably with the parser, that it 'recognizes' the table on first create, and cannot see it was deleted.
I'd suggest using exec sp_executesql 'create table'
This is a feature by design and is clarified by Microsoft against Microsoft Connect Bug ID 666430
Please see a case study on the same at
temporary-table-could-not-be-re-created

How to delete all tables from db? Cannot delete from sys.tables

How can I perform this query on whatever way:
delete from sys.tables where is_ms_shipped = 0
What happened is, I executed a very large query and I forgot to put USE directive on top of it, now I got a zillion tables on my master db, and don't want to delete them one by one.
UPDATE: It's a brand new database, so I don't have to care about any previous data, the final result I want to achieve is to reset the master db to factory shipping.
If this is a one-time issue, use SQL Server Management Studio to delete the tables.
If you must run a script very, very carefully use this:
EXEC sp_msforeachtable 'DROP TABLE ?'
One method I've used in the past which is pretty simple and relatively foolproof is to query the system tables / info schema (depending on exact requirements) and have it output the list of commands I want to execute as the results set. Review that, copy & paste, run - quick & easy for a one-time job and because you're still manually hitting the button on the destructive bit, it's (IMHO) harder to trash stuff by mistake.
For example:
select 'drop table ' + name + ';', * from sys.tables where is_ms_shipped = 0
No backups? :-)
One approach may be to create a Database Project in Visual Studio with an initial Database Import. Then delete the tables and synchronize the project back to the database. You can do the deletes en masse with this approach while being "buffered" with a commit phase and UI.
I am fairly certain the above approach can be used to take care of the table relationships as well (although I have not tried in the "master" space). I would also recommend using a VS DB project (or other database management tool that allows schema comparing and synchronization) to make life easier in the future as well as allowing version-able (e.g. with SCM) schema change-tracking.
Oh, and whatever is done, please create a backup first. If nothing else, it is good training :-)
Simplest and shortest way I did was this:
How to Rebuild System Databases in SQL Server 2008
The problem with all other answers here is that it doesn't work, since there are related tables and it refuses to execute.
This one, not only it works but actually is what I am looking for: "Reset to factory defaults" as stated in the question.
Also this one will delete everything, not only tables.
This code could be better but I was trying to be cautious as I wrote it. I think it is easy to follow an easy to tweak for testing before you commit to deleting your tables.
DECLARE
#Prefix VARCHAR(50),
#TableName NVARCHAR(255),
#SQLToFire NVARCHAR(350)
SET #Prefix = 'upgrade_%'
WHILE EXISTS(
SELECT
name
FROM
sys.tables
WHERE
name like #Prefix
)
BEGIN
SELECT
TOP 1 --This query only iterates if you are dropping tables
#TableName = name
FROM
sys.tables
WHERE
name like #Prefix
SET #SQLToFire = 'DROP TABLE ' + #TableName
EXEC sp_executesql #SQLToFire;
END
I did something really similar, and what I wound up doing was using the Tasks--> script database to only script drops for all the database objects of the originally intended database. Meaning the database I was supposed to run the giant script on, which I did run it on. Be sure to include IF Exists in the advanced options, then run that script against the master and BAM, deletes everything that exists in the original target database that also exists in the master, leaving the differences, which should be the original master items.
Not very elegant but as this is a one time task.
WHILE EXISTS(SELECT * FROM sys.tables where is_ms_shipped = 0)
EXEC sp_MSforeachtable 'DROP TABLE ?'
Works fine on this simple test (clearing a on the second loop after failing on the first attempt and proceeding onwards to delete b)
create table a
(
a int primary key
)
go
create table b
(
a int references a (a)
)
insert into a values (1)
insert into b values (1)

Disable TSQL script check?

lets say I have a script like that:
if(some condition)
begin
select somecolumn from sometable
end
Lets say, that "somecolumn" does not exist and the condition is not true, which means the select is NOT executed. Even though the select would not be executed, the script is not valid, Management Studio complains about the missing column "somecolumn".
Question: Can I somehow disable this sort of check so that the script is executed and since the if is not true, it will never notice that the column is missing?
Thanks :-)
Use dynamic SQL
if(some condition)
begin
exec ('select somecolumn from sometable') --or sp_executesql
end
It actually makes no sense to run this because of what SQL is. It's not executed line by line: the whole batch is parsed etc in one go and the error is generated here, before anything actually runs in the sense you mean. This is by design...
You can create a procedure that references a table that does not exist however that is the only exception to the rule. From the BOL:
Deferred name resolution can only be
used when you reference nonexistent
table objects. All other objects must
exist at the time the stored procedure
is created. For example, when you
reference an existing table in a
stored procedure you cannot list
nonexistent columns for that table.
Beyond using dynamic SQL, there is no means to reference non-existent columns in a stored procedure.

What is the use of GO in SQL Server Management Studio & Transact SQL?

SQL Server Management Studio always inserts a GO command when I create a query using the right click "Script As" menu. Why? What does GO actually do?
It is a batch terminator, you can however change it to whatever you want
Since Management Studio 2005 it seems that you can use GO with an int parameter, like:
INSERT INTO mytable DEFAULT VALUES
GO 10
The above will insert 10 rows into mytable. Generally speaking, GO will execute the related sql commands n times.
The GO command isn't a Transact-SQL statement, but a special command recognized by several MS utilities including SQL Server Management Studio code editor.
The GO command is used to group SQL commands into batches which are sent to the server together. The commands included in the batch, that is, the set of commands since the last GO command or the start of the session, must be logically consistent. For example, you can't define a variable in one batch and then use it in another since the scope of the variable is limited to the batch in which it's defined.
For more information, see http://msdn.microsoft.com/en-us/library/ms188037.aspx.
GO is not a SQL keyword.
It's a batch separator used by client tools (like SSMS) to break the entire script up into batches
Answered before several times... example 1
Just to add to the existing answers, when you are creating views you must separate these commands into batches using go, otherwise you will get the error 'CREATE VIEW' must be the only statement in the batch. So, for example, you won't be able to execute the following sql script without go
create view MyView1 as
select Id,Name from table1
go
create view MyView2 as
select Id,Name from table1
go
select * from MyView1
select * from MyView2
Go means, whatever SQL statements are written before it and after any earlier GO, will go to SQL server for processing.
Select * from employees;
GO -- GO 1
update employees set empID=21 where empCode=123;
GO -- GO 2
In the above example, statements before GO 1 will go to sql sever in a batch and then any other statements before GO 2 will go to sql server in another batch. So as we see it has separated batches.
I use the GO keyword when I want a set of queries to get committed before heading on to the other queries.
One thing I can add is, when you have some variables declared before the GO command you will not be able to access those after the GO command. i.e
DECLARE #dt DateTime = GETDATE();
UPDATE MyTable SET UpdatedOn = #dt where mycondition = 'myvalue';
GO
-- Below query will raise an error saying the #dt is not declared.
UPDATE MySecondTable SET UpdatedOn = #dt where mycondition = 'myvalue'; -- Must declare the scalar variable "#dt".
GO
Update
I see, people requesting when to use the Go command, so I thought, I should add why I use the Go command in my queries.
When I have huge updates in the tables and I usually run these updates while going off from work (which means, I wouldn't be monitoring the queries), since it is convenient to come the next day and find the tables ready for other operations.
I use Go command when I need to run long operations and want to separate the queries and complete part of the transactions such as:
-- First Query
Update MyBigTable1 SET somecol1='someval1' where somecol2='someval2'
GO
-- Second Query
Update MyBigTable2 SET somecol1='someval1' where somecol2='someval2'
GO
-- Third Query
Update MyBigTable3 SET somecol1='someval1' where somecol2='someval2'
Executing above queries will individually commit the modifications without resulting in huge roll-back logs formation. Plus if something fails on third query, you know first 2 queries were properly executed and nothing would be rolled-back. So you do not need to spend more time updating/deleting the records again for the previously executed queries.
To sum it up in just one sentence, "I use the GO command as a check point as in the video games." If you fail after the check point (GO command), you do not need to start over, rather your game starts from the last check point.
Use herDatabase
GO ;
Code says to execute the instructions above the GO marker.
My default database is myDatabase, so instead of using myDatabase GO and makes current query to use herDatabase
One usage that I haven't seen listed is Error Resilience.
Since only the commands between two GOs are run at a time, that means a compile error in one command can be separated from others. Normally any compile errors in a batch cause the entire thing to not be executed.
exec do.Something
GO
sel from table
print 'here'
GO
print 'there'
In above, 'here' will not be printed because of the error in the 'sel' statement.
Now, adding a GO in the middle:
exec do.Something
GO
sel from table
GO
print 'here'
GO
print 'there'
You get an error for 'sel' as before, but 'here' does get output.
tldr; In most cases nowadays GO is mostly IMO optional. Using GO is best in LARGE transaction batches where you would have compiled many different scripts together in a large script and don't want errors where similar variables are used and so that parts of the transaction is committed to the server when desired instead of all of the script being rolled back due to an error.
LARGE TRANSACTION 1 --> Runs Successfully
GO; --> Is in the server
LARGE TRANSACTION 2 --> Runs Successfully
GO; --> Is in the server
LARGE TRANSACTION 3 --> Errors
GO; --> Without the other GO statements this would rollback Transaction 1 & 2
Not sure the best way to provide this SO wise however I do feel like what I've read so far doesn't really sum it all up and include an example that I've come across.
As stated many times before GO simply "commits" a batch of commands to the server.
I think understanding sessions also helps with understanding the necessity (or optionality) of the GO statement.
(This is where my technicality may fail but the community will point it out and we can make this answer better)
Typically developers are working in a single session and typically just executing simple statements to the database. In this scenario GO is optional and really...all one would do is throw it at the end of their statements.
Where it becomes more helpful is probably an option given by Jamshaid K. where you would have many large transactions that you would want committed in turn instead of all transactions being rolled back when one fails.
The other scenario where this also becomes helpful (which is the only other spot I've experienced it) is where many small transactions are compiled into one large script. For example
Dev 1 makes script 1
Dev 2 makes script 2
Dev 1 makes script 3
In order to deploy them a python script is written to combine the scripts so Script Master = script1 + script 2 + script 3.
GO statements would be required in the 3 scripts otherwise there could be errors where the scripts use conflicting variables or if script 3 fails the transactions from scripts 1 and 2 would be rolled back.
Now this process is probably archaic given current CI/CD solutions out there now but that would probably be another scenario where I could see GO being helpful/expected.
GO means asking SQL repeat this whatever number you add next to it. Just like saying in English; "Hey GO there 3 times.". Try below in SQL and the result will be rendering table 3 times.
SELECT * FROM Table
GO 3
It is a command to separate queries. If you are doing multiple selects it doesn't make a huge difference, the main use for me for example is when I am creating scripts and you need to create stored procedures and after give access or execute them. For example:
CREATE OR ALTER PROCEDURE dbo.select1
AS
BEGIN
SET NOCOUNT ON
SELECT 1
END
EXEC dbo.select1
This one it will create the stored procedure with everything on it, including the EXEC and it would end-up in a loop.
So that GO it will say that after end create the stored proc and after execute it.
CREATE OR ALTER PROCEDURE dbo.select1
AS
BEGIN
SET NOCOUNT ON
SELECT 1
END
GO
EXEC dbo.select1

Resources