Friday, October 23, 2015

LISTAGG Function

SELECT deptno ,LISTAGG(ename, ',') WITHIN GROUP (
ORDER BY ename) AS employees
FROM emp
GROUP BY deptno;

 DEPTNO   EMPLOYEES
--------  ---------------
    10   CLARK,KING,MILLER
    20   ADAMS,FORD,JONES,SCOTT,SMITH,SMITH
    30   ALLEN,ALLEN,BLAKE,JAMES,MARTIN,TURNER,WARD,WARD

Friday, September 18, 2015

Mutating table/trigger error and how to resolve it

A good article on Mutating Table/Trigger and how to avoid this using Compound Triggers. Click Here or continue reading below.

Most of us who have worked in Oracle have encountered ORA-04091 (table xxx is mutating. Trigger/function might not see it) at some time or the other during the development process.  In this blog post, we will cover why this error occurs and how we can resolve it using different methodology.

Mutating error normally occurs when we are performing some DML operations and we are trying to select the affected record from the same trigger. So basically we are trying to select records in the trigger from the table that owns the trigger. This creates inconsistency and Oracle throws a mutating error. Let us take a simple scenario in which we have to know total number of invalid objects after any object status is updated to ‘INVALID’. We will see it with an example. First let us create a table and then trigger.

SQL> CREATE TABLE TEST
2  AS SELECT * FROM USER_OBJECTS;
Table created.
CREATE OR REPLACE TRIGGER TUA_TEST
AFTER UPDATE OF STATUS ON TEST
FOR EACH ROW
DECLARE
v_Count NUMBER;
BEGIN
SELECT count(*)
INTO v_count
FROM TEST
WHERE status = ‘INVALID’;
dbms_output.put_line(‘Total Invalid Objects are ‘ || v_count);
END;
/
Now if we try to change the status of any object to ‘INVALID’, we will run into mutating error as we are trying to update the record and trigger is trying to select total number of records in ‘INVALID’ status from the same table.

SQL> update test
2  set status = 'INVALID'
3  where object_name = 'TEST1';
update test
*
ERROR at line 1:
ORA-04091: table SCOTT.TEST is mutating, trigger/function may not see it

Having said that there are different ways we can handle mutating table errors. Let us start taking one by one scenario.
First one is to create statement level trigger instead of row level. If we omit the ‘for each row’ clause from above trigger, it will become statement level trigger. Let us create a new statement level trigger.

CREATE OR REPLACE TRIGGER TUA_TEST
AFTER UPDATE OF STATUS ON TEST
DECLARE
v_Count NUMBER;
BEGIN
SELECT count(*)
INTO v_count
FROM TEST
WHERE status = ‘INVALID’;
dbms_output.put_line(‘Total Invalid Objects are ‘ || v_count);
END;
Now let us fire the same update statement again.

SQL> UPDATE TEST
2     SET status = 'INVALID'
3   WHERE object_name = 'TEST1';
Total Invalid Objects are 6
1 row updated.
When we defined statement level trigger, update went through fine and it displayed the total number of invalid objects.
Why this is a problem when we are using ‘FOR EACH ROW’ clause? As per Oracle documentation, the session, which issues a triggering statement on the table, cannot query the same table so that trigger cannot see inconsistent data. This restriction applies to all the row level triggers and hence we run into mutating table error.
Second way of dealing with the mutating table issue is to declare row level trigger as an autonomous transaction so that it is not in the same scope of the session issuing DML statement. Following is the row level trigger defined as pragma autonomous transaction.

CREATE OR REPLACE TRIGGER TUA_TEST
AFTER UPDATE OF STATUS ON TEST
FOR EACH ROW
DECLARE
PRAGMA AUTONOMOUS_TRANSACTION;
v_Count NUMBER;
BEGIN
SELECT count(*)
INTO v_count
FROM TEST
WHERE status = ‘INVALID’;
dbms_output.put_line(‘Total Invalid Objects are ‘ || v_count);
END;
Now let is issue the update statement again and observe the results.

SQL> UPDATE TEST
2     SET status = 'INVALID'
3   WHERE object_name = 'TEST1';
Total Invalid Objects are 5
1 row updated.
If you closely look at the output, you will see only 5 objects shown in invalid status while statement level trigger showed 6 objects in invalid status. Let us try to update multiple objects at the same time.

SQL> UPDATE TEST
2     SET status = 'INVALID'
3   WHERE object_name IN ('T1','T2');

Total Invalid Objects are 6
Total Invalid Objects are 6
2 rows updated.
By defining row level trigger as an autonomous transaction, we got rid of mutating table error but result is not correct. The latest updates are not getting reflected in our result set as oppose to statement level trigger. So one has to be very careful when using this approach.
In version 11g, Oracle made it much easier with introduction of compound triggers. We have covered compound triggers in a previous blog post. Let us see in this case how a compound trigger can resolve mutating table error. Let’s create a compound trigger first:

CREATE OR REPLACE TRIGGER TEST_TRIG_COMPOUND
FOR UPDATE
ON TEST
COMPOUND TRIGGER
/* Declaration Section*/
v_count NUMBER;
AFTER EACH ROW IS
BEGIN
dbms_output.put_line(‘Update is done’);
END AFTER EACH ROW;
AFTER STATEMENT IS
BEGIN
SELECT count(*)
INTO v_count
FROM TEST
WHERE status = ‘INVALID’;
dbms_output.put_line(‘Total Invalid Objects are ‘ || v_count);
END AFTER STATEMENT;
END TEST_TRIG_COMPOUND;
/
Now let us check how many objects are invalid in the test table.

SQL> select count(*) from test where status = 'INVALID';

COUNT(*)
———-
6
Here is the update statement followed by an output.

SQL> UPDATE TEST
2     SET status = 'INVALID'
3   WHERE object_name = 'T2';

Update is done
Total Invalid Objects are 7
1 row updated.
Here we get correct result without getting mutating table error. This is also one very good advantage of compound triggers. There are other ways also to resolve mutating table error using temporary tables but we have discussed common ones in this blog post.

Tuesday, September 8, 2015

Monday, September 7, 2015

Master-Detail Relationship in Oracle forms Blocks

First create Master data Block , then detail data block.

From HR schema  : Master Data Block is Departments
                           and Detail Data Block is Employees.

Layout Style 'Form' for Departments Block, with 1 record displayed.
Layout Style 'Tabular' for Employees Block, with 5 record displayed.

Setting Relations: ( During Layout Wizard) 







And likely below ON-POPULATE-DETAILS and ON-CHECK-DELETE-MASTER 
triggers gets automatically generated under the master data block.




























If we change the Delete Record Behaviour property , the triggers will automatically change 
accordingly.

1. Non-Isolated :( Triggers are automatically generated under the master data block)
a) ON-POPULATE-DETAILS
b) ON-CHECK-DELETE-MASTER

2. Cascading :( Triggers are automatically generated under the master data block)
a) ON-POPULATE-DETAILS
b) PRE-DELETE 

3. Isolated :( Triggers are automatically generated under the master data block)
a) ON-POPULATE-DETAILS


What does the above three properties mean basically ?

Non-Isolated : Prevents deletion of master record if detail record is present.
Cascading : Deletes the detail record once master record is deleted.
Isolated : Only deletes the master record.



Challa.

Tuesday, July 14, 2015

Link between OE_ORDER_HEADERS_ALL and RA_CUSTOMER_TRX_ALL

RA_CUSTOMER_TRX_ALL will have header information

INTERFACE_HEADER_CONTEXT = 'ORDER ENTRY'
INTERFACE_HEADER_ATTRIBUTE1 = ORDER_NUMBER

RA_CUSTOMER_TRX_LINES_ALL will have invoice lines

INTERFACE_LINE_CONTEXT = 'ORDER ENTRY'
INTERFACE_LINE_ATTRIBUTE1 = ORDER_NUMBER
INTERFACE_LINE_ATTRIBUTE6 = OE_ORDER_LINES_ALL.LINE_ID
INTERFACE_LINE_ATTRIBUTE3 = WSH_NEW_DELIVERIES.DELIVERY_ID

Monday, May 4, 2015

API to change the password of an Application User

Use the below code to change the password for all the application users. I have used this to change passwords of all application users who has been created after 01-Jan-2015.

To change password for single user remove the for loop and un-comment the local variables. Amend as per your requirement.

DECLARE
--v_user_name varchar2(30):=upper('&Enter_User_Name');
--v_new_password varchar2(30):='&Enter_New_Password';
   v_status   BOOLEAN;

   CURSOR c1
   IS
      SELECT user_name
        FROM fnd_user
       WHERE TRUNC (creation_date) > '01-Jan-2015';

BEGIN
   FOR i IN c1
   LOOP
      v_status :=
         fnd_user_pkg.changepassword (username         => i.user_name, --v_user_name
                                      newpassword      => 'newpassword' --v_new_password
                                     );

      IF v_status = TRUE
      THEN
         DBMS_OUTPUT.put_line
                          (   'The password reset successfully for the User:'
                           || i.user_name
                          );
         COMMIT;
      ELSE
         DBMS_OUTPUT.put_line (   'Unable to reset password due to'
                               || SQLCODE
                               || ' '
                               || SUBSTR (SQLERRM, 1, 100)
                              );
         ROLLBACK;

      END IF;

   END LOOP;

END;

Thursday, April 9, 2015

How to get Concurrent Program Output file and Log file from Database.

This post will guide you how to get concurrent program log files and output files from database. As you are aware whenever you submit concurrent program , after successful completion of it, Two files will be generated  which are nothing but LOG files and OUTPUT files. We can get those files by calling function  FND_WEBFILE.GET_URL

This function will return output value which is nothing but the  Link for the Log file or Output File , So only thing we need to do is to just paste the link in browser to view Log File or Output file. During this activity you don't need to be login on to the Oracle Application Window

Below code will help you out in Generating Log file , Pass request id  as  input parameter'

SET SERVEROUTPUT ON

DECLARE
   l_request_id   NUMBER          := :p_req_id;             -- The request id
   l_two_task     VARCHAR2 (256);
   l_gwyuid       VARCHAR2 (256);
   l_url          VARCHAR2 (1024);
BEGIN
   -- Get the value of the profile option named, Gateway User ID (GWYUID)
   --- l_gwyuid := fnd_profile.VALUE ('APPLSYSPUB/PUB');
   SELECT profile_option_value
     INTO l_gwyuid
     FROM fnd_profile_options o, fnd_profile_option_values ov
    WHERE profile_option_name = 'GWYUID'
      AND o.application_id = ov.application_id
      AND o.profile_option_id = ov.profile_option_id;

   -- Get the value of the profile option named, Two Task(TWO_TASK)
   SELECT profile_option_value
     INTO l_two_task
     FROM fnd_profile_options o, fnd_profile_option_values ov
    WHERE profile_option_name = 'TWO_TASK'
      AND o.application_id = ov.application_id
      AND o.profile_option_id = ov.profile_option_id;

   l_url :=
      fnd_webfile.get_url
         (file_type        => fnd_webfile.request_log,
                          -- for log file. Use request_out to view output file
          ID               => l_request_id,
          gwyuid           => l_gwyuid,
          two_task         => l_two_task,
          expire_time      => 500                       -- minutes, security!.
         );
   DBMS_OUTPUT.put_line (l_url);
END;

Output will be one link , Just paste the output in Browser and you will be able to view Log file output.


Below code will help you out in Generating Out file for the specific Concurrent request , Pass request id as input parameter

SET SERVEROUTPUT ON

DECLARE
   l_request_id   NUMBER := :P_REQ_ID;                       -- The request id
   l_two_task     VARCHAR2 (256);
   l_gwyuid       VARCHAR2 (256);
   l_url          VARCHAR2 (1024);
BEGIN
   -- Get the value of the profile option named, Gateway User ID (GWYUID)
   --- l_gwyuid := fnd_profile.VALUE ('APPLSYSPUB/PUB');

   SELECT   profile_option_value
     INTO   l_gwyuid
     FROM   fnd_profile_options o, fnd_profile_option_values ov
    WHERE       profile_option_name = 'GWYUID'
            AND o.application_id = ov.application_id
            AND o.profile_option_id = ov.profile_option_id;


   -- Get the value of the profile option named, Two Task(TWO_TASK)

   SELECT   profile_option_value
     INTO   l_two_task
     FROM   fnd_profile_options o, fnd_profile_option_values ov
    WHERE       profile_option_name = 'TWO_TASK'
            AND o.application_id = ov.application_id
            AND o.profile_option_id = ov.profile_option_id;


   l_url :=
      fnd_webfile.get_url (file_type     => fnd_webfile.request_out, -- for out file
                           ID            => l_request_id,
                           gwyuid        => l_gwyuid,
                           two_task      => l_two_task,
                           expire_time   => 500-- minutes, security!.
                           );

   DBMS_OUTPUT.put_line (l_url);
END;

Output will be one link , Just paste the output in Browser and you will be able to view output file .

Sample Api Code For Ar_Receipt_Api_Pub.Apply

DECLARE
   l_return_status   VARCHAR2 (1);
   l_msg_count       NUMBER;
   l_msg_data        VARCHAR2 (240);
   l_count           NUMBER;
   l_msg_data_out    VARCHAR2 (240);
   l_mesg            VARCHAR2 (240);
   p_count           NUMBER;
   l_rec_num         VARCHAR2 (100);
   l_trx_num         VARCHAR2 (100);
BEGIN
   mo_global.set_policy_context ('S', 82);                    -- your org id
   mo_global.init ('AR');
   ar_receipt_api_pub.APPLY
                           (p_api_version           => 1.0,
                            p_init_msg_list         => fnd_api.g_true,
                            p_commit                => fnd_api.g_true,
                            p_validation_level      => fnd_api.g_valid_level_full,
                            p_cash_receipt_id       => 98654,
                                                        ---
                            p_customer_trx_id       => 87654,
                                                         ----
                            p_org_id                => 82,        -->
                            x_return_status         => l_return_status,
                            x_msg_count             => l_msg_count,
                            x_msg_data              => l_msg_data
                           );
   DBMS_OUTPUT.put_line ('Status ' || l_return_status);
   DBMS_OUTPUT.put_line ('Message count ' || l_msg_count);

   IF l_msg_count = 1
   THEN
      DBMS_OUTPUT.put_line ('l_msg_data ' || l_msg_data);
   ELSIF l_msg_count > 1
   THEN
      LOOP
         p_count := p_count + 1;
         l_msg_data := fnd_msg_pub.get (fnd_msg_pub.g_next, fnd_api.g_false);

         IF l_msg_data IS NULL
         THEN
            EXIT;
         END IF;

         DBMS_OUTPUT.put_line ('Message' || p_count || '.' || l_msg_data);
      END LOOP;
   END IF;
END;