Showing posts with label Model. Show all posts
Showing posts with label Model. Show all posts

Monday, 21 September 2009

Data Densification - filling dates example

Many years ago when I was first learning Oracle (in the deep end) I came by the problem of data densification. I can't remember how I solved the problem back then, but I've encountered it a few times since. When writing my presentation for the model clause, I explored the difference between two options. Recently whilst investigating syntax definitions for the ANSI standard outer joins, I encountered another solution called a partitioned outer join - so I thought I'd compare all three for performance and readability.

The scenario is thus - I have an amount in a table for various dates throughout the year, for a particular person.
NAME          AMT DT
------ ---------- -----------
Scott         117 11-jan-2009
Scott         250 17-feb-2009
Scott         300 14-apr-2009
Scott          50 06-jun-2009
Wade         1231 17-mar-2009
Wade         2321 22-apr-2009
Wade         3122 30-sep-2009
Wade           59 31-oct-2009
However in my report I wish all months of the year displayed for the person, regardless of whether amounts exist. Let's add a cumulative figure in there for good measure. Included below this is the SQL for my test case.
NAME   MTH              AMT    CUM_AMT
------ --------- ---------- ----------
Scott  January          117        117
Scott  February         250        367
Scott  March              0        367
Scott  April            300        667
Scott  May                0        667
Scott  June              50        717
Scott  July               0        717
Scott  August             0        717
Scott  September          0        717
Scott  October            0        717
Scott  November           0        717
Scott  December           0        717
Wade   January            0          0
Wade   February           0          0
Wade   March           1231       1231
Wade   April           2321       3552
Wade   May                0       3552
Wade   June               0       3552
Wade   July               0       3552
Wade   August             0       3552
Wade   September       3122       6674
Wade   October           59       6733
Wade   November           0       6733
Wade   December           0       6733

DROP TABLE customer;
CREATE TABLE customer (
   name  VARCHAR2(10)
  ,amt   NUMBER
  ,dt    DATE
);

insert into customer values('Scott',117,  to_date('11-01-2009','DD-MM-YYYY'));
insert into customer values('Scott',250,  to_date('17-02-2009','DD-MM-YYYY'));
insert into customer values('Scott',300,  to_date('14-04-2009','DD-MM-YYYY'));
insert into customer values('Scott',50,   to_date('06-06-2009','DD-MM-YYYY'));
insert into customer values('Wade',1231,  to_date('17-03-2009','DD-MM-YYYY'));
insert into customer values('Wade',2321,  to_date('22-04-2009','DD-MM-YYYY'));
insert into customer values('Wade',3122,  to_date('30-09-2009','DD-MM-YYYY'));
insert into customer values('Wade',59,    to_date('31-10-2009','DD-MM-YYYY'));
This is a pre-10g solution, using an efficient method on the DUAL table to conjure records. I also use analytics to determine the cumulative amount. There is however a cartesion join and two full table scans on the customer table.
SELECT date_fill.name
      ,TO_CHAR(real_dt,'Month') mth
      ,NVL(amt,0) amt
      ,NVL(SUM(amt) OVER (PARTITION BY date_fill.name
                          ORDER BY real_dt ),0) cum_amt
FROM
  (SELECT name, TRUNC(dt,'mm') dt, SUM(amt) amt
   FROM   customer
   GROUP BY name, TRUNC(dt,'mm')
   ) actual_data -- Actual data
  ,(SELECT name, real_dt
    FROM  (SELECT DISTINCT name
           FROM   customer)
         ,(WITH mths AS (SELECT TRUNC(SYSDATE,'YYYY') real_dt
                         FROM DUAL CONNECT BY LEVEL <= 12)
           SELECT ADD_MONTHS(real_dt,ROWNUM-1) real_dt FROM mths)
   ) date_fill -- Distinct list with conjured dates
-- Outer join actual data with full date list
WHERE date_fill.real_dt = actual_data.dt(+)
AND   date_fill.name    = actual_data.name(+)
ORDER BY date_fill.name, date_fill.real_dt;

-------------------------------------------------------------------------------------
| Id  | Operation                           | Name     | Rows  | Bytes | Cost (%CPU)|
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |          |     2 |    74 |    11  (28)|
|   1 |  WINDOW SORT                        |          |     2 |    74 |    11  (28)|
|*  2 |   HASH JOIN OUTER                   |          |     2 |    74 |    11  (28)|
|   3 |    VIEW                             |          |     2 |    24 |     6  (17)|
|   4 |     MERGE JOIN CARTESIAN            |          |     2 |    24 |     6  (17)|
|   5 |      VIEW                           |          |     1 |     6 |     2   (0)|
|   6 |       COUNT                         |          |       |       |            |
|   7 |        VIEW                         |          |     1 |     6 |     2   (0)|
|   8 |         CONNECT BY WITHOUT FILTERING|          |       |       |            |
|   9 |          FAST DUAL                  |          |     1 |       |     2   (0)|
|  10 |      BUFFER SORT                    |          |     2 |    12 |     6  (17)|
|  11 |       VIEW                          |          |     2 |    12 |     4  (25)|
|  12 |        HASH UNIQUE                  |          |     2 |    12 |     4  (25)|
|  13 |         TABLE ACCESS FULL           | CUSTOMER |     8 |    48 |     3   (0)|
|  14 |    VIEW                             |          |     8 |   200 |     4  (25)|
|  15 |     HASH GROUP BY                   |          |     8 |   136 |     4  (25)|
|  16 |      TABLE ACCESS FULL              | CUSTOMER |     8 |   136 |     3   (0)|
-------------------------------------------------------------------------------------
Here is what some would consider a more elegant solution using the model clause. The explain plan is far neater, and the performance was also enhanced - and there was no joins & half as many logical reads. The cumulative column was calculated using a nifty rule computation.
SELECT name, TO_CHAR(dt,'DD-MM-YYYY') dt, amt, cum_amt -- Model results
FROM (
   SELECT name, TRUNC(dt, 'MM') dt, SUM(amt) amt
   FROM   customer
   GROUP BY name, TRUNC(dt, 'MM')
)
MODEL
PARTITION BY (name)
DIMENSION BY (dt)
MEASURES (amt, cast(NULL AS NUMBER) cum_amt) -- Define calculated col
IGNORE NAV
RULES SEQUENTIAL ORDER(
   -- Conjure dates
   amt[FOR dt FROM TO_DATE('01-01-2009', 'DD-MM-YYYY')
              TO   TO_DATE('01-12-2009', 'DD-MM-YYYY')
              INCREMENT NUMTOYMINTERVAL(1, 'MONTH')
       ] = amt[CV(dt)] -- Apply amt for given date, if found
  ,cum_amt[ANY] = SUM(amt)[dt <= CV(dt)] -- Calculate cumulative
)
ORDER BY name, dt;
------------------------------------------------------------------------------
| Id  | Operation                    | Name     | Rows  | Bytes | Cost (%CPU)|
------------------------------------------------------------------------------
|   0 | SELECT STATEMENT             |          |     8 |   136 |     5  (40)|
|   1 |  SORT ORDER BY               |          |     8 |   136 |     5  (40)|
|   2 |   SQL MODEL ORDERED          |          |     8 |   136 |     5  (40)|
|   3 |    HASH GROUP BY             |          |     8 |   136 |     5  (40)|
|   4 |     TABLE ACCESS FULL        | CUSTOMER |     8 |   136 |     3   (0)|
|   5 |    WINDOW (IN SQL MODEL) SORT|          |       |       |            |
------------------------------------------------------------------------------
The following solution is the one I stumbled upon while playing with ANSI joins. I wanted to see how it compared with the others. We still have a join, but that's not necessarily an issue. It's certainly tidier looking than the first solution, and we don't need to fry our brain learn how to use model. I have heard some resistance to ANSI syntax however - not for the sake of going against the grain, but apparently there have been some security issues. I'm not qualified to comment on this, let's just compare the performance in this particular test case.
SELECT c.name, TO_CHAR(real_dt,'Month') mth, NVL(amt,0) amt
      ,NVL(SUM(amt) OVER (PARTITION BY c.name
                          ORDER BY real_dt ),0) cum_amt
FROM customer c
PARTITION BY (name)
RIGHT OUTER JOIN
  (WITH mths AS (SELECT TRUNC(SYSDATE,'YYYY') real_dt
                 FROM DUAL CONNECT BY LEVEL <= 12)
   SELECT ADD_MONTHS(real_dt,ROWNUM-1) real_dt FROM mths) mths
ON (real_dt = TRUNC(c.dt,'mm'))
ORDER BY c.name, real_dt;
-------------------------------------------------------------------------------------
| Id  | Operation                           | Name     | Rows  | Bytes | Cost (%CPU)|
-------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                    |          |     2 |   130 |     8  (13)|
|   1 |  WINDOW SORT                        |          |     2 |   130 |     8  (13)|
|   2 |   VIEW                              |          |     2 |   130 |     8  (13)|
|   3 |    NESTED LOOPS PARTITION OUTER     |          |     2 |    46 |     8  (13)|
|   4 |     BUFFER SORT                     |          |       |       |            |
|   5 |      VIEW                           |          |     1 |     6 |     2   (0)|
|   6 |       COUNT                         |          |       |       |            |
|   7 |        VIEW                         |          |     1 |     6 |     2   (0)|
|   8 |         CONNECT BY WITHOUT FILTERING|          |       |       |            |
|   9 |          FAST DUAL                  |          |     1 |       |     2   (0)|
|* 10 |     FILTER                          |          |       |       |            |
|  11 |      SORT PARTITION JOIN            |          |     1 |    17 |     4  (25)|
|  12 |       TABLE ACCESS FULL             | CUSTOMER |     1 |    17 |     3   (0)|
-------------------------------------------------------------------------------------
I executed these 3 statements 1000 times on my laptop on 10gR2 (similar results in 11gR1) to see how throughput compares. I consistently found the following performance:
6 secs -- Original
.006 secs per iteration
4.6 secs -- Model clause
.0046 secs per iteration
.35 secs -- Partitioned outer join
.00035 secs per iteration
Personally I was a little surprised at the dramatic timing difference with the partitioned outer join over the original. I thought perhaps that would have been more comparable to the model clause solution. Something to bear in mind in future development.

If I needed to write this for a future project I'd probably take a closer look at the relevant data set using Tom Kyte's runstats package. This used to be located here - htpp://asktom.oracle.com/tkyte/runstats.html, but the link is currently broken. Possibly related to his recent move. However here is one of many of his forum entries addressing runstats. This package is better than just measuring throughput, it will let you know how your application may scale by paying attention to latches. A DBA's perspective can be found here.

Monday, 20 July 2009

Presentations

Many moons ago when I first started as an Oracle Developer, I worked on a project with Penny. I wasn't under her employ then, but we were playing with materialized views in an 8.1.7 database. Fairly cutting edge at the time she suggested I do a presentation on it for the conference. Green as I was straight out of university I graciously declined, but ever since I searched for a topic I'd be confident in presenting. One day on a blog I stumbled on someone's response to a simple query that suggested using the Model Clause and for some reason this peaked (not piqued) my interest. I've never looked back since and ideas just keep coming.

So to those contemplating it, just go for it. It's like riding a bike. And you learn so much about Oracle when you're essentially forced to tinker & play.

Below is a list of presentations I've done so far.
(And yes, it turns out I'm a fan of alliteration)

Update December 2017
There is a larger, more up-to-date list on this dedicated page:

http://www.grassroots-oracle.com/p/presentations.html

***************

Oracle Apex 4.1 Security  ( prezi | bookmark )

There have always been many options for securing Oracle Apex applications, and many of them don't require much effort - just a little understanding.

This presentation will cover all things Apex Security. Scott covers discussions and examples of many of Apex's security features, including changes to Authentication & Authorisation in 4.1 towards using plug-ins.

AUSOUG WA Conference 2011 - Awarded Best Paper

Oracle Apex Performance  ( pdfprezi | bookmark )

Over the years there have been countless technical and social presentations doting on 5, 10, 12 ways to improve this, that and the other.

I will go through various performance tweaks (not tweets) for Application Express without limiting myself to a golden number.

These improvements will vary from simple PL/SQL refactoring; to monitoring for bottlenecks in your application; to cutting down maintenance time - which relates to the performance of you as an Oracle developer with only 24 hours in a day.

We may even visit a little Apex instrumentation on the way.

AUSOUG WA Conference 2010
AUSOUG SA Conference 2011
Virtathon 2011
InSync Sydney Conference 2011

Apex with Oracle Text ( pdfinline | bookmark )

Oracle Text is a facility within the database that provides more advanced indexing & search techniques - including the ability to index documents stored in your database; on your server; or even the web!

Now you can incorporate this functionality into your web application using Application Express.

This presentation will demonstrate how easy it is to combine the two, and give you a platform for further expansion and exploration within a very powerful product.

AUSOUG WA Branch Meeting 2010

Trials & Tribulations of an Oracle Forms -> Apex Conversion ( pdf | bookmark )

Abstract: One of the hot topics these days questions how long you should keep hold of your long standing, hard working Forms application. Oracle support timeframes for Forms has been quite fluid over recent years and ultimately you may need to make a move..

Depending on the size and complexity of your application, a number of options present themselves.
Application Express is one such option. The question remains, however, is there some black box tool we can use to plug our Forms in and have it pump out Apex pages? And how much assistance does this tool need before and after the conversion process?

This presentation will cover some of the considerations you may need to make when contemplating this event. We'll show what happens when you convert a form with various types of components and reveal the gaps, if any, you'll find at the other end.

Let's not forget the other components of our Forms application. What about PL/SQL libraries, reports, menus? Is it worth tackling some of these items manually? We shall see...

AUSOUG WA and Vic National Conference 2009 - Oracle ACE ODTUG Stream

Oracle Documentation ( inline | bookmark )

Abstract: If you're a database developer, regardless of whether you have 6 months or 6 years experience, you need a good reference manual.
By good, I mean one that you can locate what you need within seconds. I know you know what I mean...

I'd like to show you how easy the free Oracle supplied documentation really is to use. And if for some reason it still doesn't cater to your needs, I'll show you some other methods and destinations that might save you a few headaches.

In this short presentation, I'll show you how to find most day to day documentation requirements in 2 clicks, maybe 3 if you're unlucky - without connecting to the net.
You might also hear some other new words such as Ubiquity & Bookmarklets.

AUSOUG WA Branch Meeting 2009

11g New Features ( inline | ppt | bookmark )

Abstract: There are a wealth of new features available in the 11g database release. This presentation touches on SQL & PL/SQL features I found of interest, and concentrates particularly on virtual columns.
Relevant scripts are available here.

ACTOUG May 2009

Creative Conditional Compilation ( inline | ppt | bookmark )
Abstract: Oracle released a feature in 10g Release 2 they thought worthy of facilitating in previous versions via patch sets - so I thought it was worthy enough for a closer look.

Conditional compilation isn't a foreign concept in the programming world, and for the developer aficionado it's a wonderful paradigm to explore.

Conditional compilation was designed with the main intention of being able to create database version specific code. With the recent advent of 11g, developers can actually start adding 11g features to their 10g code today!

However it provides the savvy PL/SQL developer to enhance their code in more ways than just gearing up for the next release… Dust of your software engineering hats and discover how to utilise conditional compilation to explore concepts such as latent self tracing code; latent assertions; and enhanced prototyping for your unit tests.

This seminar will illustrate several examples of conditional compilation that will open your mind; ultimately benefit your users; and can be implemented as far back as 9.2!

AUSOUG WA and QLD National Conference 2008
AUSOUG VIC Branch Meeting 2009
AUSOUG Qld Branch Meeting 2010


Be a Bulk Binding Baron ( inline | pdf | bookmark )

Abstract: Developers - If you are not using Bulk Binds you are not writing PL/SQL efficiently!

Bulk binding has been around for a long time, yet there are sites out there that don't utilise this feature to its full extent, if at all. Every release of Oracle improves on this functionality so obviously it's a topic worthy of consistent awareness.

In PL/SQL and SQL, there are a few nifty features related to bulk binding you may not have seen - it's not all about BULK COLLECT. Whether you're on 8i, 11g or anything in between, you'll benefit from the concepts described in this seminar and become a Bulk Binding Baron!

AUSOUG WA Branch Meeting 2008
AUSOUG SA Conference 2011

The Model Clause ( inline | bookmark )

Abstract: The session will breakdown the Model clause into its fundamental components and provides some basic real-world examples to demonstrate its greater potential.

Though most developers have heard of the SQL Model clause in 10g, many may baulk at the idea of using it - daunted by seemingly foreign syntax that might well have come out of a FORTRAN program.

Look a little closer and you'll find it's just like building a spreadsheet. Concise, easy to read syntax that provides the functionality for demanding calculations that would normally require elaborate joins, unions, analytics or PL/SQL. In addition to the development and maintenance burden, we are also faced with the all too familiar problem of business customers duplicating data to an Excel spreadsheet that is shared and erroneously modified around the workplace.

This session uses the Model clause as a high performance tool that can simplify approaches to every day problems. It demonstrates that Model is an extension to SQL that forms multi-dimensional arrays with inter-row & inter-array calculations that automatically resolves formula dependencies.

AUSOUG WA and VIC National Conference 2007