DBA Data[Home] [Help]

PACKAGE BODY: APPS.QA_BIS

Source


1 PACKAGE BODY QA_BIS AS
2 /* $Header: qltbisb.plb 115.19 2002/11/27 19:22:13 jezheng ship $ */
3 
4 
5     --
6     -- Create a mirror image of QA_RESULTS into QA_BIS_RESULTS for
7     -- BIS reporting purpose.  QA_BIS_RESULTS is also called a
8     -- Summary Table.  Not all columns of QA_RESULTS are selected.
9     -- Only those that BIS reports on will be included.
10     --
11     -- There are two methodologies for this mirroring process.
12     -- One is a complete rebuild where the summary table is first
13     -- deleted and truncated.  Then the results are transferred.
14     -- Another is by incremental refresh, where only the recently
15     -- inserted or updated records are transferred.
16     --
17     -- A last refresh time is kept in the table QA_BIS_UPDATE_HISTORY.
18     -- It is updated in both complete rebuild and incremental refresh.
19     --
20     -- **OBSOLETE**
21     -- The incremental refresh method does not take care of deletion.
22     -- This means deleted records in QA_RESULTS will appear in the
23     -- summary table, and consequently, in the BIS Report.  The current
24     -- strategy is to modify QLTRSMDF/QLTRSINF so that when a row is
25     -- deleted from direct data entry, the corresponding row from
26     -- QA_BIS_RESULTS will be deleted as well.
27     --
28     -- Change of status in QA_RESULTS, however, will be taken care of.
29     -- There is a status column in QA_RESULTS which can be set to 1
30     -- to indicate an invalid rows.  Those rows with status=1 will
31     -- not be included in the summary table.  When the status is
32     -- updated to other values, as long as the qa_last_update_date is
33     -- also correctly updated, this row will be included in the next
34     -- refresh (complete or incremental).  Changing the status of a
35     -- valid row to invalid (status=1) will also delete the row from
36     -- the summary table in the next refresh (complete or incremental).
37     --
38     -- Incremental refresh now takes care of deletion by using
39     -- QA_BIS_UPDATE_HISTORY as delete log.  When a record is deleted
40     -- from QA_RESUTLS, qa_bis.delete_log is called to record an audit
41     -- trail.
42     --
43     -- Author:  Bryan So (bso)
44     -- Contribution: Revathy Narasimhan (rnarasim)
45     --
46     -- Mon Jun 21 17:49:22 PDT 1999
47     --
48 
49     Incremental constant number := 1;
50     Complete constant number := 2;
51 
52     --
53     -- Maximum no. of rows inserted before a commit.  We have had
54     -- reports of performance problem when inserting a large no. of
55     -- records with a single commit at the end.  The problem happens
56     -- because of the immensely large rollback segment that needs to
57     -- be maintained.  Intermittent commit will solve the problem.
58     -- This variable will be initiated by the wrapper from a SRS param.
59     --
60     g_intermittent_commit number;
61 
62     --
63     -- Date of last refresh.  Could be null if never performed.
64     -- Initialized by wrapper.
65     --
66     g_last_refresh date;
67 
68     --
69     -- Current system date.  Initialized by the wrapper.
70     -- When performing incremental refresh of the BIS Summary Table,
71     -- all records updated *before* (but not at) this and *after or at*
72     -- last refresh datetime will be inserted.
73     --
74     g_current_datetime date;
75 
76     --
77     -- Standard who columns.
78     --
79     who_user_id                 constant number := fnd_global.conc_login_id;
80     who_request_id              constant number := fnd_global.conc_request_id;
81     who_last_update_login       constant number := fnd_global.conc_login_id;
82     who_program_id              constant number := fnd_global.conc_program_id;
83     who_program_application_id  constant number := fnd_global.prog_appl_id;
84 
85     --
86     -- schema information for DDL
87     --
88     qa_status   varchar2(1);
89     qa_industry varchar2(10);
90     qa_schema   varchar2(30);
91 
92 
93         --
94         -- Private Functions
95         --
96 
97         PROCEDURE truncate_summary_table IS
98         --
99         -- Delete the entire BIS Summary Table.  Done for complete rebuild.
100         --
101         BEGIN
102             --
103             -- Truncate is used for quicker performance and reuse of
104             -- space.  The reuse storage clause actually means do not
105             -- purge the storage but reserve it for the QBR table.
106             -- Since this command is followed by immediate inserts, this
107             -- would be a good option.
108             --
109             qlttrafb.exec_sql('truncate table ' ||
110                 qa_schema || '.QA_BIS_RESULTS reuse storage');
111 
112             --
113             -- Performance notes.  appsperf actually recommends an alternative
114             -- of dropping the table here.  Then the new table can be created:
115             -- create table as <select_stmt>.  This approach will not make
116             -- use of a rollback segment; therefore no need for intermittent
117             -- commits.
118             --
119             -- Problem is, there are many areas to pay attention to.  We have
120             -- to drop and create the table using ad_ddl.do_array_ddl calls.
121             -- We have to worry about recreating indices on qa_bis_results...
122             -- Not implemented yet.
123             -- bso
124             --
125         END truncate_summary_table;
126 
127 
128         PROCEDURE delete_update_history IS
129         --
130         -- Delete the audit trail in the update history table.  The audit
131         -- trail stores the primary key of the purged records (i.e. those
132         -- deleted from qa_results).  This procedure should be called only
133         -- by complete rebuild or by procedure delete_purged_rows.
134         --
135         BEGIN
136             DELETE
137             FROM   qa_bis_update_history
138             WHERE  occurrence >= 0 AND
139                    last_update_date < g_current_datetime;
140             commit;
141         END delete_update_history;
142 
143 
144         PROCEDURE delete_purged_rows IS
145         --
146         -- Delete those records that have been deleted from qa_results.
147         --
148         BEGIN
149             DELETE
150             FROM   qa_bis_results qbr
151             WHERE  qbr.occurrence IN
152                (SELECT h.occurrence
153                 FROM   qa_bis_update_history h
154                 WHERE  h.occurrence >= 0 AND
155                        h.last_update_date < g_current_datetime);
156             commit;
157             delete_update_history;
158         END delete_purged_rows;
159 
160 
161         PROCEDURE delete_updated_rows IS
162         --
163         -- Delete those rows in BIS Summary Table whose counterpart in
164         -- QA_RESULTS have been modified since last_refresh.
165         --
166         -- Notes on efficiency:
167         --   . QA_RESULTS must have either an index on qa_creation_date
168         --     or an index on qa_last_update_date (preferrable)
169         --
170         --   . QA_BIS_RESULTS must have a unique index on occurrence.
171         --
172         -- Notes on coding standard:
173         --   . Never use WHO columns to quality rows for processing.
174         --     Coding Standards R10SC p. 3-4.
175         --     Therefore, qa_last_update_date is used instead.
176         --
177         BEGIN
178             DELETE
179             FROM   qa_bis_results qbr
180             WHERE  occurrence IN (
181                 SELECT occurrence
182                 FROM   qa_results qr
183                 WHERE  qr.qa_last_update_date < g_current_datetime AND
184                        g_last_refresh BETWEEN
185                            qr.qa_creation_date AND qr.qa_last_update_date);
186             commit;
187 
188         END delete_updated_rows;
189 
190 
191         PROCEDURE construct_decode(alias varchar2, x_char_id number,
192             s in out NOCOPY varchar2) IS
193         --
194         -- Dynamically construct a decode function that decodes the
195         -- value for a softcoded collection element.  The decode
196         -- statement started at the end of s.
197         --
198         -- Performance notes: Will be more efficient if qa_plan_chars
199         -- has an index on char_id.
200         --
201             x_plan_id number;
202             x_result_column_name varchar2(30);
203             CURSOR c IS
204                 SELECT plan_id, result_column_name
205                 FROM   qa_plan_chars
206                 WHERE  char_id = x_char_id;
207 
208         --
209         -- Bug 1357601.  The decode statement used to "straighten" softcoded
210         -- elements into a single column has a sever limit of 255 parameters.
211         -- These variables are added to resolve the limit.  When the limit is
212         -- up, we use the very last parameter of the decode statement to
213         -- start a new decode, which can have another 255 params.  This is
214         -- repeated as necessary.
215         --
216         -- decode_count keeps the no. of decodes being used so far.
217         -- decode_param keeps the no. of parameters in the current decode.
218         -- decode_limit is the server limit.  This should be updated if
219         --    the server is enhanced in the future.
220         --
221         -- bso Thu Sep 21 13:11:19 PDT 2000
222         --
223         decode_count NUMBER;
224         decode_param NUMBER;
225         decode_limit CONSTANT NUMBER := 255;
226 
227         BEGIN
228             OPEN c;
229             FETCH c INTO x_plan_id, x_result_column_name;
230 
231             IF c%found THEN
232 
233                 s := s || 'decode(' || alias || '.plan_id';
234 
235                 decode_count := 1;
236                 decode_param := 1;
237 
238                 --
239                 -- Find <plan_id, result_column_name> pairs for all plans
240                 -- with x_char_id as collection element.
241                 --
242                 WHILE c%found LOOP
243 
244                     IF decode_param >= (decode_limit - 2) THEN
245                       s := s || ', decode(qr.plan_id';
246                       decode_count := decode_count + 1;
247                       decode_param := 1;
248                     END IF;
249 
250                     s := s || ', ' || to_char(x_plan_id) || ', ' ||
251                         alias || '.' || x_result_column_name;
252                     decode_param := decode_param + 2;
253 
254                     FETCH c INTO x_plan_id, x_result_column_name;
255                 END LOOP;
256                 CLOSE c;
257 
258                 FOR n IN 1 .. decode_count LOOP
259                     s := s || ')';
260                 END LOOP;
261             ELSE
262                 CLOSE c;
263                 s := s || 'null';  -- no such char_id, simply select null
264             END IF;
265         END construct_decode;
266 
267 
268         PROCEDURE construct_summary_table(s in out NOCOPY varchar2) IS
269         --
270         -- Construct a SQL statement that selects from QA_RESULTS and
271         -- format it so that the results can be directly inserted into
272         -- the BIS Summary Table after putting in WHO information.
273         --
274         -- The final sql statement looks like this:
275         --
276         -- SELECT
277         --     :w1 to :w9  standard who columns
278         --     qr.organization_id,
279         --     'decoded by view' organization_name,
280         --     qr.plan_id,
281         --     qr.collection_id,
282         --     qr.occurrence,
283         --     'decoded by view' plan_type_code,
284         --     'decoded by view' meaning,
285         --     'decoded by view' plan_name,
286         --     qr.item_id,
287         --     'decoded by view' item,
288         --     -1 lot_control_code,
289         --     qr.lot_number,
290         --     decode(qr.plan_id, 120, qr.CHARACTER3 ...) defect_code,
291         --     decode(qr.plan_id, 120, qr.CHARACTER2 ...) quantity_defective,
292         --     qr.qa_creation_date,
293         --     qr.qa_last_update_date
294         -- FROM
295         --     qa_results qr
296         -- WHERE
297         --     (qr.status is null or qr.status = 2)
298         --     and qr.qa_last_update_date < :today
299         --
300         -- bso
301         --
302             defect_code_char_id constant number := 100;
303             quantity_defective_char_id constant number := 101;
304         BEGIN
305             --
306             -- Construct the select clause
307             --
308             s := 'SELECT '||
309                 who_request_id || ',' ||
310                 who_program_application_id || ',' ||
311                 who_program_id || ',' ||
312                 'sysdate,' ||
313                 who_user_id || ',' ||
314                 'sysdate,' ||
315                 who_last_update_login || ',' ||
316                 who_user_id || ',' ||
317                 'sysdate,' ||
318                 'qr.organization_id,' ||
319                 '''decoded by view'' organization_name,' ||
320                 'qr.plan_id,' ||
321                 'qr.collection_id,' ||
322                 'qr.occurrence,' ||
323                 '''decoded by view'' plan_type_code,' ||
324                 '''decoded by view'' meaning,' ||
325                 '''decoded by view'' plan_name,' ||
326                 'qr.item_id,' ||
327                 '''decoded by view'' item,' ||
331             --
328                 '-1 lot_control_code,' ||
329                 'qr.lot_number,';
330 
332             -- The followings construct the dynamic select clauses
333             -- required for softcoded collection elements.
334             --
335             construct_decode('qr', defect_code_char_id, s);
336             s := s || ' defect_code,';
337 
338             construct_decode('qr', quantity_defective_char_id, s);
339             s := s || ' quantity_defective,' ||
340                 'qr.qa_creation_date,' ||
341                 'qr.qa_last_update_date ' ||
342                 'FROM qa_results qr ' ||
343                 'WHERE (qr.status is null or qr.status = 2) ' ||
344                 'and qr.qa_last_update_date < :today';
345 
346         END construct_summary_table;
347 
348 
349         PROCEDURE write_last_refresh_datetime(x_last_refresh_time date) IS
350         --
351         -- Write a refresh time to the QA_BIS_UPDATE_HISTORY table.
352         --
353         BEGIN
354             UPDATE qa_bis_update_history SET
355                 request_id = who_request_id,
356                 program_application_id = who_program_application_id,
357                 program_id = who_program_id,
358                 program_update_date = sysdate,
359                 last_update_login = who_last_update_login,
360                 last_updated_by = who_user_id,
361                 last_update_date = sysdate,
362                 last_refresh_time = x_last_refresh_time
363             WHERE occurrence = -1;  -- special record for refresh time.
364             commit;
365         END write_last_refresh_datetime;
366 
367 
368         PROCEDURE init_last_refresh_datetime IS
369         --
370         -- Initialize the QA_BIS_UPDATE_HISTORY table.
371         --
372         BEGIN
373             INSERT INTO qa_bis_update_history(
374                 request_id,
375                 program_application_id,
376                 program_id,
377                 program_update_date,
378                 created_by,
379                 creation_date,
380                 last_update_login,
381                 last_updated_by,
382                 last_update_date,
383                 occurrence,
384                 last_refresh_time
385             )
386             VALUES (
387                 who_request_id,                 -- request_id
388                 who_program_application_id,     -- program_application_id
389                 who_program_id,                 -- program_id
390                 sysdate,                        -- program_update_date
391                 who_user_id,                    -- created_by
392                 sysdate,                        -- creation_date
393                 who_last_update_login,          -- last_update_login
394                 who_user_id,                    -- last_updated_by
395                 sysdate,                        -- last_update_date
396                 -1,                             -- special flag
397                 sysdate);                       -- last_refresh_time
398             commit;
399         END init_last_refresh_datetime;
400 
401 
402         FUNCTION get_last_refresh_datetime RETURN date IS
403         --
404         -- Get last refresh time from the QA_BIS_UPDATE_HISTORY table
405         --
406             d date;
407             CURSOR c IS
408                 SELECT last_refresh_time
409                 FROM   qa_bis_update_history
410                 WHERE  occurrence = -1;
411                     -- special flag that indicates this is the record
412                     -- to look for refresh time
413         BEGIN
414             OPEN c;
415             FETCH c INTO d;
416             IF c%notfound THEN
417                 CLOSE c;
418                 init_last_refresh_datetime;
419                 RETURN null;
420             ELSE
421                 CLOSE c;
422                 RETURN d;
423             END IF;
424         END get_last_refresh_datetime;
425 
426 
427         PROCEDURE insert_summary_table(select_statement varchar2,
428             method number) IS
429         --
430         -- Insert into the summary table by selecting rows from the
431         -- select_statement.
432         --
433         -- See comments for construct_summary_table to find out what
434         -- columns are selected.
435         --
436         -- The summary table looks like this:
437         --
438         --      request_id              number
439         --      program_application_id  number
440         --      program_id              number
441         --      program_update_date     date
442         --      created_by              number
443         --      creation_date           date
444         --      last_update_login       number
445         --      last_updated_by         number
446         --      last_update_date        date
447         --      qa_creation_date        date            not null
448         --      qa_last_update_date     date            not null
449         --      organization_id         number          not null
450         --      organization_name       varchar2(60)    not null
451         --      plan_id                 number          not null
452         --      plan_name               varchar2(30)    not null
453         --      collection_id           number          not null
454         --      occurrence              number          not null    unique
455         --      plan_type_code          varchar2(30)
456         --      plan_type_meaning       varchar2(80)
457         --      item_id                 number
458         --      item                    varchar2(2000)
459         --      lot_control_code        number
460         --      lot_number              varchar2(30)
464         -- bso
461         --      defect_code             varchar2(150)
462         --      quantity_defective      varchar2(150)
463         --
465         --
466             insert_statement varchar2(32000);
467 
468         BEGIN
469 
470             --
471             -- The following defines the cursor for inserting data into
472             -- QA_BIS_RESULTS.
473             --
474             insert_statement := 'INSERT /*+ parallel (qb,default) append */ ' ||
475                 'INTO qa_bis_results qb(' ||
476                 'request_id,' ||
477                 'program_application_id,' ||
478                 'program_id,' ||
479                 'program_update_date,' ||
480                 'created_by,' ||
481                 'creation_date,' ||
482                 'last_update_login,' ||
483                 'last_updated_by,' ||
484                 'last_update_date,' ||
485                 'organization_id,' ||
486                 'organization_name,' ||
487                 'plan_id,' ||
488                 'collection_id,' ||
489                 'occurrence,' ||
490                 'plan_type_code,' ||
491                 'plan_type_meaning,' ||
492                 'plan_name,' ||
493                 'item_id,' ||
494                 'item,' ||
495                 'lot_control_code,' ||
496                 'lot_number,' ||
497                 'defect_code,' ||
498                 'quantity_defective,' ||
499                 'qa_creation_date,' ||
500                 'qa_last_update_date) ' || select_statement;
501 
502             IF method = Complete THEN
503                 EXECUTE IMMEDIATE insert_statement USING
504                     g_current_datetime;
505             ELSE
506                 EXECUTE IMMEDIATE insert_statement USING
507                     g_current_datetime,
508                     g_last_refresh;
509             END IF;
510 
511             COMMIT;
512 
513         END insert_summary_table;
514 
515 
516 
517     --
518     -- Main Entry Points
519     --
520 
521 
522 
523     FUNCTION complete_rebuild RETURN number IS
524     --
525     -- Completely rebuild the BIS Summary Table.  This involves deleting
526     -- the existing table, truncate it, and re-compute the records.
527     -- Return a SQL error code.  0 indicates no error.
528     -- bso
529     --
530         select_statement varchar2(30000);
531     BEGIN
532         truncate_summary_table;
533         delete_update_history;
534         construct_summary_table(select_statement);
535         insert_summary_table(select_statement, Complete);
536         write_last_refresh_datetime(g_current_datetime);
537 
538         RETURN 0;
539     END complete_rebuild;
540 
541 
542     FUNCTION incremental_rebuild RETURN number IS
543     --
544     -- Rebuild the BIS Summary Table by incremental refresh method.
545     -- The records in QA_RESULTS that have been modified since the
546     -- previous refresh datetime will be inserted into the BIS
547     -- Table.  Two kinds of records in QA_RESULTS will be processed,
548     -- those that are new and those that have been updated.
549     --
550     -- Notes on efficiency:
551     --   . QA_RESULTS should have an index on qa_last_update_date.
552     --
553     -- Return a SQL error code.  0 indicates no error.
554     -- bso
555     --
556         select_statement varchar2(30000);
557     BEGIN
558         IF g_last_refresh is null THEN
559             --
560             -- Exception.  The first time Rebuild is run, last_refresh
561             -- will be null.  Just do complete rebuild.
562             --
563             RETURN complete_rebuild;
564         ELSE
565             delete_purged_rows;
566             delete_updated_rows;
567             construct_summary_table(select_statement);
568             --
569             -- Add where clause to select only the new data.
570             --
571             select_statement := select_statement ||
572                 ' and qr.qa_last_update_date >= :refresh';
573             insert_summary_table(select_statement, Incremental);
574             write_last_refresh_datetime(g_current_datetime);
575             RETURN 0;
576         END IF;
577     END incremental_rebuild;
578 
579 
580 
581 
582     --
583     -- Public Functions
584     --
585 
586 
587 
588     procedure rebuild is
589     --
590     -- Debug routine for testing purpose only.
591     --
592         dummy boolean;
593         n number;
594     begin
595         dummy := fnd_installation.get_app_info('QA', qa_status,
596             qa_industry, qa_schema);
597         g_intermittent_commit := 1000;
598         g_current_datetime := qltdate.get_sysdate;
599         g_last_refresh := get_last_refresh_datetime;
600 
601         n := complete_rebuild;
602     end;
603 
604 
605     procedure refresh is
606     --
607     -- Debug routine for testing purpose only.
608     --
609         dummy boolean;
610         n number;
611     begin
612         dummy := fnd_installation.get_app_info('QA', qa_status,
613             qa_industry, qa_schema);
614         g_intermittent_commit := 1000;
615         g_current_datetime := qltdate.get_sysdate;
616         g_last_refresh := get_last_refresh_datetime;
617 
618         n := incremental_rebuild;
619     end;
620 
621 
622     PROCEDURE delete_log(x_occurrence number) IS
623     --
624     -- This procedure is used when a row is deleted from qa_results.
625     -- Called by QLTRES.Q_RES_PRIVATE.delete_row.
626     --
627     BEGIN
628         --
629         -- Insert a row into QA_BIS_UPDATE_HISTORY to log the deletion.
630         --
631 
632             INSERT INTO qa_bis_update_history(
633                 created_by,
634                 creation_date,
635                 last_update_login,
636                 last_updated_by,
637                 last_update_date,
638                 occurrence
639             )
640             VALUES (
641                 who_user_id,                    -- created_by
642                 sysdate,                        -- creation_date
643                 who_last_update_login,          -- last_update_login
644                 who_user_id,                    -- last_updated_by
645                 sysdate,                        -- last_update_date
646                 x_occurrence);
647 
648         -- Do not commit because the user may rollback their delete.
649         -- Wait until forms does it.
650 
651     END delete_log;
652 
653 
654     PROCEDURE WRAPPER(ERRBUF OUT NOCOPY VARCHAR2,
655                       RETCODE OUT NOCOPY NUMBER,
656                       ARGUMENT1 IN VARCHAR2,     -- Rebuild strategy
657                       ARGUMENT2 IN VARCHAR2) IS  -- # of rows between commits
658         method number := nvl(to_number(argument1), 1);
659         dummy boolean;
660     BEGIN
661 
662         dummy := fnd_installation.get_app_info('QA', qa_status,
663             qa_industry, qa_schema);
664 
665         --
666         -- Intermittent_commit specifies the no. of rows to process
667         -- between commits.  It is here for performance purpose.  If
668         -- there is a large no. of rows to process, the rollback segment
669         -- becomes increasingly large and drags down performance.
670         --
671         -- OBSOLETE.  The parallel and append database hints do much
672         -- better than intermittent commit.
673         -- bso Mon May 21 18:26:38 PDT 2001
674         g_intermittent_commit := nvl(to_number(argument2), 1000);
675 
676         --
677         -- Get current time and last refresh time.  If last refresh is
678         -- null, then a complete rebuild will be performed.  This usually
679         -- means this is the first time the summary table is being used.
680         --
681         -- For incremental rebuild:
682         --
683         -- Delete all rows from QA_BIS_RESULTS that satisfy this
684         --
685         --     qr.qa_creation_date <= last_refresh <= qr.qa_last_update_date
686         --        and qr.qa_last_update_date < current_date
687         --
688         --        The last condition is needed because we will be writing
689         --        current_date to the QA_BIS_UPDATE_HISTORY table as last
690         --        refresh time.  All rows updated at or after current_date
691         --        will be excluded in this rebuild.  They will be mirrored
692         --        in the next rebulid.
693         --
694         -- Then mirror all rows in QA_RESULTS that satify this
695         --
696         --     last_refresh <= qr.qa_last_update_date < current_date
697         --
698         g_current_datetime := qltdate.get_sysdate;
699         g_last_refresh := get_last_refresh_datetime;
700 
701         IF method = Incremental THEN
702             retcode := incremental_rebuild;
703         ELSIF method = Complete THEN
704             retcode := complete_rebuild;
705         ELSE
706             retcode := 2;
707         END IF;
708 
709         errbuf := '';
710     END;
711 
712 
713 END QA_BIS;
714