Saturday, June 21, 2014

Why is my MV Query Rewrite not happening?

You have designed the model and implemented the dimensions and fact tables. Data is clean. You have implemented all the required PK-FK relationships, created required indexes, gathered statistics and built the required materialized views.

The analytic design team have completed report and issues SQL query! Oops...
You expected the response time of < 10 seconds, instead the front end is clocking for over 4 minutes, now.

You quickly investigate the active sessions in Oracle and see the BI query is performing FTS on your largest FACT table. Why is it not using the Materialized View you so painstakingly built? Why is Query Rewrite not happening?

DBMS_MVIEW.EXPLAIN_REWRITE comes to rescue!

Conditions that must be satisfied for MV Query Rewrite to occur

The conditions under which Oracle will Rewrite queries using MV are well documented here.
In summary following techniques are used for MV query rewrite.
In addition if your MV is not fresh (not expoected to be due to batch or on-demand rebuild) then it is required to set QUERY_REWRITE_INTEGRITY at session (or system level) to TRUSTED or STALE_TOLERATED.

When the MV is not used for Query Rewrite, how do we find out WHY? 
We'll use DBMS_MVIEW.EXPLAIN_REWRITE.
Script for building dimension and fact tables are at the end of document. Borrowed from ASKTOM site.

Based on the setup and MV creation following query does NOT rewrite. Let us check WHY?
We'll create REWRITE_TABLE.

CREATE TABLE REWRITE_TABLE(
  statement_id          VARCHAR2(30),   -- id for the query
  mv_owner              VARCHAR2(30),   -- owner of the MV
  mv_name               VARCHAR2(30),   -- name of the MV
  sequence              INTEGER,        -- sequence no of the msg
  query                 VARCHAR2(2000), -- user query
  query_block_no        INTEGER,        -- block no of the current subquery
  rewritten_txt         VARCHAR2(2000), -- rewritten query
  message               VARCHAR2(512),  -- EXPLAIN_REWRITE msg
  pass                  VARCHAR2(3),    -- rewrite pass no
  mv_in_msg             VARCHAR2(30),   -- MV in current message 
  measure_in_msg        VARCHAR2(30),   -- Measure in current message 
  join_back_tbl         VARCHAR2(30),   -- Join back table in message 
  join_back_col         VARCHAR2(30),   -- Join back column in message
  original_cost         INTEGER,        -- Cost of original query
  rewritten_cost        INTEGER,        -- Cost of rewritten query
  flags                 INTEGER,        -- associated flags
  reserved1             INTEGER,        -- currently not used 
  reerved2              VARCHAR2(10));   -- currently not used;
  
truncate table REWRITE_TABLE;  

DECLARE
  qrytext VARCHAR2(500)  :='  select /*+ REWRITE_OR_ERROR */  time_hierarchy.qtr_yyyy, sum(sales_amount)
       from sales, time_hierarchy
      where sales.trans_date = time_hierarchy.day
      group by time_hierarchy.qtr_yyyy';
    idno    VARCHAR2(30) :='ID1';
BEGIN
  DBMS_MVIEW.EXPLAIN_REWRITE(qrytext, '', idno);
END;
/
SELECT message FROM rewrite_table ORDER BY sequence;

QSM-01150: query did not rewrite    
QSM-01082: Joining materialized view, SALES_MV, with table, TIME_HIERARCHY, not possible  
QSM-01102: materialized view, SALES_MV, requires join back to table, TIME_HIERARCHY, on column, QTR_YYYY    

APPENDIX A - Scripts

Set up for data :
set autotrace traceonly statistics;
set timing on;

drop table sales purge;

create table sales
    (trans_date date, cust_id int, sales_amount number );


insert /*+ APPEND */ into sales
    select trunc(sysdate,'year')+mod(rownum,366) TRANS_DATE,
            mod(rownum,100) CUST_ID,
            abs(dbms_random.random)/100 SALES_AMOUNT
      from all_objects;
commit;

begin
   for i in 1 .. 4
   loop
       insert /*+ APPEND */ into sales
       select trans_date, cust_id, abs(dbms_random.random)/100 
         from sales;
       commit;
   end loop;
end;
/

select count(*) from sales;

drop table time_hierarchy;
create table time_hierarchy
(day, mmyyyy, mon_yyyy, qtr_yyyy, yyyy)
as
select distinct
  trans_date    DAY,
  cast (to_char(trans_date,'yyyymm') as number) MMYYYY,
  to_char(trans_date,'mon-yyyy') MON_YYYY,
  'Q' || ceil( to_char(trans_date,'mm')/3) || ' FY'
      || to_char(trans_date,'yyyy') QTR_YYYY,
  cast( to_char( trans_date, 'yyyy' ) as number ) YYYY
 from sales;

exec dbms_stats.gather_table_stats( user, 'SALES', cascade=>true );
exec dbms_stats.gather_table_stats( user, 'TIME_HIERARCHY', cascade=>true );
exec dbms_stats.gather_table_stats( user, 'SALES_MV', cascade=>true );

drop materialized view sales_mv;
create materialized view sales_mv
   build immediate
   refresh on demand
   enable query rewrite
   as
   select sales.cust_id, sum(sales.sales_amount) sales_amount,
          time_hierarchy.mmyyyy
     from sales, time_hierarchy
    where sales.trans_date = time_hierarchy.day
    group by sales.cust_id, time_hierarchy.mmyyyy;

Check Query Rewrite:
select name, value from v$parameter
where name in ('query_rewrite_enabled','query_rewrite_integrity');

alter session set query_rewrite_enabled = TRUE ;
alter session set query_rewrite_integrity = TRUSTED;

alter session set query_rewrite_integrity = STALE_TOLERATED;

Saturday, May 31, 2014

Installing ADF Faces Components Demo

In this post the we’ll walk through the steps for downloading ADF Faces Components Demo in Jdeveloper environment. I am using Jdeveloper version 11.1.1.6.
You need OTN account (free account) for downloading the WAR file.
Access the downloadable WAR file (rcf-dvt-demo.war) from OTN which can be accessed here.
This is a WAR file containing a comprehensive demo of all the components as well as various framework features. The WAR file contains the source code for the demo.


The rich client component runtime demo can be started from inside Oracle JDeveloper 11 using the integrated Oracle Weblogic Server.
Create a directory under C:\Jdeveloper\mywork directory. This is the directory where Jdeveloper create/stores all applications by default.
Depending on file associations in your browser on your PC, this file may download as ZIP file. Just rename the file to .war extension from .zip.

Start Oracle JDeveloper and choose File | New from the menu. Select Applications and press Ok.
In the Create Application dialog type adffacesdemo as the application name and press Finish.

This creates new application workspace and project. The project is not needed and you may delete the project, choosing File | Delete Project file from disk in the Oracle JDeveloper 11g menu.
Choose File | New from the JDeveloper menu and select Projects.
In the list of items, select Project from WAR File and press Ok.

Skip the first dialog panel, and provide a name for the project, e.g. adffacesdemo, on the second.
Keep the Directory information .
On the third panel, use the file browser to select the ADF Faces demo WAR file on your file system and finish the wizard.
This step will take 1 or 2 minutes…
Save the application.
Double click onto the project node to open the project properties and select the Run | Debug | Profile option.
Press the Edit button and select the Tool Settings. In the Before Running section, uncheck the Make Project option and close the dialog pressing Ok.
Expand the project and select index.jspx under the Web Content node and Choose Run from the right mouse context menu.
The Face DVT Demo application will open up in your default browser.

Good Luck.

Assigning primary key value using Oracle Sequence

Many applications define tables with primary key which is populated using values from database sequence. There are several posts on the net that describe these techniques with some links listed below as references. In ADF applications there are several approaches to implement assignment of primary key values.
1. Using Groovy Expression in Entity class.
2. Overriding the create method of the Entity class.
3. Using database trigger.

1. Assign Primary Key values using ADF Entity level using Groovy Expression
SequenceImpl class is used in groovy expression of the key attribute of the entity. getSequenceNumber() method of the SequenceImpl class can be invoked using the database sequence.
In the Entity or the updatable View Object editor the following expression can be set in the properties of the key attribute as shown below.

(new oracle.jbo.server.SequenceImpl("T1_SEQ",object.getDBTransaction())).getSequenceNumber()


Save the application and to test run the application module. Let us add a new row and notice that next sequence value is retrieved.


1. Assign Primary Key values by overriding create method of the entity class
To override the create method of the entity class, generate  the java Accessors  (source code)  as shown below.


Once the java class is created modify the create method with following code.

    @Override
    protected void create(AttributeList attributeList) {
        super.create(attributeList);
        SequenceImpl seq = new SequenceImpl("T2_SEQ", getDBTransaction());
        Integer seqNextval;
        seqNextval = seq.getSequenceNumber().intValue();
        setT2Id(seqNextval);
    }

Save the application and to test run the application module. Let us add a new row and notice that next sequence value is retrieved.


3. Assign Primary Key values by using database trigger
Use the database navigator to access the table, right-click on the table and select Create (PK from Sequence…) option.



Once a trigger is created, change the attribute type of the primary key column in the entity class to DBSequence.



Save the application and to test run the application module. Let us add a new row and notice that next sequence value is retrieved.
Note that a negative value is assigned until the actual INSERT is executed on the table in the database, which fires the trigger to assign new sequence value. This negative value is used to manage the entity relationships in the framework until changes are saved to the database. Once the changes are saved the framework will refresh the trigger assigned key value.


Till Next post  Bye.

Reference:

SQL Developer 4.0 - "Streams has already been closed"!

This error in SQL Developer (4.0) appears when you execute an SQL in SQL Worksheet with some errors. Any further execution of the SQL results in same error.

Once you correct the SQL and rerun the SQL you get the same error…


Any further execution of the SQL gives the same error. However if you “refresh” the result set SQL developer re-executes the SQL properly and returns he result.


SQL developer 4.0 has lots of new functionality and performance better.

Happy exploring…