views:

311

answers:

2

I'm trying to use a pipelined function to save on time and reduce redundancy in my queries. The function in question returns data from a reference table based on some input. Records in the main data table I am selecting from have multiple columns that all refer to the reference table. The problem I run into is that when I try to use the pipelined function more than once in the query, I get a "cursor already open" error.

For example:

select xmlelement("genInf", xmlelement("ID", vt.ID),
                           xmlelement("vID", vt.V_ID),
                           xmlelement("vNum", vt.V_NUM),
                           xmlelement("terrDataCode", TERR_CODE.column_value), --data is based on reference table
                           xmlelement("ABValCode", AB_VAL_CD.column_value), --data is based on reference table
                           ...
from V_TAB vt, table(UTIL.fn_getOvrdValXML(vt.terr_cd_id)) TERR_CODE,
               table(UTIL.fn_getOvrdValXML(vt.ab_val_id)) AB_VAL_CD
where vt.ID = in_vID;

This worked fine until I added the second reference to my pipeline function (fn_getOvrdValXML), and I now get the "cursor already open" error.

The pipelined function is very simple:

type t_XMLTab is table of XMLType; --this type is in the spec
....
function fn_getOvrdValXML(in_ID in ovrd.id%type) return t_XMLTab
        pipelined is
    begin
        for r in C_OvrdVal(in_ID) loop
            pipe row(r.XMLChunk);
        end loop;
        return;
    end;

The cursor is similarly simple:

cursor C_OvrdVal(in_ID in ovrd.id%type) is
        select xmlforest(ID as "valueID", S_VAL as "sValue", U_VAL as "uplValue",
                          O_VAL as "oValue", O_IND as "oIndicator", F_VAL as "finalValue",
                          O_RSN as "reason") AS XMLChunk
        from ovrd_val xov;
        where xov.id = in_ID;

Is there a way to work around this, or should I try to tackle this problem (the problem of having to reference ovrd_val and output an xmlforest in the same way many many many many times) differently?

I admit I'm new to pipelined functions so I'm not 100% sure this is an appropriate use, but it made sense at the time and I'm open to other ideas ;)

+1  A: 

If you're using pipeline functions, then you're on 9i minimum which means you can use the WITH clause:

WITH ovrdValXML AS (
  select xov.id,
         xmlforest(ID as "valueID", S_VAL as "sValue", U_VAL as "uplValue",
                        O_VAL as "oValue", O_IND as "oIndicator", F_VAL as "finalValue",
                        O_RSN as "reason") AS XMLChunk
     from ovrd_val xov)
SELECT xmlelement("genInf", xmlelement("ID", vt.ID),
                       xmlelement("vID", vt.V_ID),
                       xmlelement("vNum", vt.V_NUM),
                       xmlelement("terrDataCode", TERR_CODE.column_value), --data is based on reference table
                       xmlelement("ABValCode", AB_VAL_CD.column_value), --data is based on reference table
                       ...
  FROM V_TAB vt
  JOIN ovrdValXML terr_code ON terr_code = vt.?
                           AND terr_code.id = vt.terr_cd_id
  JOIN ovrdValXML ab_val_cd ON ab_val_cd = vt.?
                           AND ab_val_cd.id = vt.ab_val_cd
 WHERE vt.id = IN_VID;

Untested, and it's not clear what you're joining too - hence the ? on the join criteria.

OMG Ponies
Hmm interesting...This query is not the only one that will need the functionality of ordValXML - there are several others - about 6 by my last count, and there's a possibility more will be added. The reason I originally used the pipelined function (I'm on 10g) was that it seemed to be the most _reusable_ solution. Except it doesn't work asI had thought. :(
FrustratedWithFormsDesigner
@Frustrated: A view would be be the most reuseable solution - just move the content encapsulated in the WITH clause into a view.
OMG Ponies
For pipelined functions, the optimizer has to guess cardinality etc. As such it is easier to get the wrong plan (especially if you have several of them in a query). I'd go with a view, though probably a 'real' CREATE VIEW one rather than an inline or WITH view.
Gary
@OMG: Yeah, a view is probably better for solving this problem. Still not sure why I had problem with the cursor but it doesn't matter now with my view ;)
FrustratedWithFormsDesigner
A: 

Have you tried actually closing your cursor inside that pipelined function before piping row?

OPEN C_OvrdVal(in_ID);
FETCH c_OrdVal INTO my_chunk_variable;
CLOSE C_OrdVal;
PIPE ROW my_chunk_variable;
RETURN;
jva
I thought that the cursor for loop was supposed to handle that automatically.
FrustratedWithFormsDesigner