Sunday 2 September 2012

Overview of Oracle Reports



Introduction: 


            So you've got your queries set up and you are ready to design your layout. The problem is, none of the Oracle default layouts meet your needs, or, when you try to use one you get an error message saying that the layout you chose won't fit on the page. Then, when you finally finish moving the fields around into the places you want them, it either gives you a "Frequency below it's group" error, prints in an unexpected order / format / page orientation, or does something really strange like reprinting the same page endlessly. Trying to determine the cause and solution from the documentation now begins, invariably taking three or four hours to unearth the correct two second cure. It is somewhere about this point that your monitor is in danger of receiving a quick lesson in Newtonian physics. Hopefully, this document will make learning advanced topics in Oracle Reports 2.6 a much less painful experience, and perhaps save a few monitors from an untimely demise.

            This document is intended for the relative novice who has a basic understanding of the mechanics of Oracle's Report writer, but will also include tips and tricks which may aid the more experienced user. The target audience should at least know how to build a data model (including breaks although instructions for their creation is included), and have a basic understanding of how objects can be manipulated in the layout editor. This is also intended to be a continuous work in progress so that as you find a new wrinkle please add it to this document in an appropriate spot in order that the rest of us may benefit.

The document is structured into two parts. First is my own informal description of how Reports 2.5 deciphers your layout and data model as a clear understanding of this and the relationships between the two parts is critical. From there, I will cover some basic design and efficiency issues common to most reports, then move on to more advanced topics. These include a description of some of the more useful built in procedures and packages that Oracle was good enough to include in Reports but fails to clearly highlight in the main portion of their documentation. The second part of this document is a FAQ section to answer the most common "How do I …." questions I have either heard from others or asked myself. While I'm sure I will not have remembered them all, in the ones I do cover I will try to answer them by explaining how to do it, and (most importantly) why it works. Hopefully this section will give as many insights into Report's internal machinations as the first, or at least help illustrate many of the issues discussed in the first section.



The Reports 2.6 Environment:


            The main areas of the Reports environment are the Object Navigator, the Parameter Form, the Data Model and the Layout Editor. While for the most part the Object Navigator holds only the information derived from the other three areas, it also holds the information used to determine the global variables such as the page setup information and user defined variables. These variables may be used to set selection parameters, print order, or display strings used in the data model and layout.

            The format of the Object Navigator environment is very similar to that found in Oracle Forms, with a couple of exceptions. Where in forms, code snippets are kept with the object that it belongs to, in reports all triggers are kept in the "Program Units" section. The layout area only shows a "p" in the bullet at the left of  an object to indicate that a code snippet exists for that object. For this reason, it is best to stick with the generated names for object triggers as they are derived from the name of their parent object to make it easier to find them in the Navigator, or develop a similar source-based naming convention.

 

The Data Model


            The Data Model is where the queries needed to retrieve your data are created and linked. Each SQL statement is held in block, and then the join conditions between the blocks are created by selecting the link tool and then clicking first on the parent field and then the child field for the linking relationship. Each statement may be either a simple or complex query, and they may use the values held in parameters by preceding the parameter name with a full colon, consistent with the way field values and global parameters are accessed in Oracle Forms.

Because of the tight relationship between the structure of the data model and the  design of the layout, getting from here to the desired final product can be much more pleasant experience by careful planning at this stage. Each distinct grouping of data in your layout requires a distinct block be created in the data model. For the novice user, I would suggest going as far as drawing a rough mockup example of the report to determine how data is to be grouped, and where each piece of information is to be displayed. Then consider that the data is fetched from the top of the model down through your blocks. A piece of information must be included in a query above or equal to the level at which you want it displayed. This will allow you to quickly evaluate the structure and order of the blocks you will need as well as what information is needed for each block. When the same piece of information is required in more than one location on the report, remember that a layout object can always access variables from a higher level block, but not a lower level one. The rule of thumb is to put information in the highest level block of your data model as is possible while meeting the needs of the layout.
A break group is a method of breaking a single SQL block into separate blocks without the need to write separate SQL statements. They are used to create a report in which an identical value in sequential rows is printed only once for its related records. (i.e. A break group divides a query's columns into sets like a "group by" statement.)

To create a break group:
                                                                           
    1. In the Data Model Editor, drag the main group that contains the column at which
you want to break your report down from the query title object about 2 inches.                                                                      
    2. Select the column that you want to use to divide your report (e.g. if you want to
group a list of employee data by department number, select the department
number column) and drag it out of and above the group to create a new group.                                                                 
    3. Double-click on the title bar of the new group object to display its property sheet. 
Replace the name in the GENERAL:Name Field if you do not want to use the
default.  Fill in the rest of the property sheet as necessary and accept it.                                                             

Although all of the built in dialog boxes for creating queries are designed to help the user create blocks based on a query from a single table, remember that this was designed to aid the novice user with little SQL experience. For efficiency reasons, whenever possible you should try to create single block based upon an optimized compound select statement rather than a number of joined select statement blocks. This is because Oracle must open separate cursors and link them together for each individual select statement block you create, but a more complex select statement with a break group is treated as a single cursor with a group by clause. You are more likely to optimize the query than Oracle's built-in optimizer, and fewer blocks in the data model equates to fewer frames needed on your layout which is also easier to work with and more efficient at runtime. Finally, by creating a compound statement, any of the fields selcted can be promoted or demoted through break blocks to ensure that is accessible at the proper time in the layout. In separate queries this promotion is not easy to accomplish except through inefficient summary and formula fields. Your goal should be to have the fewest select statements and also the fewest distinct blocks of fields on your display that will fulfill the levels of groupings that your report layout will need.

In cases where there is no option but to create separate select statements with links between them, it is not necessary to restrict links to match columns with an "equals" condition. If you click on a link and bring up it's properties box, you will find that you can select from ( =, < ,<= ,<> ,>, >=, LIKE, NOT LIKE) as the join condition. In cases where even these options are not sufficient (for example, when you would like an "or" clause between two links rather than the default "and" used to evaluate multiple links), there is another option. First, create a dummy link between the title bars of the two blocks rather than between columns by selecting the link tool and then clicking first on the parent block's title bar and then the child block's title bar. Now, in the select statement of the child block, you can access column values in the parent block as though they were user variables by using the colon to indicate a parent block column name. You can now create as complex a link clause as you desire.

            Additional filtering can be done by right-clicking on the title bar of a block and selecting the "Program Unit Editor" option. This will create a trigger called a "group filter" returning true or false which determines whether a row for this block is included in the report. It can also be used to perform actions on each fetch instance of the block. This is different from the trigger on that block's repeating frame in the layout because returning false in the group filter will exclude that row's data from any summary columns, while the repeating frame trigger will only exclude the data from the display. It also ensures that the trigger fires on every fetch. This cannot be guaranteed in the layout since if that block has a parent whose repeating frame also has a trigger, if the parent repeating frame does not print, the child's trigger will not be fired since it's result is irrelevant to the layout.

            You will also notice that when Reports parses a select statement you have given it, to the left of every field name in the block diagram is a little black triangle. These are representations of the break order for each field and can be considered equivalent to an order by clause. These are initially all to ascending by default unless your query specified a descending "order by" clause for a given field in which case you will see a little upside down black triangle. The thing to remember about these break orders is that Oracle completely ignores them in the lowest level detail block for that select statement, but lives by them once you drag the fields into a break block.

In a break block, all fields moved into it will use these break orderings, even if they override the original select statement's ordering clauses, and at least one field in a break box must have an ordering clause. The  display order of output for the report will be based upon the break orders of the fields in the break box ordered from top to bottom in the data model. For efficiency reasons, if you move a bunch of other fields into the break for soley for display reasons to have them at this level of your data model structure, you should turn off their break orders from within their properties forms. This is done by unchecking the "break order" checkbox near the bottom of the field's Properties box. If you do not do this, Oracle will put in all the extra indexing overtime to sort the results on these fields when you don't care about the order, or worse yet when the order is trivial.

            An example of this would be assuming you have a master-detail type report on some aspect of account information, and want the account_id, account_name, and contact info in a break box with the rest of the report details underneath. Your presentation is to be sorted by account_id only. If you leave the break orders on all the fields in the break box, Oracle will be running your query as though there was a "order by account_id, account_name, contact_id, contact_name …." clause. This is extremely inefficient since it's doing all the indexing work even though there is never more than one account_name or contact_id for each account_id, but it'll spend a good chunk of time spinning it's wheels checking the indexes just to make sure.


The Layout Editor


            The Layout Editor provides two different views of the page. The margin view shows the entire page size and anything on this view is printed on each page of the report unless overridden by a trigger. A thick black box indicates the area in which the details of the report will be printed. The margin view should be used to print all static titles, headings and footer fields which will remain positionally constant on every page. If your headings will change from page to page depending on the current block being printed, then they should be placed within the body view. Note that the heavy box outlining the details section can be moved and sized to create the appropriate margins needed for the body view, allowing you to then use the full limits of the body view without worrying about the margins.

            The body view is the size of the margin box you created in the margin view, and so is usually smaller than your actual page size. Here is where the fetched record details are formatted and displayed.  When building a layout from scratch, you must remember that frames must be nested in order of  the hierarchy you developed in the Data Model, and all higher level frames must exist before a lower level one can be included (i.e. you cannot skip a repeating block just because you do not need to print anything from it).  Basically, you need one repeating frame for each distinct block that you have in your data model. The only exception is a detail block that is only used to provide info that is summarized in a higher level block and is never actually displayed. In this case, the detail block would not have to be included in the layout to let it's parent evaluate the summaries. However, for speed and efficiency reasons, if such a situation can be avoided through such things a "sum(a) … group by b" select statement in the data model instead of the existing statement it should be implemented that way. SQL mathematical functions work much, much (and probably three more "muchs")  faster than the Report's summary columns.

In addition to the explicit anchors you can create  to keep items in position to each other, Oracle creates a set of implicit anchors between objects based upon what it thinks the intent of your layout was. The algorithm used is included in the Oracle Reports Reference Manual. Often, when items are not printing where you expected them to it is because Oracle either misinterpreted or was unable to determine your intent. The basic algorithm consists of looking at objects who have variable sizes and establishing which way they will grow or shrink. Objects within the path that they can grow/shrink are assumed to want to stay the same distance from the end of the object. Objects of fixed size are not evaluated as being parent objects in the implicit anchoring algorithm. This means that if an object is of fixed size and it's trigger tells it not to print, objects following within the path it would have had if were of variable size are not repositioned to take it's place unless they are explicitly anchored to that object.

While Reports will not show you what implicit anchors it has created in the layout editor, you can get it to do so in the Object Navigator. Select the Object Navigator as the active window, and then select "Navigator Options" from the "Navigator" menu. Select the "Layout" page of the options form and check the "Anchoring Information" checkbox and then the "Apply" button. Now, if you look at the "Layout" section of the Object Navigator, any object for which you have defined an explicit anchor or Oracle has defined an implicit one will have an "Anchoring Information" section. The following example shows a field which is anchored as the parent object to B_AA_LINE1, and is the child object of  P_AA_MSG1.

 fig 1. Anchors displayed in Object Navigator

The graphics used for these anchor displays is somewhat counter-intuitive in that the current object is the small ball, and the object named as the other object in the anchoring relationship is the large ball. There is also no way of determining from the Object Navigator if the anchor is an implicit or explicit one. You would have to look at the Layout Editor to see if there is an explicit anchor showing for that object. Finally, when defining explicit anchors, remember that the order of selecting objects is important. First you click on the child, and then select the parent. This is opposite to creating links in the Data Model where you select the parent first.

The Layout Editor is extremely picky about the creation order as it assumes that this is directly related to the ordering of parent-child relationships among the objects. As a new object is created on the layout, it becomes a child of the most recently created frame or repeating frame which completely encloses it. Once this parent-child relationship has been set, changing it is a complex task (although there are several techniques explained in the FAQ section of this document). This means that you should first create the parent frame and then all the child fields which are to be placed in it if you want to avoid "accessing value below it's frequency" errors. This ordering scheme also holds true for any frames you may wish to place in the margin view of the layout, although this should be avoided whenever possible.

Report Execution


The basic order of execution is to:

    1. Fire the before form trigger
    2. Run the parameter form
    3. Fire the after form trigger
    4. Parse the data model
    5. Fire the before report trigger
    6. Execute the report, firing between page trigger after each page but the last
    7. Fire the after report trigger.

While it is the order of execution within part 6 which requires the closest attention, notice that the data model is parsed before the "before report" trigger fires. This means that the BeforeReport trigger fires too late to alter any user variables used in the data model as the cursor has been defined. Any such validation must be done either in the validation triggers attached to the variables themselves or in the AfterPForm trigger to have any effect on the query definition. The BeforeReport trigger can still, however, be used to alter the tables to change what data is available within the tables for selection. All triggers are required to return a Boolean value, and report execution is terminated upon a return value of false in any one of the main report triggers.

It can almost be said that the layout and data model act independently from each other in that each record is fetched by the query created by your data model, and then is evaluated as to how it fits into the layout. The layout is scanned from the top down  with each object being evaluated and printed as it is reached. It is not until an object is printed in it's entirety that the scan continues downwards. In this way, each frame may be viewed almost as a child instance of the report as when a repeating frame becomes the current object being printed, a new scan is started within that frame under the same rules as the main scan. Only when all of the contents of all instances of this repeating frame are printed does the main scan resume for the next item outside of the repeating frame it has just completed. Oracle maintains a hierarchy of nested items currently being formatted to determine what triggers may need to be fired as objects are formatted over multiple pages.

When a page break occurs, the report reprints the margin specification, as well as any objects higher up in the nesting hierarchy of currently printing objects which have appropriately set their print conditions to reprint and then continues it's scan. Some print conditions or variables may require that the report format itself further ahead to determine what must be printed on the current page. The simplest example of this is to set a field to display $TotalPhysicalPages (e.g. So that the header shows a "page x of y" field). This requires the report to run completely to determine the total pages before it can fully format the first page, even when run in preview mode.

Triggers


Besides the five built in main triggers mentioned earlier, triggers are small pieces of code which may be attached to any of the objects placed in the layout editor. They are required to return a true or false value which corresponds to whether this instance of the trigger's object should be printed. For example, a trigger on a repeating frame is reevaluated for each repeat to see if the contents should be printed in that instance.  The basic rule regarding format triggers is 'The fewer the better' as they slow the report formatting. You should try to perform as much data validation in the data model as possible through such things as the decode statement or group filters. When circumstances dictate the use of triggers, try to group items which will print under the same conditions into a frame and then attach the trigger to the frame. This avoids unnecessary repetition of the same code for multiple objects.

Frames and repeating frames will have their triggers fire on each page for which that object prints. This means that a frame that stretches to print over multiple pages will fire it's format trigger on each of those pages. Also, for all objects the trigger fires on the first page on which that object is formatted. One significant result of this is that you should not base the format trigger's result upon anything pertaining to page numbers if you are not sure of where on the page it will print (e.g. detail lines). An object may start to print on the bottom of one page, thus firing the trigger, and then Oracle decides the object is too big and moves it to the next page without re-firing the trigger. In this case, a result based upon the current page number would in fact be being evaluated on the basis of the previous page number. This problem may also cause some problems if you are attempting to coordinate between the object's trigger and the BetweenPages trigger where the BetweenPages fires after a field you would have expected it to have fired before. The only way to guarantee what page an object will print on is if it is an object in the Margin View of the Layout Editor, or by making it a fixed size child of a fixed size frame which has been set to "page break before".

The BetweenPages trigger also has another notable side-effect. In preview mode it will fire in both directions if you work forwards and backwards through the display. This may lead to some strange results if it is manipulating variable values upon which other fields or their printing conditions are based. Of course, this problem will not occur when the report is run to the screen or to a printer.

Another trigger with potentially harmfull side-effects is the BeforePForm trigger. While any errors in the AfterPForm or BeforeReport will simply reactivate the parameter form, an unhandled exception in the BeforePForm trigger will cause the report to exit. This is very frustrating to the user as the report exits before the parameter form pops up, so it looks as though the report never executed at all. In cases where the error is due to a select from a missing table, the report may GPF and also kill the application that called it. Selecting into a user parameter with a format mask or restricted list of possible values is also risky as if the select returns null. The report decides that it can not apply the mask to null and exits. The way around this eventuality is to select into a local variable and then assign this value to the parameter only if it is non-null.

To access the values of any of the user variables from the data model portion of the object navigator, simply precede them with a full colon. E.g. :p_invoice_date, :p_account_name, or whatever else you might have named them. Note that while you may access their values, Reports 2.5 does not allow you to modify them after the BeforeReport trigger has completed.

User variables may also have validation triggers attached to them. These can be created by pressing the "Edit.." button on the parameter's properties form, double clicking the round dot to the left of the parameter in the object navigator, or right clicking on it in the Object Navigator and selecting "Program Unit Editor". The only advantage to performing validation here rather than in the AfterPform trigger is that on failure it will reopen the parameter form and highlight the offending parameter whereas the AfterPForm trigger does not know exactly why it has failed so only reopens the parameter form with no other hints. The disadvantage is that splitting up a bunch of factors to be considered before accepting the users parameters is less maintainable, especially where report failure may be due to the combination of parameters rather than a single setting. As a point of form, I prefer putting all parameter validation into the AfterPForm trigger to keep it all easy to find and easily accessible in it's entirety.


Built-In Packages:

           
There is one main package Oracle has included which provides many useful functions to the developer. This is the SRW package detailed in chapter 13 of the Reports 2.5 reference manual. It includes many functions to allow such things as DDL statements to alter the database which are normally not allowed within PL/SQL. It also includes a full set of exceptions for use by these DDL functions. One of the most useful procedures for debugging is the SRW.Message functions which will pop up a message window with up to three lines of text. Unfortunately, when run in batch mode as all of our reports are when run off the application menu, these messages are not displayed but are instead written to the report log. This means that it cannot be used to provide instant visual feedback or hints to the users.

            There are also Break and Trace procedures to help in debugging, as well as a few other handy tools for such things as changing visual attributes and format masks of fields during runtime, or for handling uncommon exceptions such as when a field overflows or is truncated. I strongly recommend reading the chapter on the SRW package to all new Reports developers.

 



Frequently Asked Questions:

I switched the page size to 11 × 8.5, but the printer still prints in portrait.

           
Even though you set the page size in the report properties, there is a another variable in the system parameters section under the data model in the object navigator called orientation. This sets the printer orientation. Oracle starts by setting it to "default" which means that no matter how you set the page size, the user's default printer setup will be used. You can also set it to either "Landscape" or "Portrait" to force the printer orientation no matter what the user has set as default. These sorts of picky, minor details are the ones which are invariably forgotten when you are designing your report and are the reason I created our two report templates, reptmp_p and reptmp_l (portrait and landscape). For anyone who wants a consistent look in their reports I strongly recommend building a similar pair to save yourself an ulcer, unless you actually like starting from scratch every time!?!

I moved this field into that repeating frame, but I'm still getting a "frequency below it's group" error.


            Moving fields around does not change what enclosing object is considered it's parent group. Oracle carefully remembers what  repeating frame a field was originally placed in and assigns that as it's parent. If you then reference a column further down the line of the query structure it will return that error. If you are not exactly sure which repeating frame a  field belongs to, try dragging it out of all of them. Whichever frame will not allow it to escape is it's parent. To change a field's parent, first click on the lock button on the speedbutton bar. It should now look like an unlocked padlock. Now all of the fields on the layout can be repositioned regardless of their original parent items. When you are satisfied with the repositioning click the lock button again to lock the layout. Oracle will parse the layout and assumes that any item fully enclosed in a repeating frame is a child object of that frame. This can be confirmed again by trying to drag an object out of it's parent. (Cntrl – Z or edit..undo will put it back where it came from)

Sometimes, for unknown and mysterious reasons, this method does not work. The alternative in this case is to highlight the field (or fields), cut it (cntrl-x), and then paste it into the desired frame. The paste does not initially set it into the right frame, but if you drag and drop it there before clicking on any other objects, and then click on something else, Oracle will usually figure what your intent was and assign the object(s) as a child of that frame. This is my preferred method of changing a field's parent as it works much more consistently then the unlock/lock method. One note though, if you are reassigning a group of fields, make sure the frame you are going to move them into is large enough to accept the whole group at once before you do the cut/paste. If you do the paste and then try to grow the frame to fit, you will have to cut and paste again. Once you de-select an object that has just been pasted, Oracle will assign it as a child of whatever it is in at the time.

If this technique also fails, you are probably going to have to delete and then recreate the objects within the desired frame. If the object has triggers attached, save yourself some typing by creating the new object in the right frame, copying over the trigger code, and then deleting the old object

I must put a repeating frame around these fields. How do I do this easily ?


Well congratulations, you have just discovered one of the main reasons why good planning goes a long way. Oracle looks at the layout as a sort of layered inheritance model such that anything created on top of and completely inside another object is by definition a child of that object. Creation order is therefor critical to the layout process. This means that placing a repeating frame on top of a field but larger than that field fails the ownership criteria. At best, if the new frame is fully enclosed within the same higher level frame as the field then the two will be considered sibling children of the higher level frame.

From this point you have two options. First, you can place the new repeating frame in the correct place and then use the techniques shown above in the "I moved this field but am still getting a frequency error" to reassign the fields into the new frame. There is also a second choice (which can also be used as a solution to the above). Go ahead and draw the new frame around the fields you want to have placed in it. Now if you try to click on one of the fields you will not be able to as they are fully covered by the new frame. Now go to the "Arrange" menu. You will find the options Send to back, bring to front, move forwards, move backwards. These are used to alter an object position in the Reports layer ordering. You use the "send backwards" option to move the frame backwards until all of the fields have popped to the front and are now enclosed in it. Oracle reassigns the new repeating frame as each object's parent as they pop to the front.

Note that you can only move an object back and forth amongst it's siblings. You cannot set it back below it's parent, nor in front of it's children. This means that once an object has popped to the front and had a reassignment of parent, you cannot move it back using these tools.

Why does part of a row sometimes get shifted to the next page, but not all of it?


This is due to the way the scan works when Oracle is parsing the layout. If the tops of all the fields in a row are aligned and the fields are all of the same height and font, they should all stay together. I suspect, however, that Reports bases it's decision on the printed size rather than the field size you define to determine which objects are too large and must be shifted to the next page. This means that even if you set two fields top-aligned with the same height and font but one of them is bolded, the bolded field could get shifted to the next page due to it's bigger footprint. The solution is to put the whole row into a regular frame which is page protected.

What exactly does the "Print Condition" do….         

           
The print condition type First, All, All but first, Last, All but last refer to the frequency with which you want to appear based upon the setting of the print condition object. A print condition object of Enclosing Object is whichever object encloses the current object (could be the parent or a frame within the parent), while Anchoring Object is the parent object (unless you have explicitly anchored the object in which case it is the object to which it is anchored). The key here is that this is about the pages on which the Print Condition Object appears, not the current object. Oracle views First as the first page on which any part of the Print Condition Object is printed, likewise Last is the last page on which any part of the Print Condition Object is printed. For objects inside a repeating frame, this condition is re-evaluated for each instance of the frame.

As an example, assume we have created a field inside a repeating frame with Print Condition Object set to 'anchoring object', and Print Condition Type set to 'All But First'.  On every instance of that repeating frame which is printed entirely within a single page, our object will not print. However, if an instance of that frame spans more than one page then our object will print on the second and every subsequent page that this instance of the repeating frame spans.
           
For most objects you will not have to play with this print condition setting as the default setting is pretty good at determining what pages to print on, even though it only chooses between 'first' and 'last'. Only such things as heading objects you want reprinted on multiple pages are normally candidates for fooling around with this setting.


How do I create a truly dynamic 'where' condition which the user can input on the parameter form for my select statement.


While setting a simple parameter for use in defining the select statement, such as a date, bill_period_id etc. is simple, there are times when you may wish to allow a user to add any "where" statement they wish. However, if you create a varchar user variable and try to reference it as an SQL condition ( e.g. Select * from account where :usercondition) you will get an error. The secret is that the variable must be initialized to a valid SQL condition before the Data Model will accept it. This is done in the "Initial Value" spot on the variable's properties form. The usual default is "1 = 1" which simply means all rows meeting whatever other conditions are included in the select statement will pass this condition if the user does not change it in the parameter form.


How do I change a user parameter at runtime from a layout object trigger?

Quite simply, you can't. Once the BeforeReport trigger has fired, Reports locks down the user parameters until the report is finished. Oh, I know you can put a statement into a layout trigger at design time and the compiler will accept it, but the moment you run the report you will get a nasty error and the report will die. Why they couldn't catch those problems at compile time I have no idea, except that it probably uses the same PL/SQL compiler as Forms which uses that same syntax for the perfectly acceptable function of changing field values.

That being said, there is valid technique to mimic having a user variable which can be changed over the course of the report execution. What you have to do is create a PL/SQL package that contains a variable as well as the functions to read and write to that variable. Since variables inside a package are both local to that package and persistent over the duration of the run, you use this to save and change your variable value. I know that this seems like overkill, but it is the most efficient way of handling an issue that is very rarely encountered. As you can probably guess, this technique is a last resort to finding an SQL work around if one exists.

How do I set the initial values of parameters for the parameter form at runtime?


This is what the BeforeForm trigger is primarily used for. Even if you have used a select statement to create a lookup list for the parameter, this statement is fully parsed before the parameter form is opened. Simply setting the parameter to a given value in the BeforeForm trigger will select that option as the default value displayed to the user. For example, assume you have a parameter called p_input_date which is intended to hold an invoice date. The following example will select the most recent invoice date as the default, and note that it properly handles exceptions to ensure that the report does not arbitrarily die if this default setting fails. Note also that like all report triggers, it must return a true or false value.

function BeforePForm return boolean is
begin
select max(bill_period_end_date + 1)
  into :p_input_date
  from billing_period
 where bill_period_end_date <= (select trunc(sysdate)
                                  from dual);
  return (TRUE);
exception
  when others then
     :p_input_date := null;
     return true;
end;

Why can't I highlight a bunch of fields and change all their format masks or print conditions at once?


You can. If you highlight a bunch of objects and then right click and select "properties..", Oracle gives you a stacked set of the individual properties forms for each of the selected objects. While this may be useful for some things, it requires changing values individually for each object. However, instead you can select the group of fields and then select "Common properties" from the "Tools" menu which will allow you to set the format mask , print conditions etc. for the whole set of objects at once.

 

How do I change the printed value of a field at runtime.


Triggers are intended to simply provide a true or false return value to determine whether an object should be printed. It is generally not allowed to change any values held in the cursor, make changes to the database, or change the value of it's objects value. That being said, there is a highly unpublicized method of doing just that using the SRW.Set_Field_Char procedure. The syntax is SRW.Set_Field_char(0,<newstring>) and the output of the object that the current trigger is attached to will be replaced by <newstring>. There are also SRW.set_fileld_num, and SRW.set_field_date for numeric or date fields.

While these options do work, they should only be used if a suitable NVL or DECODE statement in the original query is not possible as they are much, much slower to run. Also, note that this change of value only applies to the formatted output. It does not change the value held in the cursor and so can not be used for evaluating summary totals.


Give me your Feedback.......

No comments:

Post a Comment

Note: only a member of this blog may post a comment.