Another PL/SQL refactoring question!
I have several cursors that are of the general simplified form:
cursor_1 is
with X as (select col1, col2 from TAB where col1 = '1'),
Y as (select col1, col2 from TAB where col2 = '3'),
/*main select*/
select count(X.col1), ...
from X inner join Y on...
group by rollup (X.col1, ...
cursor_2 is
with X as (select col1, col2 from TAB where col1 = '7' and col2 = '9' and col3 = 'TEST'),
Y as (select col1, col2 from TAB where col3 = '6'),
/*main select*/
select count(X.col1), ...
from X inner join Y on...
group by rollup (X.col1, ...
cursor_2 is
with X as (select col1, col2 from TAB where col1 IS NULL ),
Y as (select col1, col2 from TAB where col2 IS NOT NULL ),
/*main select*/
select count(X.col1), ...
from X inner join Y on...
group by rollup (X.col1, ...
...
begin
for r in cursor_1 loop
print_report_results(r);
end loop;
for r in cursor_2 loop
print_report_results(r);
end loop;
...
end;
Basically, all of these cursors (there’s more than 3) are the same summary/reporting queries. The difference is in the factored subqueries. There are always 2 factored subqueries, “X” and “Y”, and they always select the same columns to feed into the main reporting query.
The problem is that the main reporting query is VERY large, about 70 lines. This itself isn’t so bad, but it was copy-pasted for ALL of the reporting queries (I think there’s over a dozen).
Since the only difference is in the factored subqueries (and they all return the same columns, it’s really just a difference in the tables they select from and their conditions) I was hoping to find a way to refactor all this so that there is ONE query for the giant report and smaller ones for the various factored subqueries so that when changes are made to the way the report is done, I only have to do it in one place, not a dozen. Not to mention a much easier-to-navigate (and read) file!
I just don’t know how to properly refactor something like this. I was thinking pipelined functions? I’m not sure they’re appropriate for this though, or if there’s a simpler way…
On the other hand, I also wonder if performance would be significantly worse by splitting out the reporting query. Performance (speed) is an issue for this system. I’d rather not introduce changes for developer convenience if it adds significant execution time.
I guess what I’d ultimately like is something that looks sort of like this (I’m just not sure how to do this so that it will actually compile):
cursor main_report_cursor (in_X, in_Y) is
with X as (select * from in_X),
Y as (select * from in_Y)
/*main select*/
select count(X.col1), ...
from X inner join Y on...
group by rollup (X.col1, ...
cursor x_1 is
select col1, col2 from TAB where col1 = '1';
cursor y_1 is
select col1, col2 from TAB where col2 = '3'
...
begin
for r in main_report_cursor(x_1,y_1) loop
print_report_results(r);
end loop;
for r in main_report_cursor(x_2,y_2) loop
print_report_results(r);
end loop;
...
(Using Oracle 10g)
Use a pipelined function. For example:
Create the function pipelined
MAIN procedure example
All of your various subqueries are reduced to a call to a single pipelined function, which determines the rows to return.
EDIT:
To combine all needed types and functions into 1 procedure, and also to use variables for subquery function parameters, I’m adding the following example:
Note the benefit now is that even if you have many different cursors, you only need to define the main query and subquery SQL once. After that, you’re just changing variables.
Cheers