Unix Technical Forum

Group and Count Performance Issue challenge

This is a discussion on Group and Count Performance Issue challenge within the Oracle Miscellaneous forums, part of the Oracle Database category; --> I have been challenged to optimize a stored procedure on Oracle 9i that will return a ref cursor with ...


Go Back   Unix Technical Forum > Database Server Software > Oracle Database > Oracle Miscellaneous

FAQ Members List Calendar Search Today's Posts Mark Forums Read
  #1 (permalink)  
Old 04-08-2008, 12:17 PM
glamar66@hotmail.com
 
Posts: n/a
Default Group and Count Performance Issue challenge

I have been challenged to optimize a stored procedure on Oracle 9i
that will return a ref cursor with the counts of each status for each
batch from the tables defined below. I started with a View to join
the tables together on the OrderNo field. Then I wrote a query in the
stored procdure that grouped the Batch values together, and then did a
count for each Status value in each Batch like this:

SELECT Batch,
COUNT(Batch) Total,
(SELECT COUNT(Batch)
FROM MYVIEW
WHERE Status = 1
AND Batch = V1.Batch
(SELECT COUNT(Batch)
FROM MYVIEW
WHERE Status = 2
AND Batch = V1.Batch
(SELECT COUNT(Batch)
FROM MYVIEW
WHERE Status = 3
AND Batch = V1.Batch
FROM MYVIEW V1
WHERE Status < 4
GROUP BY Batch;

With 42 unique Batch values and 26,000 OrderNo values my dev server
takes over 5 seconds. I built the query without the view and added
the joins in the query and took the same amount of time. I can't
change the table structures but the view is wide open. This is a
conversion project from MS SQL and this same data returns from MS SQL
in 0.09 seconds. I hope Oracle can beat MS

Glamar

----------------
Suporting Data and table structures.


TableOne Definition:
Batch - VarChar
OrderNo - VarChar
Various other Columns of Data...

TableTwo Definition:
OrderNo - VarChar
Status - Number
Various other Columns of Data...

View Definition:
Select o.Batch, o.OrderNo, t.Status, ...
FROM TableOne o Left Outer Join TableTwo t
on o.OrderNo = t.OrderNo;

Sample Data TableOne:
ABC1 123 ...
ABC1 234 ...
ABC1 345 ...
ABC1 456 ...
ABC2 567 ...
ABC2 678 ...
ABC2 789 ...
ABC2 321 ...
ABC3 432 ...
ABC4 543 ...

Sample Data TableTwo:
123 1 ...
234 1 ...
345 2 ...
456 3 ...
567 2 ...
678 1 ...
789 2 ...
321 2 ...
432 3 ...
543 3 ...

Expected Return Ref Cursor:
ABC1 4 2 1 3
ABC2 4 1 3 null
ABC3 1 null null 1
ABC4 1 null 1 null

Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
  #2 (permalink)  
Old 04-08-2008, 12:17 PM
Charles Hooper
 
Posts: n/a
Default Re: Group and Count Performance Issue challenge

On Feb 13, 10:56 am, glama...@hotmail.com wrote:
> I have been challenged to optimize a stored procedure on Oracle 9i
> that will return a ref cursor with the counts of each status for each
> batch from the tables defined below. I started with a View to join
> the tables together on the OrderNo field. Then I wrote a query in the
> stored procdure that grouped the Batch values together, and then did a
> count for each Status value in each Batch like this:
>
> SELECT Batch,
> COUNT(Batch) Total,
> (SELECT COUNT(Batch)
> FROM MYVIEW
> WHERE Status = 1
> AND Batch = V1.Batch
> (SELECT COUNT(Batch)
> FROM MYVIEW
> WHERE Status = 2
> AND Batch = V1.Batch
> (SELECT COUNT(Batch)
> FROM MYVIEW
> WHERE Status = 3
> AND Batch = V1.Batch
> FROM MYVIEW V1
> WHERE Status < 4
> GROUP BY Batch;
>
> With 42 unique Batch values and 26,000 OrderNo values my dev server
> takes over 5 seconds. I built the query without the view and added
> the joins in the query and took the same amount of time. I can't
> change the table structures but the view is wide open. This is a
> conversion project from MS SQL and this same data returns from MS SQL
> in 0.09 seconds. I hope Oracle can beat MS
>
> Glamar
>
> ----------------
> Suporting Data and table structures.
>
> TableOne Definition:
> Batch - VarChar
> OrderNo - VarChar
> Various other Columns of Data...
>
> TableTwo Definition:
> OrderNo - VarChar
> Status - Number
> Various other Columns of Data...
>
> View Definition:
> Select o.Batch, o.OrderNo, t.Status, ...
> FROM TableOne o Left Outer Join TableTwo t
> on o.OrderNo = t.OrderNo;
>
> Sample Data TableOne:
> ABC1 123 ...
> ABC1 234 ...
> ABC1 345 ...
> ABC1 456 ...
> ABC2 567 ...
> ABC2 678 ...
> ABC2 789 ...
> ABC2 321 ...
> ABC3 432 ...
> ABC4 543 ...
>
> Sample Data TableTwo:
> 123 1 ...
> 234 1 ...
> 345 2 ...
> 456 3 ...
> 567 2 ...
> 678 1 ...
> 789 2 ...
> 321 2 ...
> 432 3 ...
> 543 3 ...
>
> Expected Return Ref Cursor:
> ABC1 4 2 1 3
> ABC2 4 1 3 null
> ABC3 1 null null 1
> ABC4 1 null 1 null


Reduce it to a simple SQL statement using DECODE. If the STATUS is
the expected value (1, 2, 3) for the column, return 1, otherwise
return NULL. Then count the non-null return values. COUNT will only
count non-null values:
SELECT
BATCH,
COUNT(BATCH) TOTAL,
COUNT(DECODE(STATUS,1,1,NULL)) TOTAL_1,
COUNT(DECODE(STATUS,2,1,NULL)) TOTAL_2,
COUNT(DECODE(STATUS,3,1,NULL)) TOTAL_3
FROM
MYVIEW V1
WHERE
STATUS < 4
GROUP BY
BATCH;

It appears that if COUNT returns 0, that you want NULL to be returned
rather than 0. Once again, use DECODE to convert 0 to NULL and all
other values to the original formula:
SELECT
BATCH,
COUNT(BATCH) TOTAL,
DECODE(COUNT(DECODE(STATUS,1,1,NULL)),0,NULL,COUNT (DECODE(STATUS,
1,1,NULL))) TOTAL_1,
DECODE(COUNT(DECODE(STATUS,2,1,NULL)),0,NULL,COUNT (DECODE(STATUS,
2,1,NULL))) TOTAL_2,
DECODE(COUNT(DECODE(STATUS,3,1,NULL)),0,COUNT(DECO DE(STATUS,
3,1,NULL))) TOTAL_3
FROM
MYVIEW V1
WHERE
STATUS < 4
GROUP BY
BATCH;

The inclusion of STATUS < 4 leads me to believe that you are
attempting to reuse a view definition for a different task. Doing so
may lead to performance issues. It may be a good idea to eliminate
the view and directly reference the exact SQL statement of interest.

Charles Hooper
PC Support Specialist
K&M Machine-Fabricating, Inc.

Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
  #3 (permalink)  
Old 04-08-2008, 12:17 PM
Maxim Demenko
 
Posts: n/a
Default Re: Group and Count Performance Issue challenge

Charles Hooper schrieb:
Once again, use DECODE to convert 0 to NULL and all
> other values to the original formula:
> SELECT
> BATCH,
> COUNT(BATCH) TOTAL,
> DECODE(COUNT(DECODE(STATUS,1,1,NULL)),0,NULL,COUNT (DECODE(STATUS,
> 1,1,NULL))) TOTAL_1,
> DECODE(COUNT(DECODE(STATUS,2,1,NULL)),0,NULL,COUNT (DECODE(STATUS,
> 2,1,NULL))) TOTAL_2,
> DECODE(COUNT(DECODE(STATUS,3,1,NULL)),0,COUNT(DECO DE(STATUS,
> 3,1,NULL))) TOTAL_3
> FROM
> MYVIEW V1
> WHERE
> STATUS < 4
> GROUP BY
> BATCH;
>


> Charles Hooper
> PC Support Specialist
> K&M Machine-Fabricating, Inc.
>


Charles, count(1) and count(batch) may yield different results.
I would suggest a minor modification

SELECT
BATCH,
COUNT(BATCH) TOTAL,
nullif(COUNT(DECODE(STATUS,1,batch,NULL)),0) TOTAL_1,
nullif(COUNT(DECODE(STATUS,2,batch,NULL)),0) TOTAL_2,
nullif(COUNT(DECODE(STATUS,3,batch,NULL)),0) TOTAL_3
FROM
MYVIEW V1
WHERE
STATUS < 4
GROUP BY
BATCH;


Best regards

Maxim
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
  #4 (permalink)  
Old 04-08-2008, 12:17 PM
Charles Hooper
 
Posts: n/a
Default Re: Group and Count Performance Issue challenge

On Feb 13, 11:58 am, Maxim Demenko <mdeme...@arcor.de> wrote:
> Charles Hooper schrieb:
> Once again, use DECODE to convert 0 to NULL and all
> > other values to the original formula:
> > SELECT
> > BATCH,
> > COUNT(BATCH) TOTAL,
> > DECODE(COUNT(DECODE(STATUS,1,1,NULL)),0,NULL,COUNT (DECODE(STATUS,
> > 1,1,NULL))) TOTAL_1,
> > DECODE(COUNT(DECODE(STATUS,2,1,NULL)),0,NULL,COUNT (DECODE(STATUS,
> > 2,1,NULL))) TOTAL_2,
> > DECODE(COUNT(DECODE(STATUS,3,1,NULL)),0,COUNT(DECO DE(STATUS,
> > 3,1,NULL))) TOTAL_3
> > FROM
> > MYVIEW V1
> > WHERE
> > STATUS < 4
> > GROUP BY
> > BATCH;

>
> > Charles Hooper
> > PC Support Specialist
> > K&M Machine-Fabricating, Inc.

>
> Charles, count(1) and count(batch) may yield different results.
> I would suggest a minor modification
>
> SELECT
> BATCH,
> COUNT(BATCH) TOTAL,
> nullif(COUNT(DECODE(STATUS,1,batch,NULL)),0) TOTAL_1,
> nullif(COUNT(DECODE(STATUS,2,batch,NULL)),0) TOTAL_2,
> nullif(COUNT(DECODE(STATUS,3,batch,NULL)),0) TOTAL_3
> FROM
> MYVIEW V1
> WHERE
> STATUS < 4
> GROUP BY
> BATCH;
>
> Best regards
>
> Maxim- Hide quoted text -


Maxim, good catch. COUNT(BATCH) and COUNT(1) could be different
values if null values are permitted in the BATCH column.

If nulls are not permitted in the BATCH column, this should also work:
SELECT
BATCH,
SUM(1) TOTAL,
SUM(DECODE(STATUS,1,1,NULL)) TOTAL_1,
SUM(DECODE(STATUS,2,1,NULL)) TOTAL_2,
SUM(DECODE(STATUS,3,1,NULL)) TOTAL_3
FROM
MYVIEW V1
WHERE
STATUS < 4
GROUP BY
BATCH;

Charles Hooper
PC Support Specialist
K&M Machine-Fabricating, Inc.

Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
  #5 (permalink)  
Old 04-08-2008, 12:17 PM
joel garry
 
Posts: n/a
Default Re: Group and Count Performance Issue challenge

On Feb 13, 7:56 am, glama...@hotmail.com wrote:
> I have been challenged to optimize a stored procedure on Oracle 9i
> that will return a ref cursor with the counts of each status for each
> batch from the tables defined below. I started with a View to join
> the tables together on the OrderNo field. Then I wrote a query in the
> stored procdure that grouped the Batch values together, and then did a
> count for each Status value in each Batch like this:
>
> SELECT Batch,
> COUNT(Batch) Total,
> (SELECT COUNT(Batch)
> FROM MYVIEW
> WHERE Status = 1
> AND Batch = V1.Batch
> (SELECT COUNT(Batch)
> FROM MYVIEW
> WHERE Status = 2
> AND Batch = V1.Batch
> (SELECT COUNT(Batch)
> FROM MYVIEW
> WHERE Status = 3
> AND Batch = V1.Batch
> FROM MYVIEW V1
> WHERE Status < 4
> GROUP BY Batch;
>
> With 42 unique Batch values and 26,000 OrderNo values my dev server
> takes over 5 seconds. I built the query without the view and added
> the joins in the query and took the same amount of time. I can't
> change the table structures but the view is wide open. This is a
> conversion project from MS SQL and this same data returns from MS SQL
> in 0.09 seconds. I hope Oracle can beat MS


"Beat" is a relative term. Some things you might want to note:

Oracle is not MS SQL.

Timing done on one run may not reflect timing done on several runs.
Part of the reason for this is Oracle is biased towards giving
multiple
users their own views of the database, based on when transactions
start relative to one another. Another part of the reason is Oracle
has
algorythms to keep more-used data in memory. So while MS-SQL
may "beat" Oracle counting a few hundred pieces of data on a PC
off a SATA drive, the situation may be different when ten thousand
users want the same few hundred pieces of data.

Oracle may be able to count the data from an index. Do you have
an index? Would the index be smaller than the table? Would a
substantial fraction of the data be null? Oracle can also "skip-scan"
an index, which means ignore a leading field.

Oracle performance tuning uses explain plans, which tell how the
optimizer is getting at the data. You want to understand the various
ways to access the data and what influences the optimizer. The
basics are explained in the performance tuning guide, and there
are other resources available when this becomes deep. In general,
in this usenet hierarchy any performance question is likely to be
answered with a request to show plan (from a built-in procedure) or
trace (for serious tuning) output. You should run this for your code
and Charles', the results may be informative.

The biggest dependency for the optimizer (after the code, of course)
is statistics, please state how and when you have generated them.
The details also vary by version, so it is important to state the
exact
version of Oracle, as well as, in general, your platform/OS.

There is a context switch going from SQL to PL/SQL. Also, the
SQL engine is highly optimized for relational or set-based queries,
so a generally good rule of thumb is to do anything you can in SQL
rather than PL/SQL, if you can. This is why Charles' decode example
is likely to work well. Not for this example, but in other cases
UNION
is the way to go.

Oracle is also biased towards the same exact code being run over
and over, so that is the basis of statements like "bind variables
are good" and "you automatically have bind variables when..."

Oracle uses latching code to control who has access to what
data where, so if your code is actually going to do something
like keep header information in the first row of a table and then
have a lot of people fight over it, you may have some redesign
work to do.

Oracle has views, but it also has Materialized Views. That's
another way of saying you can pre-digest some data for some
purposes.

Some people here want your data and code to come with
actual create table and insert statements to put the test
data together, as well as complete stored procedure code.
(Personally, I don't care, that's just a statement of some
expectations, makes it easy for people to play around.
I know I am more likely to work on something in such a
situation).

jg
--
@home.com is bogus.
http://theinvisiblethings.blogspot.c...every-day.html

Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
  #6 (permalink)  
Old 04-08-2008, 12:17 PM
glamar66@hotmail.com
 
Posts: n/a
Default Re: Group and Count Performance Issue challenge

Charles,

That did it, brought it down to under a second. Thanks for the help.

Glamar

Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
Reply


Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On
Forum Jump


All times are GMT. The time now is 07:52 AM.


Powered by vBulletin® Version 3.6.5
Copyright ©2000 - 2008, Jelsoft Enterprises Ltd.
Search Engine Optimization by vBSEO 3.2.0
www.UnixAdminTalk.com