GO statement behaves differently when called in sql job - sql

Could you please explain me the behavior of GO statement.
I had given two T-SQl statements
truncate table CustomerDetails
GO
truncate table CustomerDetails_Log
GO
The table CustomerDetails does not exists, even if I truncate the table, it goes to the next statement, but displays in the Message section of SQL server that the "CustomerDetails table does not exist" and proceeds with the next truncation of CustomerDetails_Log.
If I place the same set of SQL statements in a SQL job, it fails at the first level, and does not proceed to next statement.
Can anyone please explain me this behavior, as why does GO behave differently in job and in TSQL places.
Thanks
P.S : I do understand that I have not understood the concept of GO properly, any good links would also be very helpful.

To make this work in a SQL job you can wrap each step in TRY/CATCH blocks. This has the added benefit of allowing you to handle/log any problems.
BEGIN TRY
TRUNCATE TABLE CustomerDetails
END TRY
BEGIN CATCH
-- Log error
END CATCH
GO
BEGIN TRY
TRUNCATE TABLE CustomerDetails_Log
END TRY
BEGIN CATCH
-- Log error
END CATCH
GO

Related

roll back a batch is not easy?

After two days tests on batch, roll back and try ..catch, my mind is still vague. I separate what I was doing into two step in order to clear my question.
1. roll back a batch
As online book explains, in a batch, executed statements cannot be roll back only except the batch is in a transaction and error in the batch cause the transaction is roll back.
So I put the batch into a transaction like
begin transaction
create table A ...
insert into A values...
insert into A values... (error here!)
insert into A values...
GO
rollback
This works with error output and no table was created
(1 row(s) affected)
Msg 213, Level 16, State 1, Line 5
Column name or number of supplied values does not match table definition.
Msg 208, Level 16, State 1, Line 1
Invalid object name 'A'.
However, the rollback will be executed anyway even no error in transaction. In order to deal with this case, I use TRY ...CATCH as in 2.
2. use TRY ...CATCH
BEGIN TRY
begin transaction
create table A ...
insert into A values...
insert into A values... (error here!)
insert into A values...
--GO
END TRY
BEGIN CATCH
ROLLBACK
END CATCH
This time it doesn't allow statement GO here any more. S*o Batch is whole block between BEGIN TRY AND END TRY in this case?*
In addition, the result is not as I expected. The CREATE TABLE AND first insert were still executed and didn't roll back.
I searched again. It seems I need to SET XACT_ABORT ON in order to record these executed statement as uncommitted before touch commit. What I am understanding here is right? If so, I didn't add any commit statement in this case.
By the way, test are done on SQL SERVER 2012. Thanks for any clarification!
The reason it doesn't allow the GO statement is that the try and catch must be part of the same batch as metioned in this MSDN article. It states;
"Each TRY…CATCH construct must be inside a single batch, stored
procedure, or trigger. For example, you cannot place a TRY block in
one batch and the associated CATCH block in another batch. The
following script would generate an error:"
BEGIN TRY
SELECT *
FROM sys.messages
WHERE message_id = 21;
END TRY
GO
-- The previous GO breaks the script into two batches,
-- generating syntax errors. The script runs if this GO
-- is removed.
BEGIN CATCH
SELECT ERROR_NUMBER() AS ErrorNumber;
END CATCH;
GO
For an alternative idea on how to handle this, take a look at gbns answer on the question Nested stored procedures containing TRY CATCH ROLLBACK pattern? as he discusses his pattern/template for handling transactions including the use of (and reason for) XACT_ABORT and other nifty features. I'd also suggest reading the associated links in gbns answer
Aaron Betrand's answer to the same question refers to a Erland Somarsskog's article on error handling that is very similar to gbn's answer also.
Even though the title of the original question relates to nested transactions, it is still applicable in your situation I believe.
Try using SET XACT_ABORT ON in the begining of your script.
Check out XACT_ABORT MDSN reference page here

USE DB that may not exist

I have a script that has a USE DATABASE statement.
The script runs perfectly fine if the database exists. If it doesn't exist, it fails with the message "the database doesn't exist", which makes perfect sense.
Now, I don't it to fail so I added a check to select if the DB exists on sys.databases (which I will represent here with a IF 1=2 check for the sake of simplicity), so, if the DB exists (1=1), then run the "use" statement.
To my surprise, the script kept failing. So I tried to add a TRY CATCH block. Same result.
It seems that the use statement is evaluated prior to anything else, which id quite annoying because now my script may break.
So my question is: how can I have an use statement on a script to a database that may not exist?
BEGIN TRY
IF (1=1) BEGIN --if DB exists
USE DB_THAT_MAY_NOT_EXIST
END
END TRY
BEGIN CATCH
END CATCH
I don't believe you can do what you want to do. The documentation specifies that use is executed at both compile time and execution time.
As such, use on a database that does not exist is going to create a compile time error. I am not aware of a way to bypass compile time errors.
As another answer suggests, use the database qualifier in all your names.
You can also check if a database exists, without switching to it. Here is one way:
begin try
exec('use dum');
print 'database exists'
end try
begin catch
print 'database does not exist'
end catch
How about this? May be you could check in this way.
if db_id('dbname') is not null
-- do stuff
or try this:
if not exists(select * from sys.databases where name = 'dbname')
-- do stuff
So for table:
if object_id('objectname', 'somename') is not null
or
sp_msforeachdb ‘select * from ?.sys.tables’
Reference
Off the top of my head, you could fully qualify all your references to avoid the USE statement.
I hope someone comes up with a solution that requires less PT.
After doing your check to see if the DB exists, instead of
SELECT Moo FROM MyTable
use
SELECT Moo FROM MyDB.MySchema.MyTable

Auto update with script file with transaction

I need to provide an auto update feature to my application.
I am having problem in applying the SQL updates. I have the updated SQL statement in my .sql file and what i want to achieve is that if one statment fails then entire script file must be rolled back
Ex.
create procedure [dbo].[test1]
#P1 varchar(200),
#C1 int
as
begin
Select 1
end
GO
Insert into test (name) values ('vv')
Go
alter procedure [dbo].[test2]
#P1 varchar(200),
#C1 int
as
begin
Select 1
end
GO
Now in the above example, if i get the error in third statement of "alter procedure [dbo].[test2]" then i want to rollback the first two changes also which is creating SP of "test1" and inserting data into "test" table
How should i approach this task? Any help will be much appreciated.
If you need any more info then let me know
Normally, you would want to add a BEGIN TRAN at the beginning, remove the GO statements, and then handle the ROLLBACK TRAN/COMMIT TRAN with a TRY..CATCH block.
When dealing with DML though there are often statements that have to be at the start of a batch, so you can't wrap them in a TRY..CATCH block. In that case you need to put together a system that knows how to roll itself back.
A simple system would be just to backup the database at the start and restore it if anything fails (assuming that you are the only one accessing the database the whole time). Another method would be to log each batch that runs successfully and to have corresponding rollback scripts which you can run to put everything back should a later batch fail. This obviously requires much more work (writing an undo script for every script PLUS fully testing the rollbacks) and can also be a problem if people are still accessing the database while the upgrade is happening.
EDIT:
Here's an example of a simple TRY..CATCH block with transaction handling:
BEGIN TRY
BEGIN TRANSACTION
-- All of your code here, with `RAISERROR` used for any of your own error conditions
COMMIT TRANSACTION
END TRY
BEGIN CATCH
ROLLBACK TRANSACTION
END CATCH
However, the TRY..CATCH block cannot span batches (maybe that's what I was thinking of when I said transactions couldn't), so in your case it would probably be something more like:
IF (OBJECT_ID('dbo.Error_Happened') IS NOT NULL)
DROP TABLE dbo.Error_Happened
GO
BEGIN TRANSACTION
<Some line of code>
IF (##ERROR <> 0)
CREATE TABLE dbo.Error_Happened (my_id INT)
IF (OBJECT_ID('dbo.Error_Happened') IS NOT NULL)
BEGIN
<Another line of code>
IF (##ERROR <> 0)
CREATE TABLE dbo.Error_Happened (my_id INT)
END
...
IF (OBJECT_ID('dbo.Error_Happened) IS NOT NULL)
BEGIN
ROLLBACK TRANSACTION
DROP TABLE dbo.Error_Happened
END
ELSE
COMMIT TRANSACTION
Unfortunately, because of the separate batches from the GO statements you can't use GOTO, you can't use the TRY..CATCH, and you can't persist a variable across the batches. This is why I used the very kludgy trick of creating a table to indicate an error.
A better way would be to simply have an error table and look for rows in it. Just keep in mind that your ROLLBACK will remove those rows at the end as well.

Why is CREATE TABLE command not working in a user-defined transaction?

I apologise in advance for this stupid question, but I cannot figure why CREATE TABLE is not rolled back in the code shown below. I know that CREATE DATABASE, CREATE FULLTEXT CATALOG, CREATE FULLTEXT INDEX cannot be specified witnin user-defined transaction. Please note that Tables folder within SSMS gets locked whilst executing this code.
BEGIN TRANSACTION T1
CREATE TABLE temp
(
chisla char(1)
)
SELECT count(chisla) AS Count, chisla AS My_Numbers
FROM temp
--GROUP BY chisla
ORDER BY chisla
drop table temp
COMMIT TRANSACTION T1
GO
You didn't tell it to rollback. JNK shows how the try Catch shoudl be done to rollback a transacation in case of a trappable error. However in this case with incorrect SQL, it wouldn't rollback in any event because it is a non-trappable error. You must have correct syntax in the SQl for transactions to work correctly which is one reason why dynamic SQl can be very dangerous as it is impossible to fully test.
It is a bad practice to be creating tables on the fly like this anyway. If you want something temporaily, use a temp table or table variable, do not create a permanent table that you expect to rollback the creating of if the action fails.
I think you want a TRY...CATCH Block. There's a very nice explanation here on msdn.
For a quick example, though:
BEGIN TRY
BEGIN TRANSACTION
...your code...
COMMIT TRANSACTION
END TRY
BEGIN CATCH
...error reporting code here...
ROLLBACK TRANSACTION
END CATCH;

SQL Try Catch the exact errors caused by the recent variables

Query:
BEGIN TRY
SELECT #AccountNumber,
#AccountSuffix,
#Sedat,
#Dedo,
#Payalo,
#Artisto
FROM SWORDBROS
WHERE AMAZING ='HAPPENS'
END TRY
EGIN CATCH
Print #Sedat
END CATCH
How can I get the #Sedat, is it possible?
SQL 2005 , it will be in an SP
Like this, no?
BEGIN TRY
SELECT #AccountNumber,
#AccountSuffix,
#Sedat,
#Dedo,
#Payalo,
#Artisto
FROM SWORDBROS
WHERE AMAZING ='HAPPENS'
END TRY
BEGIN CATCH
--error handling only
END CATCH
--There is no finally block like .net
Print #Sedat
IN a proc when I want to trap the exact values that caused an erorr, this is what I do. I declare a table variable (very important must be a table variable not a temp table) that has the fields I want to have information on. I populate the table variable with records as I go. In a multitep proc, I would add one record for each step if I wanted to see the who process or only a record if I hit an error (which I would populate in this case in the catch block typically). Then in The catch block I would rollback the transaction and then I would insert the contents of the table varaible into a permanent exception processing table. You could also just do a select of this table if you wanted, but if I'm going to this much trouble it usually is for an automated process where I need to be able to research the problem at a later time, not see the problem when it hits becasue I'm not running it on my mchine or where I could see a select or print statement. By using the table varaible which stay in scope even after the rollback, my information is still available for me to log in my exception logging table. But it important that you do the logging to any permananent table after the rollback or the process will rollback with everything else.
which database are you using?
also, which programming language is this?
usually there would be an INTO clause and some local variables declared.
your query should also have a FROM clause at a minimum
It is not clear if you are expecting the returned values to be placed into the # variables or whether you are trying to dynamically specify which columns you want selected. In a Sql Server stored procedure you usually return a result set, not a bunch of individual variables. The syntax you have will not work if you want column values returned since what you have will dynamically specify which columns are wanted based on the column names passed into the stored procedure. And this will not work since the stored procedure must know which columns you are going after when it is analyzed as it is stored. Now the except clause will be trigged if there is a problem reading from the database (communication down, disk error, etc.) in which case none of the column values will be known.
Use the Sql Query Analyzer tool (under the "Tools" menu in SqlManager after you have selected a database) to define your stored procedure and test it. If you installed the documentation when you installed SqlManager go to Start>Programs>Microsoft Sql Server>Books Online and open the "Transact-SQL Reference" node for documentation on what can be done.