Search code examples
oracleplsqlcursorsys-refcursor

Open a refcursor for a PL/SQL statement?


I'm creating a report for a business using Oracle and SSRS. The report requires me to aggregate contiguous ranges of serial numbers, which can consist of alphanumerics.

For example, say I have the following serials:

OPS114
OPS115
OPS116
OPS117
OPS145
OPS146
OPS160
890RPT
896RPT
897RPT

The report should have a single aggregate row for each contiguous range, with the count of each range, like so:

OPS114 - OPS117 (4)
OPS145 - OPS146 (2)
OPS160 - OPS160 (1)
890RPT - 890RPT (1)
896RPT - 897RPT (2)

I've pulled the data I need, and I'm bulk-collecting it into a table variable. Now, I need to aggregate the rows - this wouldn't be bad if I only needed to manipulate the data, but I need to have this available as a query for the refcursor. Can I open a refcursor for a PL/SQL FOR loop, or am I barking up the wrong tree? I've attempted to Google this, but the "cursor for loop" is not what I'm looking for. The alternative is to try to aggregate the results in SSRS using VB. (So either way, it won't be a good time.) I'm not sure if I have access to create a SQL table type for this, so this is the alternative I've sought.

If anyone has any experience with this, it would be greatly appreciated!


Solution

  • You could do this from a single SQL statement but you need to define the data better. Your column stores strings but you are using them as numbers to find out the range. And it seems the number part could either be before or after the string part.

    If you are able to write some logic that separates out the numbers like this (and maybe keep the string part in another column)-

    114
    115
    116
    117
    145
    146
    160
    890
    896
    897
    

    Then it reduces to a simple gaps and islands problem.

    Step 1 - Select rownum along with this column (this would be a continuous sequence starting from 1)

    Step 2 - Subtract rownum from this numeric data column.

    Step 3 - Group by that result

    Step 4 - Get min(numeric value) and max(numeric value) and count(numeric value) from the group which will be your result when combined as string.

    Numeric_part     Rownum      Difference
    ------------     ------      ------------
    114              1           113
    115              2           113
    116              3           113
    117              4           113
    145              5           140
    146              6           140
    160              7           153
    890              8           882
    896              9           887
    897              10          887
    

    Grouping this by Difference column, you get -

    Difference     Min(num)    Max(num)    count(num)   Result
    ----------     ---------   ----------  ----------   -----------------
    113            114         117         4            114 - 117 (4)
    140            145         146         2            145 - 146 (2)
    153            160         160         1            160 - 160 (1)
    882            890         890         1            890 - 890 (1)
    887            896         897         2            896 - 897 (2)
    

    That SQL statement can be used in PLSQL to return a cursor as in this link that @MickMnemonic has in the comments.

    Basically -

    OPEN cursor_variable FOR SELECT something FROM something...;