Oracle APEX Interactive report based on PLSQL function PDF Print E-mail
Tuesday, 24 November 2009 12:13

With the latest release of Application Express, Oracle have included a brand new reporting widget called Interactive Reports. They have managed to make it so simple to have fully functioning report where the only effort from the developer is creating the SQL query. Unfortunately Oracle have failed to allow the report to be based on a PLSQL function returning the query. This omission is a serious oversight by Oracle, as all my reports use this technique. Until Oracle update APEX to include this feature here is a workaround to display an interactive report based on a query stored in the database.

Step1.
Create a collection based on a query. The code for this should be placed in a before header process

APEX_COLLECTION.CREATE_COLLECTION_FROM_QUERY(
p_collection_name => ‘IR_TEST’,
p_query => function_returning_query );

Step 2.
Create an interactive report querying a collection.

Select *
From apex_collections
Where collection_name = ‘IR_TEST’;

Step 3.
You will notice that if you run the report that the column headers do not correspond to your dynamic query. This is because the IR is querying the collection and therefore using the column names from that. To resolve this, create hidden page items(eg P1_COL_HEAD1, P1_COL_HEAD2 etc) to hold the correct column name. Ensure that you create enough to hold the maximum expected columns. (I create 50 since this is the maximum a collection can hold). For each of these items replace the cxxx column header text in the report attributes for the relevant page item. eg. C001 for &P1_COL_HEAD1. and C002 for &P1_COL_HEAD2. (remember to include the dot).

Step 4.
Create a process that will populate the heading page items with the relevant column name values from the dynamic query. This can be done by splitting the select line using the commas and extracting the dynamic column headers.

Step 5.
Now that the column headers are displaying correctly we should hide any irrelevant columns. Because a collection has 50 main columns there can be many columns without any data, and because this report is meant to be dynamic we want the columns to dynamically display whenever there is any data in them. Go into the report attributes and go into every column starting cxxx and set a conditional display type of exists and enter the following SQL. Replacing c001 with the relevant column header.

Select 1
From apex_collections
Where c001 is not null;

Finally set the conditional display to never for the following columns collection_name, seq, clob001, md5_original.

And that’s it! The method outlined above does seem a bit extreme to allow an interactive report to display a dynamic query but currently it is the only workaround I know of.

Comments (12)
  • Carol M  - Clarification / Questions
    hi -- I'm very interested in using this solution, but I do have a few questions before investigating further... Note that I don't have much experience using collections!

    1) In the interactive report query, don't I need to query apex_collection_members based on the collection_id (rather than apex_collections based on the collection name)?

    2) In my interactive report page, how do I access the collection query string (returned by my function) in order to set the headers on the report columns? I assume there's some standard syntax for doing this...

    3) If I understand correctly, the function used to create the collection will need to apply the filters defined on the interactive report (otherwise it seems that the power of the interactive report would be greatly diminished!). How do I access those filters?

    Thanks!
    Carol
  • Admin
    Hi Carol

    1) It works just fine using the collection name

    2) Step 3 should guide you through how to create the custom column headers

    3) APEX takes the filters in the interactive report and applies them to whatever query you give it. So you dont have to worry about any filters within the interactive report query
  • Carol M  - RE: Clarification / Questions
    hi -- Thanks much. I do want to follow up with a couple more questions.

    Regarding my 2), I probably should have referred to step 4. How do I get/reference the dynamic query string so that I can split it up using the commas and set the values of the hidden page items?

    I think your response to 3) makes sense (I'm sure it will all become clearer when I start trying it myself), but in thinking ahead, I know that we might want to create other pages that can be branched to from the IR, and that have the same result set as is currently in the IR. For instance, the user uses the IR to get to the desired set of rows, then wants to do multi-row editing on that set. So, it would be good to allow them to branch to a tabular form page that holds the same result set. In that case, I (think) I'd need to know the filters that the IR is using. Is there a way to do that?

    Thanks,
    Carol
  • Admin
    I assume that you have created a function that returns the SQL statement as a varchar2. To retrieve the column headers from this, you would have to do some clever stuff in splitting the string so that you receive the correct column names. I did not specifically mention this in my guide as it could get quite complex, depending on how you format your SQL query. For example, you could extract the string prior to the first "from", and then split that up using commas. that should give you the column headers. Of course it can be more complex than that if you start using column aliases. If you find a technique that works for this, i would appreciate it if you could post it here and i will include it in my guide.

    Unfortunately i cannot think of a solution for your second comment. I will do a bit of investigation and if i find out how to do it, i will let you know
  • Carol M  - RE: Clarification / Questions
    I was making that first one too complex! I was thinking that the query string might somehow be accessible using the collection (eg attributes of it or something), but of course, I can just set a varchar to the return value of the function. Thanks for sticking w/ me on that!

    I'll be very curious if you can think of a way to capture and use the active filters in the IR. Please keep me posted.

    Thanks,
    Carol
  • Jesper Gurlev  - Systems Consultant
    Hi

    Thanks for your tutorial here on dynamic queries in combination with IR. Just what I need.
    One question though, to ensure that the collection is refreshed it seems that a page submit is needed. My query is dependant on various page items and is built by a pl/sql function returning the same columns every time, which make it a bit simpler.
    How can you achieve a a page submit when the IR Go button is pressed?

    Best regards,

    Jesper, DK
  • findly  - Clarification
    Hi,

    I tired to use your tutorial, unfortunatly I can't pass step 1, I have this error:
    ORA-06550: Ligne 4, colonne 12 : PLS-00201: l'identificateur 'FUNCTION_RETURNING_QUERY' doit être déclaré

    meaning function_returning_query is not define,

    some clarification, to solve that because I have no idea

    thanks
  • Roel  - Solution for the process for the headers
    Using dbms_sql it is not that difficult to populate the header fields with the column names from your query. The whole Before Header process is here (change the P7_COL to whatever you've called the header items):

    declare
    col_tab dbms_sql.desc_tab;
    col_num number;
    l_query varchar2(32767);
    l_cursor integer default dbms_sql.open_cursor;
    l_colcnt number default 0;
    begin
    -- Define the query
    l_query := 'select * from customers_vw';
    -- Populate the colletion
    if apex_collection.collection_exists(p_collection_name => 'DYNAMIC_IR')
    then
    apex_collection.delete_collection(p_collection_name => 'DYNAMIC_IR');
    end if;
    apex_collection.create_collection_from_query_b
    ( p_collection_name => 'DYNAMIC_IR'
    , p_query => l_query
    );
    -- Populate Column Headers
    dbms_sql.parse( l_cursor, l_query, dbms_sql.native );
    dbms_sql.describe_columns( c => l_cursor,
    col_cnt => l_colcnt,
    desc_t => col...
  • daniel  - step 4?
    Hi Guys,
    Can somone please explain step 4 in a bit more detail?
    i have created an interactive report and an item with a source of 'sql query'
    this method works fine in a standard report but fails to work with an interactive report.

    the sql query that i am setting into the item returns a date value from a database.
    :s
    Thanks,
    Dan
  • z0kNe  - LOV from PLSQL function
    hi all,
    I found a solution for the LOVs from PL/SQL functions, this script is based on http://forums.oracle.com/forums/thread.jspa?threadID=969167

    May be is a rough solution, but this method could be good in some cases.



    CREATE OR REPLACE TYPE tLOVPrueba IS OBJECT (
    display VARCHAR2(256),
    valor VARCHAR2(256)
    );


    CREATE OR REPLACE TYPE tipoTablaLOVPrueba AS TABLE OF tLOVPrueba;


    CREATE OR REPLACE FUNCTION obtenLOV (parametro in varchar2)
    RETURN tipoTablaLOVPrueba
    IS
    tablaDelaLOV tipoTablaLOVPrueba := tipoTablaLOVPrueba();
    cadsql VARCHAR2(3000);
    BEGIN

    cadsql := 'SELECT tLOVPrueba(''prueba'',''valor'')
    FROm dual';
    EXECUTE IMMEDIATE cadsql BULK COLLECT INTO tablaDelaLOV;
    RETURN tablaDelaLOV;
    END obtenLOV;






    -- LOV >> List of values definition
    select DISTINCT tabla.display d, tabla.valor r
    from table(obtenLOV('parametro')) tabla

    regards!!!
  • Sherry  - passing data element not data value
    Hi,
    I am stuggling to get this working for a demo next week. I do have all components correct (or so I hope) but one. I have a summary report that's based on document type & a count of the number of records within that doc type & within an age range. I have a link of Document type which brings back only that document type (this works well) but I have a hyperlink under the count(it's an aging report so the first column is age that's less than 11 (age is 0 - 10). The report is passing the resulting count back and not the data element (age10). Where would I put the sql condition to say 'where age < 10)?
    thank you for any help you can offer me.
    Sherry
  • craig  - Re Sherry
    in step 1 a function is called to return the SQL query. you could pass in the item that specifies the age restriction at that stage.
Write comment
Your Contact Details:
Comment:
:angry::0:confused::cheer:B):evil::silly::dry::lol::kiss::D:pinch:
:(:shock::X:side::):P:unsure::woohoo::huh::whistle:;):s
:!::?::idea::arrow:
Security
Please input the anti-spam code that you can read in the image.
 
| More

Sponsored Links

Who's Online

We have 4 guests online