SQL - How to call a part of query conditionally - sql

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.

Related

IBM DB2 SQL sleep, wait or delay for stored procedure

I have a small loop procedure that is waiting for another process to write a flag to a table. Is there any way to add a delay so this process doesn't consume so much cpu? I believe it may need to run between 1-2 min if everything ends correctly.
BEGIN
DECLARE STOPPED_TOMCAT VARCHAR (1);
UPDATE MRC_MAIN.DAYEND SET DENDSTR = 'Y';
SET STOPPED_TOMCAT = (SELECT TOMCSTP FROM MRC_MAIN.DAYEND);
WHILE ( STOPPED_TOMCAT <> 'Y')
DO
SET STOPPED_TOMCAT = (SELECT TOMCSTP FROM MRC_MAIN.DAYEND);
END WHILE;
END;
Use call dbms_alert.sleep(x), where x - number of seconds.
I don't have the resources to test this solution, but why don't try calling IBM i Command DLYJOB in your code:
CALL QCMDEXC('DLYJOB DLY(1)', 13);
The parameter DLY indicates the wait time in seconds and the number 13 is the length of the command string being executed.

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.

PowerBuilder 12.5 sql cursors transaction size error

i have a major problem and trying to find a workaround. I have an application in PB12.5 that works on both sql and oracle dbs.. (with a lot of data)
and i m using CURSOR at a point,, but the aplications crashes only in sql. Using debuging in PB i found that the sql connection returs -1 due to huge transaction size. But i want to fetch row by row my data.. is any work around to fetch data like paging?? i mean lets fetch the first 1000 rows next the other 1000 and so on.. i hope that you understand what i want to achieve (to break the fetch process and so to reduce the transaction size if possible) , here is my code
DECLARE trans_Curs CURSOR FOR
SELECT associate_trans.trans_code
FROM associate_trans
WHERE associate_trans.usage_code = :ggs_vars.usage ORDER BY associate_trans.trans_code ;
OPEN trans_Curs;
FETCH trans_Curs INTO :ll_transId;
DO WHILE sqlca.sqlcode = 0
ll_index += 1
hpb_1.Position = ll_index
if not guo_associates.of_asstrans_updatemaster( ll_transId, ls_error) then
ROLLBACK;
CLOSE trans_Curs;
SetPointer(Arrow!)
MessageBox("Update Process", "Problem with the update process on~r~n" + sqlca.sqlerrtext)
cb_2.Enabled = TRUE
return
end if
FETCH trans_Curs INTO :ll_transId;
LOOP
CLOSE trans_Curs;
Since the structure of your source table s not fully presented, I'll make some assumptions here.
Let's assume that the records include a unique field that can be used as a reference (could be a counter or a timestamp). I'll assume here that the field is a timestamp.
Let's also assume that PB accepts cursors with parameters (not all solutions do; if it does not, there are simple workarounds).
You could modify your cursor to be something like:
[Note: I'm assuming also that the syntax presented here is valid for your environment; if not, adaptations are simple]
DECLARE TopTime TIMESTAMP ;
DECLARE trans_Curs CURSOR FOR
SELECT ots.associate_trans.trans_code
FROM ots.associate_trans
WHERE ots.associate_trans.usage_code = :ggs_vars.usage
AND ots.associate_trans.Timestamp < TopTime
ORDER BY ots.associate_trans.trans_code
LIMIT 1000 ;
:
:
IF (p_Start_Timestamp IS NULL) THEN
TopTime = CURRENT_TIMESTAMP() ;
ELSE
TopTime = p_Start_Timestamp ;
END IF ;
OPEN trans_Curs;
FETCH trans_Curs INTO :ll_transId;
:
:
In the above:
p_Start_Timestamp is a received timestamp parameter which would initially be empty and then will contain the OLDEST timestamp fetched in the previous invocation,
CURRENT_TIMESTAMP() is a function of your environment returning the current timestamp.
This solution will work solely when you need to progress in one direction (i.e. from present to past) and that you are accumulating all the fetched records in an internal buffer in case you need to scroll up again.
Hope this makes things clearer.
First of all thank you FDavidov for your effort, so i managed to do it using dynamic datastore instead of cursor,, so here is my solution in case someone else need this.
String ls_sql, ls_syntax, ls_err
Long ll_row
DataStore lds_info
ls_sql = "SELECT associate_trans.trans_code " &
+ " FROM associate_trans " &
+ " WHERE associate_trans.usage_code = '" + ggs_vars.usage +"' "&
+ " ORDER BY associate_trans.trans_code"
ls_syntax = SQLCA.SyntaxFromSQL( ls_sql, "", ls_err )
IF ls_err <> '' THEN
MessageBox( 'Error...', ls_err )
RETURN
END IF
lds_info = CREATE DataStore
lds_info.Create( ls_syntax, ls_err )
lds_info.SetTransObject( SQLCA )
lds_info.Retrieve( )
DO WHILE sqlca.sqlcode = 0 and ll_row <= ll_count
FOR ll_row = 1 TO ll_count
ll_transId = lds_info.GetItemNumber( ll_row, 'trans_code' )
ll_index += 1
hpb_1.Position = ll_index
do while yield(); loop
if not guo_associates.of_asstrans_updatemaster( ll_transId, ls_error) then
ROLLBACK;
DESTROY lds_info
SetPointer(Arrow!)
MessageBox("Update Process", "Problem with the update process on~r~n" + sqlca.sqlerrtext)
cb_2.Enabled = TRUE
return
end if
NEXT
DESTROY lds_info
LOOP

Not able to remove injected script from database rows

I've been handed a MS SQL 2000 database which has been injected with malware.
The malware script is as follows:
<script src=http://www.someAddress.ru/aScript.js></script>
Now I want to remove this piece of code from the table rows.
As a test, I inputed < h1> Test < /h1> on a row, and successfully ran the following query:
UPDATE myTable
SET description = REPLACE (description, '<h1>','')
WHERE id = 2;
This removed the h1 tag.
But trying the same with the script tag does not work:
UPDATE myTable
set description = REPLACE (description, '<script src=http://www.someAddress.ru/aScript.js></script>','')
WHERE id = 2
Why does this not work?
UPDATE 2
WOHO! I found the solution!
I'm using the folloing code, which I found here: http://www.tek-tips.com/viewthread.cfm?qid=1563568&page=3
-- Look for open and close HTML tags making sure a letter or / follows < ensuring its an opening
-- HTML tag or closing HTML tag and not an unencoded < symbol
CREATE FUNCTION [dbo].[udf_StripHTML]
(#HTMLText VARCHAR(8000))
RETURNS VARCHAR(8000)
AS
BEGIN
DECLARE #Start INT
DECLARE #End INT
DECLARE #Length INT
SET #Start = CHARINDEX('<',#HTMLText)
SET #End = CHARINDEX('>',#HTMLText,CHARINDEX('<',#HTMLText))
SET #Length = (#End - #Start) + 1
WHILE #Start > 0
AND #End > 0
AND #Length > 0
BEGIN
SET #HTMLText = STUFF(#HTMLText,#Start,#Length,'')
SET #Start = CHARINDEX('<',#HTMLText)
SET #End = CHARINDEX('>',#HTMLText,CHARINDEX('<',#HTMLText))
SET #Length = (#End - #Start) + 1
END
RETURN Replace(LTRIM(RTRIM(#HTMLText)),' ',' ')
END
GO
To remove the HTML tags / scripts, I run the following query:
UPDATE mytable
SET description = [dbo].[udf_StripHTML](description)
//WHERE id = 35;
This works perfectly. Note that this script removes ALL html. So if I only want to remove < script> , I just replace '<' with '< script'.
Have you tried looking for just aScript.js, the entry could be url_encoded, or something similar, so it gives something like
%3Cscript+src%3Dhttp%3A%2F%2Fwww.someAddress.ru%2FaScript.js%3E%3C%2Fscript%3E
Reread Question
Do you mean that even when you have the script tag in a column with id=2 it doesn't work? Because if its not working are you sure that it exists in row with id=2? :p
Should work, unless there are other hidden characters in there you can't see, or there is some form of encoding going on. Can you SELECT a suspect row to look at more closely.
I would tend to completely DELETE FROM myTable WHERE description LIKE '%someAddress.ru%' where possible.
However, fixing the database isn't a real solution; the application must be fixed. It shouldn't ever be echoing text out of the database unencoded. If someone enters some data including the string <script> it should simply appear on the page as the literal string <script>, or in the source <script>.
Wouldn't the src attribute value be surrounded by quotes? If so, you would have to escape them to get a proper match on the replace.
Why not try:
UPDATE myTable
set description = REPLACE (description, 'www.someAddress.ru','localhost')
WHERE id = 2
That would eliminate the immediate hijacking problem, and would likely avoid line break / funky characters problems.
You could try the following to strip the code out of your field (I'm assuming you have information in the same field that you want to keep):
update myTable
set description = case when PATINDEX('%<script%', notes) > 0
then SUBSTRING(notes, 1, PATINDEX('%<script%', notes)-1) + SUBSTRING(notes, PATINDEX('%script>%', notes) + 7, LEN(notes))
else notes
end
where id=2
You could first run a select to see if the value returned by the CASE statement is correct before running the update. It should not affect fields without a script tag in them, though.
Hold on...
Is the database related to a financial system? Is the application under Sarbanes-Oxley? Has any fraud been committed?
Any of those things preclude you from making changes that would, "destroy evidence." Those little guys running around with "FBI" on their jackets don't take kindly to that. It would be a good thing to back it up now, and the logs (SQL and Web), and put that backup on a few DVDs. It would be better to remove the disk and put in another one (but that may not be an option).
Moving on to cleansing:
bobince's direction is the correct one. Don't look for the whole SCRIPT tag, or try to find variations. Instead, look for something in the script tag that isn't part of the normal dataset. That's what you key off. If it SELECTs okay, then turn it into a DELETE and save that query, because you will need it while you turn to fixing the application (guaranteed your database will get corrupted again).

SQL GOTO statement using variable label

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)