SQL GOTO statement using variable label - sql

i was hoping to be able to do something like this:
declare #label varchar
set #label = 'label_1'
goto #label
label_1:
label_2:
of course, sql server gives me an incorrect syntax error... so i was wondering if i can still do this with a slightly different syntax?

I've cooked up a functional hack that may prove useful to others. On SQL Server 2008, you cannot dynamically build the goto, nor can you supply the label in a variable. The old school hack is to load the variable, jump to a single point that then uses if statements to determine the real destination.
GOTOs are of course considered harmful and are generally a bad idea.
l1:
print '1'
l2:
print '2'
goto l4
l3:
print '3' -- should not print
l4:
print '4'
declare #lbl nvarchar(5)
set #lbl = N'l6'
goto vjump
-- goto #lbl -- doesn't work
-- exec('goto ' + #lbl) -- doesn't work
l5:
print '5'
l6:
print '6'
l7:
print '7'
return
vjump:
if #lbl = 'l1'
goto l1
if #lbl = 'l2'
goto l2
if #lbl = 'l3'
goto l3
if #lbl = 'l6'
goto l6
This produces
1
2
4
6
7

You can only do this if you dynamically build the SQL statement.
GOTOs are best avoided though - mainly for code readability.

Why not simply use an if/else?

As far as I know, you cannot parameterize GOTO. You can do that if you use Dynamic SQL.

Not a timely answer obviously, but when I recently found myself wanting to do this, the reason was because I wanted to execute the same bit of code multiple times and then return to the normal flow, basically "gosub" and not "goto"....
And that you can do. Not with a gosub, but with a temporary stored procedure -- create a temp stored procedure create procedure #DRY and then call it. You can drop it when you are done.

try this:
exec("goto "+#label)

Related

SQL - How to call a part of query conditionally

I need to write a SQL query like this:
do step1
do step2
do step3
- this step does a lot of stuffs: define variables, use some variables defined in step 1, query a lot of data into temporary table...
if(cond1)
if(cond2)
Begin
do step 4
call step 3 here
End
else
Begin
do step 5
End
else
Begin
call step 3 here
End
How to make step 3 a reusable query to avoid calling step 3 unnecessarily? notice that step 3 should be a part of the whole query
When I try to create a #step3Query NVARCHAR(MAX), SET #step3Query = '...'
Then call this query in appropriated places by call "EXEC sp_executesql #step3Query". But I got a lot of errors, and I am not sure that is a correct way to do task like that.
Any suggestions would be highly appreciated.
Here is one method that could work (depending on your exact circumstance). The main thing for this is that it's easy, as well as easy to read.
You could have 3 variables to use as flags e.g., #Query3_Flag, #Query4_flag, #Query5_flag.
Your conditional checks just set these flags e.g.,
IF (condition1)
BEGIN
SET #Query4_Flag = 1;
SET #Query3_flag = 1;
SET #Query5_flag = 0;
END
Before each of queries 3, 4 and 5, have the IF statement check the flag and only run the query if the flag is set to 1 e.g.,
IF #Query3_Flag = 1
BEGIN
(run Query 3)
END
Note that the queries will need to be in the correct order.

SSMS Set Statistics Time On/Off prints multiple lines instead of just one

When I run my query, it prints multiple execution times instead of just one. I only want one so what do I need to do to get this to only print one time stamp?
SET STATISTICS TIME ON
DECLARE #firstNum INT, #secondNum INT, #thirdNum INT, #evenSum INT
SET #firstNum = 1
SET #secondNum = 2
set #thirdNum = 2
SET #evenSum = 2
WHILE (#thirdNum <= 4000000)
BEGIN
SET #thirdNum = #firstNum + #secondNum
SET #firstNum = #secondNum
SET #secondNum = #thirdNum
IF (#thirdNum % 2) = 0
SET #evenSum += #thirdNum
END
PRINT 'Answer = ' + CONVERT(VARCHAR, #evenSum)
SET STATISTICS TIME OFF
If you remove PRINT 'Answer = ' + CONVERT(VARCHAR, #evenSum) from your code
then it won't print multiple execution time.
Here is the example of it.
Statistic time will print for each execution. Since you are looping you are performing multiple query statements (I believe this also applies for sets -i could be wrong though), each will provide an execution time. There really is no way to modify statistic time for what I think you are looking for.
You could declare a datetime2 and store the start time in the beginning by using SysDateTime, and get the end time upon completion, thus printing the difference onto the screen using DateDiff. (This will achieve what you are asking for. )
You could also look into client statistic (but that might not be what you want).
Side note and irrelevant: you are looping queries. This is not profient and you may be doing this to learn. I would recommend looking into Tally tables for replacing loops. You can greatly improve performance if you design your query correctly.

UDF on DB2 11.0

I was asked to build a user defined function on our Mainframe environment that checks for a search string in a longer string. The only catch is that if we search for example for 'AA' in 'ABCAADAA' the only valid result is the last AA because the first AA actually split in CA and AD.
CREATE FUNCTION F#CRE#WK (WK CHAR(02), WKATTR CHAR(10))
RETURNS INTEGER
LANGUAGE SQL
READS SQL DATA
BEGIN
DECLARE INDEX INTEGER DEFAULT 1;
WHILE (INDEX < 9) DO
SET INDEX = LOCATE_IN_STRING(WKATTR, WK, INDEX);
IF (MOD(INDEX, 2) <> 0) THEN
RETURN 1;
END IF;
END WHILE;
RETURN 0;
END;
It is working fine when I implement it using Data Studio but if I put it onto the host directly (we're using Quick32770) I'm getting a bunch of errors which don't make sense at all. I couldn't find any helpful resources(searched the whole IBM page and Google of course).
First error I'm getting is:
SQLCODE = -104, ERROR: ILLEGAL SYMBOL "<END-OF-STATEMENT>". SOME
SYMBOLS THAT MIGHT BE LEGAL ARE: ;
Which refers to the line I'm declaring my index variable. If I remove the semicolon it tells me that the SET is illegal there because it is expecting a semicolon.
I cannot think of anything else I could try(I messed around with the code a lot but errors just kept getting more weird.). I started working in this field while being in college just a couple of weeks ago and nobody here has actual knowledge about this so I was hoping to find some help here.
If there's anything else you need, just let me know!
Thanks in advance.
This might help you:
https://bytes.com/topic/db2/answers/754686-db2-udf-need-eliminate-if-statement
It says the if statement is not allowed on the mainframe in UDF ?
So this user bend it around to a CASE function.
In order to fix this you need to go into the SPUFI settings and change the TERMINATOR option to something else than a semicolon. If I changed it to & my code must look like this:
CREATE FUNCTION F#CRE#WK (WK CHAR(02), WKATTR CHAR(10))
RETURNS INTEGER
LANGUAGE SQL
READS SQL DATA
BEGIN
DECLARE INDEX INTEGER DEFAULT 1;
WHILE (INDEX < 9) DO
SET INDEX = LOCATE_IN_STRING(WKATTR, WK, INDEX);
IF (MOD(INDEX, 2) <> 0) THEN
RETURN 1;
END IF;
END WHILE;
RETURN 0;
END&

DB2 Procedures return result set after for loop

I'm currently on writing a SQL-Procedure on IBM i 5 V7R1.
If I want to return a result set after my for loop I can't create the procedure.
Here is the code:
create or replace procedure test1()
DYNAMIC RESULT SETS 1
LANGUAGE SQL
SPECIFIC EDVVAEH1.test1
NOT DETERMINISTIC
MODIFIES SQL DATA
CALLED ON NULL INPUT
SET OPTION ALWBLK = *ALLREAD ,
ALWCPYDTA = *OPTIMIZE ,
COMMIT = *NONE ,
DECRESULT = (31, 31, 00) ,
DFTRDBCOL = *NONE ,
DYNDFTCOL = *NO ,
DYNUSRPRF = *USER ,
SRTSEQ = *HEX
proc: BEGIN
DECLARE x VARCHAR(255);
DECLARE return_cur CURSOR with return FOR
select * from sysibm.sysdummy1;
loop1: for record as C1 cursor for
SELECT TABLE_NAME
from qsys2.systables
DO
SET x = record.table_name;
END FOR loop1;
open return_cur;
END proc
As the options define, I want to return a result set after both loops, but it doesn't let me create this procedure.
The Error is -104 token: "End of Statement" is not valid, but everything is there :(
It seems I found the root cause of my problem, but I can't fix it.
As far as I can see, my i Access has a Problem with the for-statement.
I copied the statement to my RDi 9.1 in the Database Development view and ran it there. I also set a different end of statement command and after that I was able to create my procedure.
Now I need to wait until someone in my company can provide me a PTF for the client access. I already tried to install all PTFs delivered with the IBM i, but no improvements ...
If anyone has an idea, where I can download a PTF for client Access please give me a hint. The IBM site is not possible for me since, I am not registered for out IBM i.
Thanks.

Is there a way to exec a SP, check the result with an if statement and return the value, all within just one line?

I know this works but does anybody know of a way of doing all these in one line of code:
EXEC #ResultInt = NameofAStoredProcedure
IF #ResultInt <> 0
RETURN #ResultInt
LIKE:
-- I'm trying something like this but it does not work we want to do it all in
-- one line to do a Find-Replace throughout our code base
IF (EXEC #ResultInt = NameofAStoredProcedure) <> 0 RETURN ResultInt
Maybe I'm getting something wrong, but why not simply removing the whitespaces?
EXEC #ResultInt = NameofAStoredProcedure IF #ResultInt <> 0 RETURN #ResultInt;
If all you want is to have it in one line for easier editing, that should work. You even could include the declaration of #ResultInt:
DECLARE #ResultInt int; EXEC #ResultInt = NameofAStoredProcedure IF #ResultInt <> 0 RETURN #ResultInt;
Tested with MS SQL 2005.
Instead of trying to crush everything down to one row to make mass find and replaces easier,
I would recommend using regular expressions to handle carriage returns and line feeds while maintaining code readability. For instance, let's say you wanted to change "IF #ResultInt <> 0" to "IF #ResultInt <> 1" and add row "SET #ResultInt = 1", then do the following:
ENABLE REGULAR EXPRESSIONS IN FIND AND REPLACE
In Find and Replace in both Visual Studio and SQL Server Management Studio, expand the Find Options -> Select Use -> Select Regular expressions.
FIND WHAT STATEMENT
EXEC \#ResultInt = NameofAStoredProcedure\nIF \#ResultInt \<\> 0\nRETURN \#ResultInt
REPLACE WITH STATEMENT
EXEC \#ResultInt = NameofAStoredProcedure\nIF \#ResultInt \<\> 1\nSET \#ResultInt = 1\nRETURN \#ResultInt
RESULTS
EXEC #ResultInt = NameofAStoredProcedure
IF #ResultInt <> 1
SET #ResultInt = 1
RETURN #ResultInt
EDIT. As noted this doesn't work, but I'll leave it in place to show what you cannot do :)
Without chaining items all together onto one ugly line, the only way I can think of is to wrap the call in a Scalar Function.
But then it's not something you can re-use easily and will create allot of wrapper objects if your doing this allot. Is great way to keep code clean if your only doing this for one or two procedures, more then it just makes allot of objects to manage in the DB.
Example
CREATE FUNCTION dbo.SPWrapper() RETURNS int
AS
BEGIN
DECLARE #out int;
EXEC #out=NameOfStoredProcedure;
RETURN #out;
END
GO
-- Usage
IF (SELECT dbo.SPWrapper() ) <> 0