Search code examples
oracleplsqlsys-refcursororacle-cursor

How can I get the total number of records selected by a sysref cursor?


BACKGROUND

I am working on a web application that calls PLSQL stored procedures to retrieve and manipulate information. In one such case, the database has two stored procedures; one that selects the total number of records for a given set of parameters and one that returns the actual records with the same parameters, plus pagination parameters (min and max rownum).

EX (not the actual code):

PROCEDURE get_records_count(
    p_first_name IN record_table.first_name%TYPE,
    p_last_name IN record_table.last_name%TYPE,
    p_resultCursor OUT sys_refcursor
)
IS BEGIN 
    OPEN p_resultCursor FOR 
    SELECT count(*) from record_table 
        WHERE first_name LIKE (p_first_name || '%') 
        OR last_name LIKE (p_last_name || '%');
END;


PROCEDURE get_records(
    p_first_name IN record_table.first_name%TYPE,
    p_last_name IN record_table.last_name%TYPE,
    p_min IN NUMBER,
    p_max IN NUMBER,
    p_resultCursor OUT sys_refcursor
)
IS BEGIN 
    OPEN p_resultCursor FOR 
    SELECT * from record_table 
        WHERE first_name LIKE (p_first_name || '%') 
        OR last_name LIKE (p_last_name || '%')
        AND rownum >= p_min AND rownum <= p_max;
END;

Whether or not one thinks that this is a good idea is beyond the scope of my position. The problem is that whenever either stored procedure is changed, the results don't match up. The short term fix is to look through both stored procedures, determine which selection criteria from which stored procedure is appropriate and then edit the other stored procedure so that they both match up.

As a long term fix, I would like to change the get_records_count procedure to call the get_records procedure and then return the total number of records that would be returned from the resulting sys_refcursor.

EX:

PROCEDURE get_records_count(
    p_first_name IN record_table.first_name%TYPE,
    p_last_name IN record_table.last_name%TYPE,
    p_resultCursor OUT sys_refcursor
)
AS
v_recordsSelectedCursor sys_refcursor; 
BEGIN 
    /*I understand that I will need to change some logic in get_records to
      handle the case in which p_max is set to zero. 
      It should take this case to not apply an upper limit.*/ 
    get_records(p_first_name,p_last_name,0,0,v_recordsSelectedCursor);
    /*This is where I really have NO idea what I'm doing.
      Hopefully, you can infer what I'm trying to do. */ 
    OPEN p_resultCursor FOR
        SELECT count(*) FROM v_recordsSelectedCursor;  
END;

ACTUAL QUESTION

How can I select the number of records that would be returned for a sys_refcursor? The resulting number needs to be returned inside a sys_refcursor


Solution

  • A cursor is just a spec for fetching rows - it doesn't know how many rows are going to be returned until it has fetched them all.

    Any method involving calling it twice risks getting inconsistent results unless you use dbms_flashback.enable_at_time at the start of the procedure (and disable it at the end). There is also the performance overhead, of course.

    The only way to get a cursor to include its total rowcount is to include an analytic count(*) over () expression in the select list.