SQL GOTO statement in a script with multiple GO - tsql

I need to exit a SQL script without an error if a certain condition holds true. I've read that 1 solution would be to raiseerror with error code 20+ and with log parameter. But the limitation for that is that i can execute that only as an admin and the connection to the db will be aborted.
Also, I tried using GOTO and jump to the end-of-the-script, but it doesnt work, because I have multiple GO in the middle of the script. Is there a another solution?
IF <some condition>
BEGIN
GOTO Finished;
END
GO
Finished:
SELECT 'Done'
Thanks!

goto cannot jump past a go. You'd have to retest the condition in each block:
IF NOT <some condition>
BEGIN
...
END
GO
IF NOT <some condition>
BEGIN
...
END
GO
IF NOT <some condition>
...

Related

Questions on structuring T-SQL

I'm learning T-SQL and trying to get my head around how to approach it/best practices. Working through some examples, there are three questions I have:
1) Coming from Python, is there a generally agreed upon style guide (something like PEP 8) or approach for laying out T-SQL or is it more like JavaScript where anything goes?
2) When creating stored procedures and functions I see some authors always use a main BEGIN/END block and others don't. At first I thought you need BEGIN/END if you have more than one statement. However, this doesn't seem to be true because I have seen lengthy stored procedures with no main BEGIN/END. Any thoughts on this?
3) Within stored procedures, some authors seem to like to enclose parts of their code in BEGIN/END blocks. I can't see why you'd do this and wonder if I'm missing something. For example:
CREATE PROCEDURE <NAME> (
<Parameter List...>
)
AS
BEGIN
-- Setup:
-- Declare/initialize variables...
BEGIN TRY
BEGIN TRANSACTION
-- Validity check 1:
IF #Param1 ...
BEGIN
-- Do stuff...
END
-- Validity check 2
IF #Param2 ...
BEGIN
-- Do stuff...
END
-- Update - added BEGIN/END after if blocks for clarity
-- Why wrap these statements in a BEGIN/END Block???
BEGIN
--Add the entry
INSERT dbo.JournalClientFamilyChanges (
HouseholdMembersID,
PreviousClientsID,
NewClientsID,
ActionTaken,
Notes,
ModifiedBy,
ModifiedDate
)
VALUES (
#HouseholdMembersID,
#PreviousClientsID,
#NewClientsID,
#ActionTaken,
#Notes,
#ModifiedBy,
SYSDATETIME()
)
SET #success =1;
SET #ErrorStatus ='';
COMMIT TRANSACTION;
END
END TRY
BEGIN CATCH
-- Error handling...
END CATCH
END
Any thoughts appreciated,
--Jim
To answer your question at your own example
IF #Param1 ...
-- Validity check 2
IF #Param2 ...
-- Why wrap these statements in a BEGIN/END Block???
BEGIN
--Add the entry
INSERT dbo.JournalClientFamilyChanges (
HouseholdMembersID,
...
)
VALUES (
#HouseholdMembersID,
#PreviousClientsID,
...
)
SET #success =1;
SET #ErrorStatus ='';
COMMIT TRANSACTION;
END
There is an IF which will call the code below just under certain circumstances. Without the BEGIN ... END this will be valid for the very next statement only. Using BEGIN ... END will see the whole code block covered by the IF condition (the lines with SET and COMMIT).
Try this out:
DECLARE #SomeInt INT=0;
IF #SomeInt=1
PRINT 'Example 1: hello, this is the first line';
PRINT 'Example 1: hello, the next line';
IF #SomeInt=1
BEGIN
PRINT 'Example 2: hello, this is the first line';
PRINT 'Example 2: hello, the next line';
END
For better readability one would use indents. But this is not relevant for the engine. There are some languages using indents as block marker though, others use paranthesis or any kind of brackets. T-SQL uses BEGIN ... END. Sometimes people use this in larger procedures just to allow collapsing (like the #region in C#)
IF #SomeInt=1
PRINT 'Example 1: hello, this is the first line';
PRINT 'Example 1: hello, the next line';
IF #SomeInt=1
BEGIN
PRINT 'Example 2: hello, this is the first line';
PRINT 'Example 2: hello, the next line';
END
update
Just to reflect the comments below: The OP changed the initial question in a way, that the BEGIN ... END is no longer connected to the IF. So the question shoudl be: Why would one use BEGIN ... END without any functional reason?.
The answer for this is
To mark some lines of code as a "block"
... thus emphasising the functional unit
Collapsing is a nice side-effect

How to break a loop if "break" is inside fork-join?

I need to break a repeat loop, whose break decision is made inside a fork-join block, but my simulator doesn't compile the code that has the following structure.
repeat (10) begin
fork
begin
// do something
end
begin
#(100ms);
break; // compile error
end
join_any
disable fork;
end
I also tried the disable command. I was able to compile, but it didn't break the loop.
repeat (10)
begin : repeat10_loop
fork
begin
// do something
end
begin
#(100ms);
disable repeat10_loop; // try
end
join_any
disable fork;
end : repeat10_loop
Is there a way to use break or disable inside fork-join?
The break and continue statements must be within the same process as the loop statement. The problem with your disable is you incorrectly labeled your loop. Try:
repeat10_loop: repeat (10)
begin

Remove redundant GOs from SQL script using Powershell

I need to remove redundant GO statements from a large SQL file before it gets passed through Invoke-sqlcmd for deployment.
Multiple GO statements together causes "There are no batches in the input script" and using -OutputSqlErrors $false masks all other errors.
Get-Unique deletes all duplicate data - which is not desirable. I would only like to delete the duplicate GO statements
Current Script:
Exec (#SQLScript)
Print #SQLScript
End
GO
GO
if obj is not null
drop procedure
go
CREATE PROC
#al varchar(16),
#rule varchar(128)
END CATCH
GO
GO
If Exists (Select * From Table)
Go
Set #Start = DateAdd(m, 1, #Start)
End
GO
GO
I would like to get a script like this:
Exec (#SQLScript)
Print #SQLScript
End
GO
if obj is not null
drop procedure
go
CREATE PROC
#al varchar(16),
#rule varchar(128)
END CATCH
GO
If Exists (Select * From Table)
Go
Set #Start = DateAdd(m, 1, #Start)
End
GO
If you load the script into a variable, you can use regular expressions to match and replace multiple "GO" statements. For ex:
$ReplacedText = $OriginalScript -replace '(GO(\n)*){2,}',"GO`n"
The Regular expression matches "GO" that may or may not be followed by a new line, 2 or more times. and replace it with a single "GO" followed by a new line.

SQL Server Management Studio Transaction and Variable

In SSMS 2012, after setting Options > Query Execution > ANSI > SET IMPLICIT_TRANSACTIONS, cf this SO post
I have the following code in a query window:
begin transaction
select ##TRANCOUNT
begin
declare #someNumber int; set #someNumber = 1;
print #someNumber;
end
rollback
When I select the whole block and press Execute, I see the expected result, i.e. 1.
However, when I select the first 4 lines and execute, then select line 5, i.e. print #someNumber;, I got the following message:
Msg 137, Level 15, State 2, Line 1
Must declare the scalar variable "#someNumber".
What is exactly the scope of the variable?
I'm baffled. Can someone shed any light or point me to the right direction please?
The variable is scoped per batch.
The scope of a local variable is the batch in which it is declared.
Each press of "Execute" is a batch. So, for the 2nd run, it isn't declared
What are doing with 2 runs is this
--start of batch 1
begin transaction
select ##TRANCOUNT
begin
declare #someNumber int; set #someNumber = 1;
--end of batch 1
GO --separate batch after this
--start of batch 2
print #someNumber;
end
rollback
--end of batch 2
In SSMS, a variable lives for the duration of the execution.
If you execute the declaration only, it dies when the execution ends.
If you then execute the print statement of the variable, it was not declared within this execution so it does not exists.

How to purposely test errors in a transaction?

I have a stored procedure that has several transactions in a loop:
WHILE #COUNT < #MY_NUM
BEGIN
BEGIN TRANSACTION
-- DO STUFF HERE
IF(##ERROR != 0)
BEGIN
ROLLBACK TRANSACTION
BREAK
END
COMMIT TRANSACTION
END
I would now like to test whether or not my ROLLBACK TRANSACTION and BREAK logic will work by purposely introducing an error to the loop after a certain number of runs and look at the data.
Furthermore, I run these stored procedures from a shell script. So, I would like to test by using Ctrl + C in the middle of a run. Will this work? If not, how can I purposely introduce an error?
Thanks
You could do one of the following:
Intentially cause an error like Divide by zero
Use RaiseError()
You can to use print:
WHILE #COUNT < #MY_NUM
BEGIN
BEGIN TRANSACTION
print 'BEGIN TRANSACTION'
-- DO STUFF HERE
print '-- DO STUFF HERE'
IF(##ERROR != 0)
BEGIN
ROLLBACK TRANSACTION
print 'ROLLBACK TRANSACTION #MY_NUM: %1!',convert(varchar,#MY_NUM)
BREAK
END
COMMIT TRANSACTION
print 'COMMIT TRANSACTION'
END
Here it's link to documentation.