Global Data Management Forum

 View Only
Expand all | Collapse all

inside a stored procedure loop how to break based on table column status

  • 1.  inside a stored procedure loop how to break based on table column status

    Posted Tue December 17, 2024 05:47 AM

    Hi All,

    what is the problem with this procedure.
    A procedure is in a infinite loop, every time it checks if status_column in table status_table is 'S', then it will break out of loop and exit procedure.
    If status is other than 'S' , it will continue for ever until another application changes status_column value.  the issue is it is not coming out of loop based on status value column.  Please find procedure code and output also, you can see in output it reads the value 'C' and keeps on looping infinitely, but does not come out even after update of column value to 'S' .

    CREATE OR REPLACE PROCEDURE LOOP_UNTIL_STATUS_CHANGE()
    LANGUAGE SQL
    BEGIN
    DECLARE v_id INT;
    DECLARE v_value VARCHAR(100);
    DECLARE v_status CHAR(1);
    DECLARE done SMALLINT DEFAULT 0;
    DECLARE fl_done SMALLINT DEFAULT 1;

    -- Cursor declaration
    DECLARE cursor_loop CURSOR FOR
    SELECT ID, VALUE_COLUMN FROM MY_TABLE;
    DECLARE status_cursor CURSOR FOR
    SELECT STATUS_COLUMN FROM STATUS_TABLE WHERE ID =1;

    -- Declare a handler for when the cursor reaches the end of the table
    DECLARE CONTINUE HANDLER FOR NOT FOUND SET fl_done = 0;

    -- Infinite loop
    LOOP_INFI: LOOP
    -- Open the cursor
    OPEN cursor_loop;
    OPEN status_cursor;
    FETCH status_cursor INTO v_status;
    FETCH_LOOP: LOOP
    -- Fetch data from the cursor
    FETCH cursor_loop INTO v_id, v_value;
    IF fl_done = 0 THEN
    CLOSE cursor_loop;
    LEAVE FETCH_LOOP;
    END IF;
    set fl_done = fl_done + 1;
    call dbms_output.put_line('Fetch Looping ' || v_id || ' ' || v_value);
    END LOOP FETCH_LOOP;

    IF v_status = 'S' THEN
    -- Exit the loop if status is 'S'
    LEAVE LOOP_INFI;
    END IF;

    -- Close the cursor (optional in each cycle)
    set done = done + 1;
    set fl_done = 1;
    call dbms_output.put_line('Looping ' || done || ' ' || v_status);
    CLOSE status_cursor;
    END LOOP LOOP_INFI;

    call dbms_output.put_line('Out of Infinite Looping ' || done );
    END@

    the above code works infinitely, but it is not coming out of the infinite loop based on status_column value. the status column value is always C as seen at the start of procedure, it never picks up S from database after my update successful in another connection....
    Fetch Looping 1 Asherah
    Fetch Looping 2 Baal
    Fetch Looping 3 El
    Fetch Looping 4 Manat
    Fetch Looping 5 Mot
    Looping 21 C
    Fetch Looping 1 Asherah
    Fetch Looping 2 Baal
    Fetch Looping 3 El
    Fetch Looping 4 Manat
    Fetch Looping 5 Mot
    Looping 22 C
    Fetch Looping 1 Asherah
    Fetch Looping 2 Baal
    Fetch Looping 3 El
    Fetch Looping 4 Manat
    Fetch Looping 5 Mot
    Looping 23 C
    As you can see in output , it never picks up S at all keeps going in infinite loop.
    how to solve it?
    Thanks
    Harishkumar



    ------------------------------
    Harishkumar Pathangay
    ------------------------------


  • 2.  RE: inside a stored procedure loop how to break based on table column status

    Posted Wed December 18, 2024 11:33 AM

    Hi Harishkumar,

    The first thing I would suggest is checking the SQLCODE after the cursor opens and fetches.  Perhaps the open or fetch on status_cursor is getting an error.

    Steve



    ------------------------------
    Steve Johnson
    ------------------------------



  • 3.  RE: inside a stored procedure loop how to break based on table column status

    Posted Sun December 29, 2024 07:12 AM
    Hi All,
    Thanks for your valuable time and responses.
    I got the code working in a loop with exit based on Status Column in a Status Table. Please find attached the code i used to get the desired output.
    Thanks very much. Please consider the thread is closed. I do not want to debug this further as pointed out by Steve Johnson there is some unhandled SQLCODE causing some issues if i use dbms_output package.  

    --
    Thanks,
    Harish P





  • 4.  RE: inside a stored procedure loop how to break based on table column status

    Posted Wed December 18, 2024 04:10 PM

    I suspect that there is only a single row that satisfies
    SELECT STATUS_COLUMN FROM STATUS_TABLE WHERE ID =1

    As Steve suggests, the second time 
    FETCH status_cursor INTO v_status
    is executed SQLCODE=+100 is returned, and v_status is not updated.

    If you are expecting a different process to update v_status, you might need to be careful of locks.

    You don't mention which platform (z/OS, Windows, etc) or version of Db2 you are executing, depending on that, you might be able to use

    SELECT STATUS_COLUMN INTO v_status
    FROM STATUS_TABLE WHERE ID =1
    FETCH FIRST 1 ROW ONLY

    instead of the FETCH statement, and discard the status_cursor statements. 

    The FETCH FIRST 1 ROW ONLY is there to ensure that if there are multiple rows, you won't get an SQLCODE=-811.



    ------------------------------
    James Campbell
    ------------------------------



  • 5.  RE: inside a stored procedure loop how to break based on table column status

    Posted Wed December 18, 2024 05:15 PM

    May I suggest that you try to use global variables for STATUS - you may also be an innocent (?) victim of CURRENTLY COMMITTED semantics - unless it is explicitly off - you are seeing currently committed value of status instead of current value. It appears to me that on/off toggle or single value flag is a poor example of using table and cursor to inquiry value of such toggle.



    ------------------------------
    Jan Nelken
    ------------------------------



  • 6.  RE: inside a stored procedure loop how to break based on table column status

    Posted Thu December 19, 2024 08:51 AM

    Harishkumar,

    you're opening/closing status_cur within loop_infi.  I didn't see status_column updated here and I'll assume it is not updated somewhere else concurrently.

    Given that, closing and re-opening the cursor then fetching will always return the first row which apparently is always 'C'.  If you want status_cur to fetch an existing row with an 'S', you should move the opening and closing of status_cur outside loop_infi.



    ------------------------------
    Randy Nakagawa
    ------------------------------



  • 7.  RE: inside a stored procedure loop how to break based on table column status

    Posted Fri December 20, 2024 01:38 AM

    Hi,

    I am definitely updating the table with status column as S from another connection. 

    I don't know how to give evidence of that because I typing here. Do you want me to create a video and post it, I don't know what else to do.

    I will definitely create a video and share it with you.

    Thanks 



    ------------------------------
    Harishkumar Pathangay
    ------------------------------



  • 8.  RE: inside a stored procedure loop how to break based on table column status

    Posted Sun December 29, 2024 07:13 AM

    --
    Thanks,
    Harish P





  • 9.  RE: inside a stored procedure loop how to break based on table column status

    Posted Thu December 19, 2024 08:52 AM

    Hi Harishkumar,

    The issue with your procedure lies in the fact that the cursor on STATUS_TABLE is not re-fetching the updated value of the STATUS_COLUMN from the database during each iteration of the infinite loop. When a cursor is opened, it fetches a static snapshot of the data at that point in time, and subsequent changes to the table in the database are not reflected unless the cursor is closed and reopened.

    Potencial solution:

    CREATE OR REPLACE PROCEDURE LOOP_UNTIL_STATUS_CHANGE()
    LANGUAGE SQL
    BEGIN
        DECLARE v_id INT;
        DECLARE v_value VARCHAR(100);
        DECLARE v_status CHAR(1);
        DECLARE done SMALLINT DEFAULT 0;
        DECLARE fl_done SMALLINT DEFAULT 1;

        -- Cursor declaration for MY_TABLE
        DECLARE cursor_loop CURSOR FOR
        SELECT ID, VALUE_COLUMN FROM MY_TABLE;

        -- Declare a handler for when the cursor reaches the end of the table
        DECLARE CONTINUE HANDLER FOR NOT FOUND SET fl_done = 0;

        -- Infinite loop
        LOOP_INFI: LOOP
            -- Open the cursor for MY_TABLE
            OPEN cursor_loop;

            FETCH_LOOP: LOOP
                -- Fetch data from the cursor
                FETCH cursor_loop INTO v_id, v_value;
                IF fl_done = 0 THEN
                    CLOSE cursor_loop;
                    LEAVE FETCH_LOOP;
                END IF;
                SET fl_done = fl_done + 1;
                CALL dbms_output.put_line('Fetch Looping ' || v_id || ' ' || v_value);
            END LOOP FETCH_LOOP;

            -- Dynamically fetch the latest status value
            SELECT STATUS_COLUMN INTO v_status
            FROM STATUS_TABLE
            WHERE ID = 1
            FETCH FIRST 1 ROW ONLY;

            -- Exit the loop if status is 'S'
            IF v_status = 'S' THEN
                LEAVE LOOP_INFI;
            END IF;

            -- Increment the loop counter
            SET done = done + 1;
            CALL dbms_output.put_line('Looping ' || done || ' ' || v_status);

        END LOOP LOOP_INFI;

        CALL dbms_output.put_line('Out of Infinite Looping ' || done);
    END@

    This addresses the root cause of the issue by replacing the cursor-based approach for status_cursor with a direct query(SELECT INTO) to dynamically fetch the updated status.

    Yet another solution is to reopen the Cursor Each Iteration, like this:

    CREATE OR REPLACE PROCEDURE LOOP_UNTIL_STATUS_CHANGE()
    LANGUAGE SQL
    BEGIN
        DECLARE v_id INT;
        DECLARE v_value VARCHAR(100);
        DECLARE v_status CHAR(1);
        DECLARE done SMALLINT DEFAULT 0;
        DECLARE fl_done SMALLINT DEFAULT 1;

        -- Cursor declaration
        DECLARE cursor_loop CURSOR FOR
        SELECT ID, VALUE_COLUMN FROM MY_TABLE;

        DECLARE status_cursor CURSOR FOR
        SELECT STATUS_COLUMN FROM STATUS_TABLE WHERE ID = 1;

        -- Declare a handler for when the cursor reaches the end of the table
        DECLARE CONTINUE HANDLER FOR NOT FOUND SET fl_done = 0;

        -- Infinite loop
        LOOP_INFI: LOOP
            -- Open the main loop cursor
            OPEN cursor_loop;
            FETCH_LOOP: LOOP
                -- Fetch data from the main loop cursor
                FETCH cursor_loop INTO v_id, v_value;
                IF fl_done = 0 THEN
                    CLOSE cursor_loop;
                    LEAVE FETCH_LOOP;
                END IF;
                CALL dbms_output.put_line('Fetch Looping ' || v_id || ' ' || v_value);
            END LOOP FETCH_LOOP;

            -- Reopen the status cursor to fetch the updated status value
            OPEN status_cursor;
            FETCH status_cursor INTO v_status;
            CLOSE status_cursor;  -- Close it immediately after fetching the value

            -- Debugging output
            CALL dbms_output.put_line('Current Status: ' || v_status);

            -- Check if the status is 'S'
            IF v_status = 'S' THEN
                -- Exit the infinite loop if the status is 'S'
                LEAVE LOOP_INFI;
            END IF;

            -- Increment loop counter and print debugging output
            SET done = done + 1;
            CALL dbms_output.put_line('Looping ' || done || ' Status: ' || v_status);
        END LOOP LOOP_INFI;

        CALL dbms_output.put_line('Out of Infinite Loop after ' || done || ' iterations.');
    END@

    Pedro.



    ------------------------------
    PEDRO BARBAS
    ------------------------------



  • 10.  RE: inside a stored procedure loop how to break based on table column status

    Posted Thu December 19, 2024 08:55 AM


    The infinite loop is caused by a combination of 4 factors below:

    1. Incorrect Cursor Handling: You're opening cursor_loop inside the LOOP_INFI loop. This means it's reopened and re-processed on every iteration. The FETCH cursor_loop will always find rows unless MY_TABLE is empty. Even if the status_cursor shows 'S', the cursor_loop continues to fetch from MY_TABLE repeatedly. You only close cursor_loop conditionally when fl_done is 0, which only happens after a complete iteration of the inner loop.
    2. Misuse of fl_done: The variable fl_done is intended to signal the end of the cursor_loop but its logic is flawed. It's incremented regardless of whether the cursor reaches the end, effectively overriding the NOT FOUND handler's effect. The fl_done = 0 condition only stops the inner loop (FETCH_LOOP), not the outer LOOP_INFI.
    3. Unnecessary Nested Loops: The nested loops are problematic. The inner loop (FETCH_LOOP) iterates through all rows in MY_TABLE in every iteration of the outer loop. This is highly inefficient and the primary source of the infinite loop behavior because the inner loop always finishes before the condition to check v_status is ever met.
    4. Status_cursor Reopening: The Status_cursor is opened and closed in every iteration of LOOP_INFI. While this doesn't directly cause the infinite loop, it's inefficient. It should be opened once outside the main loop.

    John D



    ------------------------------
    JOHN DIEP
    ------------------------------



  • 11.  RE: inside a stored procedure loop how to break based on table column status

    Posted Fri December 20, 2024 03:30 AM

    Hello John Diep.  I used to work with a person with a similar name. Just wanted to make sure if it is the same person. This is Ramachandran Subramanian.  



    ------------------------------
    Ramachandran Subramanian
    ------------------------------



  • 12.  RE: inside a stored procedure loop how to break based on table column status

    Posted Sun December 22, 2024 04:04 AM

    Hi all,
    Please give me some time, I will respond next week. This week I am not available.
    Thanks
    Harishkumar







  • 13.  RE: inside a stored procedure loop how to break based on table column status

    Posted Thu December 19, 2024 08:56 AM

    The issue with your procedure lies in the fact that the cursor on STATUS_TABLE is not re-fetching the updated value of the STATUS_COLUMN from the database during each iteration of the infinite loop. When a cursor is opened, it fetches a static snapshot of the data at that point in time, and subsequent changes to the table in the database are not reflected unless the cursor is closed and reopened. To ensure the STATUS_COLUMN value is re-fetched during each iteration, reopen the status_cursor inside the loop so that it fetches the most recent value from the database.

    CREATE OR REPLACE PROCEDURE LOOP_UNTIL_STATUS_CHANGE()
    LANGUAGE SQL
    BEGIN
        DECLARE v_id INT;
        DECLARE v_value VARCHAR(100);
        DECLARE v_status CHAR(1);
        DECLARE done SMALLINT DEFAULT 0;
        DECLARE fl_done SMALLINT DEFAULT 1;

        -- Cursor declaration
        DECLARE cursor_loop CURSOR FOR
        SELECT ID, VALUE_COLUMN FROM MY_TABLE;

        DECLARE status_cursor CURSOR FOR
        SELECT STATUS_COLUMN FROM STATUS_TABLE WHERE ID = 1;

        -- Declare a handler for when the cursor reaches the end of the table
        DECLARE CONTINUE HANDLER FOR NOT FOUND SET fl_done = 0;

        -- Infinite loop
        LOOP_INFI: LOOP
            -- Open the main loop cursor
            OPEN cursor_loop;
            FETCH_LOOP: LOOP
                -- Fetch data from the main loop cursor
                FETCH cursor_loop INTO v_id, v_value;
                IF fl_done = 0 THEN
                    CLOSE cursor_loop;
                    LEAVE FETCH_LOOP;
                END IF;
                CALL dbms_output.put_line('Fetch Looping ' || v_id || ' ' || v_value);
            END LOOP FETCH_LOOP;

            -- Reopen the status cursor to fetch the updated status value
            OPEN status_cursor;
            FETCH status_cursor INTO v_status;
            CLOSE status_cursor;  -- Close it immediately after fetching the value

            -- Debugging output
            CALL dbms_output.put_line('Current Status: ' || v_status);

            -- Check if the status is 'S'
            IF v_status = 'S' THEN
                -- Exit the infinite loop if the status is 'S'
                LEAVE LOOP_INFI;
            END IF;

            -- Increment loop counter and print debugging output
            SET done = done + 1;
            CALL dbms_output.put_line('Looping ' || done || ' Status: ' || v_status);
        END LOOP LOOP_INFI;

        CALL dbms_output.put_line('Out of Infinite Loop after ' || done || ' iterations.');
    END@

    Other solution is to avoid the repeated reopening and closing of the cursor for fetching the status value by using a direct query within the loop to retrieve the STATUS_COLUMN value dynamically.



    ------------------------------
    PEDRO BARBAS
    ------------------------------



  • 14.  RE: inside a stored procedure loop how to break based on table column status

    Posted Thu December 19, 2024 09:10 AM

    The OPEN and CLOSE of status_cursor is within loop_infi.
    For each iteration of loop_info, it will fetch the first row from status_cursor, which it appears is always 'C'.
    I assume there is not another thread updating that 1 row from C to S, and that there is another row with S within the table.

    Given that, you should move the open/close of status cursor outside loop_infi.



    ------------------------------
    Randy Nakagawa
    ------------------------------