IBM SolidDB, fetching result fails due to no result en execute statement - sql

In my stored procedures I'm doing a lot of:
EXEC SQL EXECUTE ...
EXEC SQL FETCH ...
However, I'm getting some errors (Error 23506: End of table in cursor) when the execute statement doesn't return anything and I'm trying to fetch the result.
Are there anyway to check if the result contains anything before trying to fetch it?
I've tried SQLSUCCESS and SQLROWCOUNT, but SQLSUCCESS only tells me if the statement doesn't fail, not if it returns anything, and SQLROWCOUNT apparently only works for inserts, updates and deletes. Not select statements.

This reference compares DB2 and SolidDB procedures.
In the section "Listing 15. SQLSUCCESS showing end of result set", the IBM reference shows this snippet.
EXEC SQL FETCH sel_tab;
WHILE SQLSUCCESS LOOP
EXEC SQL FETCH sel_tab;
END LOOP
It also says
When the FETCH cursor statement fails
and does not find another row to
retrieve, the value of SQLSUCCESS is
set to 0 and the WHILE LOOP ends.
That pretty much tells me that the WHILE loop shouldn't even be entered if that first EXEC SQL FETCH doesn't find a row. But you seem to be suggesting that isn't happening in your code.
Later, in "Table 7. solidDB SQLERROR of cursorname statement", it shows this code. (My annotations.)
"CREATE PROCEDURE tabs_in_schema (schema_nm
VARCHAR) RETURNS (nr_of_rows INTEGER)
BEGIN
DECLARE tab_nm VARCHAR;
EXEC SQL PREPARE sel_tab -- A SELECT statement
SELECT table_name FROM sys_tables
WHERE table_schema = ?;
EXEC SQL PREPARE ins_tab
INSERT INTO my_table (table_name,
schema) VALUES (?,?);
nr_of_rows := 0;
EXEC SQL EXECUTE sel_tab USING -- Executes the SELECT
(schema_nm)INTO (tab_nm);
EXEC SQL FETCH sel_tab; -- EXEC SQL FETCH first row
WHILE SQLSUCCESS LOOP -- Like listing 15
nr_of_rows := nr_of_rows + 1;
EXEC SQL EXECUTE ins_tab USING
(tab_nm, schema_nm);
IF SQLROWCOUNT <> 1 THEN
RETURN SQLERROR OF ins_tab;
END IF;
EXEC SQL FETCH sel_tab; -- FETCH subsequent rows
END LOOP
END";
I suppose you could execute something like
SELECT COUNT(your_column_name)
FROM your_table
WHERE ...;
COUNT() will always return at least one row as long as your query is valid. But that involves more round trips to the database. I think you're better off sticking to the idiom of trying to fetch a row, and trapping the error.

Related

SQL continue handler is not triggered

IBM i V7R1M0. I need to continue processing a statement whenever an error occurs and as far as I have read, such as from this:
https://dba.stackexchange.com/questions/88862/how-to-ignore-sql-errors-in-stored-procedure-not-handle
DECLARE CONTINUE HANDLER seems to be the answer, so
I have a very simple procedure that looks like this:
exec SQL create or replace procedure test_prod1
(in test2 decimal(1,0))
language sql modifies sql data
begin
declare continue handler for sqlexception
begin end;
update DUPEPF set INT2 = test2;
end;
As far as I can tell, this means whenever an error occurs (such as unique key violation) the SQL statement will simply continue, yet this isn't the case. The statement stops whenever a key violation appears and the next rows are not processed. I am confused as to why this is the case
Your continue handler is working...
Your procedure is ignoring the error thrown by the UPDATE statement and continuing. Except that there isn't anything else to do.
Just because your proc is ignoring the error, doesn't mean the DB can ignore the error inside of it's update statement processing.
EDIT
handlers change how your stored procedure or UDF handle errrors...think of them as a way to "catch" errors thrown by the DB. They don't prevent the DB from throwing those errors in the first place
Make sense?
In order to do what you're trying to do, you'd need to use your own cursor, something like so...
create or replace procedure test_prod1
(in test2 decimal(1,0))
language sql modifies sql data
begin
declare myInt integer;
DECLARE DUPLICATE_KEY CONDITION FOR SQLSTATE '23505';
DECLARE END_OF_TABLE CONDITION FOR SQLSTATE '02000';
declare test_cursor cursor for
select int2 from DUPEPP for update;
declare exit handler for END_OF_TABLE
close test_cursor;
declare continue handler for DUPLICATE_KEY
begin end;
open test_cursor;
fetch_loop:
LOOP
fetch next from test_cursor into myInt;
update dupepf set int2 = test2
where current of test_cursor;
END LOOP fetch_loop;
end;

Teradata Dynamic Insert - Looped?

I'm doing something very similar to dnoeth's second comment on this question:
Insert Into table Teradata dynamic stored procedure SQL
I need to run it multiple times to loop the same insert statement but with different values for the "?" and I'm not sure how to go about that.
The dynamic value in my version is a date span. I can't run a massive insert without spooling out so I've broken the data into segments.
Thanks.
William,
As you are aware that you need to use cursor to iterate through each value you want to process.
For that purpose store dynamically created INSERT statement into a variable.
Final step: Use EXECUTE IMMEDIATE command to run insert statement stored in teradata variable
Here is the working sample that you may refer
REPLACE PROCEDURE td_user.sp_dynamic_insert( OUT proc_msg VARCHAR(5000) )
BEGIN
DECLARE lv_insert_txt VARCHAR(20000);
DECLARE EXIT HANDLER FOR SQLEXCEPTION
BEGIN
/* Error handling code if required */
END;
L0:
FOR insert_cursor AS select_list
CURSOR FOR
SELECT Col2
FROM test_1
DO
SET lv_insert_txt = 'INSERT INTO test_2(Col1,Col2) VALUES('||TRIM(insert_cursor.Col2)||','||TRIM(insert_cursor.Col2)||')';
EXECUTE IMMEDIATE lv_insert_txt;
END FOR L0;
SET proc_msg = 'Procedure completed successfully';
END;

Matching and updating two table

I am going to fetch every row from one table and find the equivalent in another table. Then i am going to update the rows of the second table by using the id which i have already gotten.
I tried to run my script but i had some problems.
I actually tried to make a loop and then put the id of every row in a variable to use them for my update statement but Pl shows me an error which tells me "not data found"
My unfinished script
DECLARE
tbl1Count number(4);
counter number(4);
MyO66ID number(8);
Begin
select Count(*) INTO tbl1Count from crbank ;
<<my_loop>>
For counter IN 1..tbl1Count-1 Loop
select O66ID INTO MyO66ID from crbank where rownum=counter;
End loop my_loop;
End;
You have written a strange logic in this scenario
This should work:
DECLARE
tbl1Count number(4) :=0;
MyO66ID number(8);
Begin
-- select Count(*) INTO tbl1Count from crbank; -- not needed at all
For myItems IN (select O66ID, ROWNUM, whatever_columns_you_need from crbank) Loop
MyO66ID := myItems.O66ID;
tbl1Count := tbl1Count + 1; -- this will serve you better than the first select if you are concerned of the number of rows you have.
/*
Do your logic here for the values you have in the myItems object
EX: update yourTable set yourColumn = myItems.otherColumn where id= myItems.something
You dont need variables to be defined if you noticed as in the above example.
*/
End loop;
End;
Hints:
You are getting the count, then looping on the count you get and matching it with rownum!, which is not a best practice; hitting your database twice, for count and for select, although you can do it in once loop, and no need for the first select
rownum will be different for each select statement, depending on the order you specified, so is it wise to use it?
You have mentioned in your question
I am going to fetch every row from one table and find the equivalent in another table
Oracle just have a workaround for this type of conditions. MERGE statement is very useful in these typical scenarios. Consider the below illustrated snippet. Let me know if this helps.
Whenever it is possible try to use pure SQL over PL/SQL
MERGE INTO <Update_table> USING <LOOKUP_TABLE>
ON
(UPDATE_TABLE.COLUMN_NAME = LOOKUP_TABLE.COLUMN_NAME)
WHEN MATCHED THEN
UPDATE SET
<UPDATE_TABLE.COLUMN_NAME> = <Update_value>
;
Try this one using cursor in sql.
Declare #id bigint
DECLARE CUR CURSOR FOR
select data from table1
open CUR
Fetch next from cur into #id
while ##FETCH_STATUS=0
begin
update table2 set columnname=value where id=#id
Fetch next from cur into #id
end
CLOSE CUR
DEALLOCATE CUR

How to execute stored procedures containing dynamic SQL in oracle?

I've created the following procedure
Create or replace procedure abcd
(
tab_name in USER_TABLES.table_name%type
)
is
begin
execute immediate
'select * from'||tab_name;
end abcd;
The procedure gets compiled.
I am trying to get the output using the following
select abcd('Table') from dual ;
I am new to dynamic SQL and this does not seem to work for me. I keep getting the error
[Error] Execution (44: 8): ORA-00904: "ABCD": invalid identifier
Can someone please help ?
Regards,
Kshitij
You're missing a space before your table name:
create or replace procedure abcd (tab_name in USER_TABLES.table_name%type )
is
begin
execute immediate 'select * from '||tab_name;
end abcd;
This won't work because you're trying to call it as a function, not a procedure:
select abcd('Table') from dual ;
Your second attempt should now work:
exec abcd('Table');
... but will now get a different error. In PL/SQL you have to select into something. In this case you probably want to open a cursor with the dynamic string and do something with the results. Not really sure what your end goal is though.
You should also read up about SQL injection while you learn about dynamic SQL.
you cannot perform a select on a procedure, a function will work only if single record return.
use
begin
abcd();
end;
or use
execute keyword
ALSO use a space after from in query
It will not work.
When you invoke EXECUTE IMMEDIATE the sql statement is send to SQL engine. No results are passed back to the PL/SQL.
Writing "SELECT * FROM a_table" is not that hard and much safer.

HSQL Iterated FOR Statement not working

Using HSQL 2.2.5 I need to shudder process one row at a time in a stored procedure, so I thought the "Iterated FOR" statement might do the trick for me. Unfortunately I don't seem to be able to make it work. It's supposed to look something like:
FOR SELECT somestuff FROM sometable DO
some random SQL statements
END FOR;
That leaves off a bit of the syntax, but it's close enough for now.
The problem seems to be that the statements inside the loop never execute. I've verified that my SELECT statement does indeed return something.
So let's get concrete. When I execute this stored procedure:
CREATE PROCEDURE b()
MODIFIES SQL DATA
BEGIN ATOMIC
DECLARE count_var INTEGER;
SET count_var = 0;
WHILE count_var < 10 DO
INSERT INTO TTP2 VALUES(count_var);
SET count_var = count_var + 1;
END WHILE;
END;
I get 10 rows inserted into table TTP2, with values 0 through 9. (TTP2 has just one column defined, of type INTEGER.)
But when I substitute a FOR statement for the WHILE like so:
CREATE PROCEDURE c()
MODIFIES SQL DATA
BEGIN ATOMIC
DECLARE count_var INTEGER;
SET count_var = 0;
FOR SELECT id FROM ttp_by_session FETCH 10 ROWS ONLY DO
INSERT INTO TTP2 VALUES(count_var);
SET count_var = count_var + 1;
END FOR;
END;
I get nothing inserted into TTP2. (I have verified that the SELECT statement returns 10 rows, one column of integers.)
When I leave the FETCH clause off I still get no results. ttp_by_session is a view, but the same thing happens with a bare table.
What am I doing wrong?
Thanks for the help.
This works fine with the latest version of HSQLDB. Try with the 2.3.0 release candidate snapshot from the HSQLDB web site.
When the FOR statement was initially added about two years ago, it had limited functionality. The functionality was extended in later versions.