Saturday, 26 January 2008

RANK, DENSE_RANK AND ROW_NUMBER analytic functions

I wrote this test to better understand the use of RANK, DENSE_RANK and ROW_NUMBER Oracle analytic functions and Top-N querying. The beauty of this exercise is that all 3 analytic functions are illustrated with one example.


Suppose the business requirement in this case is to rank the products of each customer by the quantity purchased.

The test table I used for this is:

CREATE TABLE t
(
customer_name VARCHAR2(50),
product_name VARCHAR2(50),
hits NUMBER
)

The data in the table is 16 rows like below:

INSERT INTO t VALUES('bill', 'egg', 1);
INSERT INTO t VALUES('bill', 'egg', 1);
INSERT INTO t VALUES('bill', 'beer', 3);
INSERT INTO t VALUES('bill', 'beer', 2);
INSERT INTO t VALUES('bill', 'jeans', 1);
INSERT INTO t VALUES('larry', 'beer', 4);
INSERT INTO t VALUES('larry', 'beer', 2);
INSERT INTO t VALUES('larry', 'cheese', 1);
INSERT INTO t VALUES('larry', 'cheese', 1);
INSERT INTO t VALUES('larry', 'cheese', 1);
INSERT INTO t VALUES('larry', 'cheese', 1);
INSERT INTO t VALUES('larry', 'cheese', 1);
INSERT INTO t VALUES('larry', 'cheese', 1);
INSERT INTO t VALUES('larry', 'olives', 1);
INSERT INTO t VALUES('larry', 'olives', 1);
INSERT INTO t VALUES('larry', 'car', 1);
COMMIT;


SELECT * FROM t;

CUSTOMER_NAME PRODUCT_NAME HITS
------------------ ----------------- ----------
bill egg 1
bill egg 1
bill beer 3
bill beer 2
bill jeans 1
larry beer 4
larry beer 2
larry cheese 1
larry cheese 1
larry cheese 1
larry cheese 1
larry cheese 1
larry cheese 1
larry olives 1
larry olives 1
larry car 1


The solution query


SELECT
customer_name,
product_name,
hits,
rank() over(PARTITION BY customer_name ORDER BY hits DESC nulls LAST) rank,
dense_rank() over(PARTITION BY customer_name ORDER BY hits DESC nulls LAST) denserank,
row_number() over(PARTITION BY customer_name ORDER BY hits DESC) rownumber
FROM
(
SELECT
customer_name,
product_name,
SUM(hits) hits
FROM t
GROUP BY customer_name, product_name
);

CUSTOMER_NAME PRODUCT_NAME HITS RANK DENSERANK ROWNUMBER
--------------- --------------- ---------- ---------- ---------- ----------
bill beer 5 1 1 1
bill jeans 1 2 2 2
bill egg 1 2 2 3
larry cheese 6 1 1 1
larry beer 6 1 1 2
larry olives 2 3 2 3
larry car 1 4 3 4

7 rows selected.



Observe how dense rank will not skip the ranking sequence when products with the same amount of hits are ranked with the same rank. In this case Larry's cheese and beer are ranked in rank 1 in the DENSERANK column and Larry's olives immediately after that are ranked with 2. Whereas the situation is quite different for this case in the RANK column where Larry's olives are ranked with 3.

Thursday, 24 January 2008

Data Modeling with Oracle JDeveloper

Long time now I have been looking for a Free Data Modelling tool which is from the Oracle breed but is not part of a gigabytes size software installation (Discoverer...etc).



Finally oracle has bundled Data Modeling in JDeveloper which is a free tool they give for building Java applications. All this time JDeveloper hasn't been of my taste because of its name "the J". I am not a Java Developer! But I kind of started liking it now, when I checked it closely, I found out that it has changed a bit since the last time I had a look at it couple of years before, it is more DBA friendly.

JDeveloper is:


  • Free!

  • Smaller, compact sizes of 400M for a full installation

  • Familiar interface to Oracle SQL Developer. (I didn't realise that Oracle SQL Developer actually was already part of JDeveloper all this time, I always thought that Oracle SQL Developer was a new tool, clever!)

  • Capable of database development and more DBA friendly with SQL Sheet, ER Modeling, PL/SQL Editing


I would still prefer to see the Data Modeling part of it available in Oracle SQL Developer as well, as a DBA I want a mean and lean tool to do SQL, PL/SQL and ER Modeling.

For a download, and tutorials on how to create data models with JDeveloper go http://www.oracle.com/technology/obe/obe1013jdev/10131/database%20development/obe_%20dbasedevmt.htm
(This link takes time, alternatively search for JDeveloper in Google)

Thursday, 6 December 2007

Upgrade an Oracle 10g database to Oracle 11g using dbua

I am trying to upgrade my Oracle installation and a database on my Ubuntu 7.10 Desktop from Oracle (10.2.0.1) to Oracle 11g (11.1.0.6.0) using dbua. During this very messy and sticky job, I hit couple of problems, looked around in blogs and here is how I got it all sorted.



1. Install New Oracle Software in a seperate directory in a new home

I have downloaded the Oracle 11g (11.1.0.6.0) version from Oracle here , then I kept my old $ORACLE_BASE directory as: /usr/local/oracle and created a new $ORACLE_HOME on my Desktop like this /usr/local/oracle/product/11.1.0.6.0/. I installed the Oracle 11g version in there using OUI (Oracle Universal Installer). Hit lots of errors, as I am using an uncertified OS version (Ubuntu 7.10). I implemented different workarounds, most of them just ignoring all the errors Still installs!

Blogs which I found very useful while doing this were:


http://www.pythian.com/blogs/549/installing-oracle-11g-on-ubuntu-linux-704



http://www.dizwell.com/prod/node/1046



http://advait.wordpress.com/2007/09/07/upgrading-to-oracle-database-11g/




Although the bloggers above indicate gentle ways of dealing with installing Oracle 11g on uncertified OS (like Ubuntu), I followed the crude way and “Ignored All” warning messages until I have completed the installation of Oracle 11g softwarre. Next in step 2 I talk about upgrading my existing Oracel 10g database using dbua (Database Upgrade Assistant).


2. Upgrading existing databases to the New Oracle 11g (11.1.0.6.0) version using dbua

Now is time to do the upgrade. Setting all ENV parameters ORACLE_HOME and ORACLE_BASE correctly to point to the new Oracle 11g software (don’t forget oratab), I fired up dbua Database Upgrade Assistant and started upgrading my database.

ADVAIT’S ORACLE BLOG http://advait.wordpress.com/2007/09/07/upgrading-to-oracle-database-11g/ explains the command line method for upgrading databases from Oracle 10g 10.2.x.x version to Oracle 11g 11.1.0.6.0. Very good. Well done Advait!

It solved all my critical upgrade issues which I have encountered using dbua. Especially the timezone 4 bug identified as 396387.1 in metalink is very well explained in Advait’s blog. This one hits versions 10.2.x.x, most of them.

I have ignored lots of INVALID object warnings from dbua and went ahead with the upgrade. Had a bumpy ride with Enterprise Manager Configuration not succeeding (says failed), skipped that as well as I could do it later. I have managed to upgrade and got the Oracle 11g prompt:


SQL*Plus: Release 11.1.0.6.0 - Production on Thu Dec 6 22:40:21 2007
Copyright (c) 1982, 2007, Oracle. All rights reserved.
SQL> conn / as sysdba
Connected.
SQL> select name from v$database;
NAME
---------
TESTDB01


Tuesday, 13 November 2007

SQL*Plus takes 100% of CPU time

This is a bug, a funny one, which I experienced on Linux x86 boxes where SQL*Plus would just hang, and do nothing...


That is, if the Linux x86 box has an uptime of more than 200 days, then SQL*Plus would just hang!

I experienced it on a linux x86 Oracle 10g Client installation where I had the client loging in everyday via SQL*Plus to remote boxes and do things. Suddenly it stopped. I checked top on the box where the Oracle 10g client was installed and saw SQL*Plus eating up the box alive! How strange!??

Check the stack trace:

strace $ORACLE_HOME/bin/sqlplus -V 2>&1 |less

you will see lots of jibberish...


Immediately checked metalink I found the Bug 4612267.

Note: 338461.1: SQL*Plus With Instant Client 10.2.0.1 Hangs, When System Uptime Is More Than 248 Days

Oracle support confirmed this, they asked me to patch the installation with applying a one-off patch they gave me on top of the 10.2.0.1.0 Linux Client.

Wednesday, 7 November 2007

Table of Dates

Use a PL/SQL collection TYPE to generate a date list as a database table which you can then use as time series table in your data warehouse.


1. Create a DATE_TABLE type.

SQL> create or replace TYPE "DATE_TABLE" AS TABLE OF DATE;

2. Create the DATE_RANGE function to manipulate (load) the above type.

.


CREATE OR REPLACE FUNCTION date_range(from_dt IN DATE, to_dt IN DATE)
RETURN date_table AS
a_date_table date_table := date_table();
cur_dt DATE := from_dt;
BEGIN
WHILE cur_dt <= to_dt
LOOP
a_date_table.extend;
a_date_table(a_date_table.COUNT) := cur_dt;
cur_dt := cur_dt + 1;
END LOOP;
RETURN a_date_table;
END date_range;


more about this function here:

http://www.adp-gmbh.ch/ora/sql/table_cast.html

3. Use the TYPE as a date table which you can then join with other tables.


SELECT column_value DAYS
FROM TABLE(CAST(date_range('18-FEB-1981', '25-FEB-1981') AS date_table))

/

DAYS
-------------------------
18-FEB-81
19-FEB-81
20-FEB-81
21-FEB-81
22-FEB-81
23-FEB-81
24-FEB-81
25-FEB-81



Then use the "Date Table" to join it with SCOTT.EMP and get the employees which were hired during the period


SELECT d.DAYS,e.ename,e.hiredate
FROM emp e,

(SELECT column_value DAYS
FROM TABLE(CAST(date_range('18-FEB-1981', '25-FEB-1981') AS
date_table))) d

WHERE d.DAYS = e.hiredate(+)
/



DAYS ENAME HIREDATE
------------------------- ---------- -------------------------
18-FEB-81
19-FEB-81
20-FEB-81 ALLEN 20-FEB-81
21-FEB-81
22-FEB-81 WARD 22-FEB-81
23-FEB-81
24-FEB-81
25-FEB-81