Skip to content
accelerando

Category Archives: Oracle

Optimizing the Oracle Query Optimizer

20-Oct-10

The Oracle Query Optimizer does a great job in rewriting Queries. For example the nested subquery in

SELECT C.cust_last_name, C.country_id 
FROM    customers C 
WHERE EXISTS (SELECT 1 
                          FROM sales S 
                          WHERE S.quantity_sold > 1000 AND 
                                        S.cust_id = C.cust_id);

will be rewritten into the following query, using a semi-join:

SELECT C.cust_last_name, C.country_id 
FROM customers C, sales S 
WHERE S.quantity_sold > 1000 AND 
C.cust_id S= S.cust_id;

(Example from Optimizer Transformations: Subquery Unnesting part 1)

In the first form, the subquery will be invoked for each entry in customers, replacing the value of the correlated column C.cust_id. Thus the query cannot be parallelized among other implications. Most of the time, if the columns are correctly indexed, the semijoin will be much faster.

This form of rewriting is called “unnesting”.

Consider the following query:

SELECT C.cust_last_name, C.country_id 
FROM    customers C, (SELECT f_compute_some_complicated_value(s.quantity_sold), S.cust_id
                          FROM sales S                          
                          WHERE S.quantity_sold > 1000) S
WHERE  S.cust_id = C.cust_id

The subquery in the from clause its called a derived table. It will be rewritten as:

SELECT C.cust_last_name, C.country_id, f_compute_some_complicated_value(s.quantity_sold), S.cust_id
FROM    customers C, Sales S
WHERE  S.cust_id = C.cust_id 
AND S.quantity_sold > 1000)

This technique is called query merging. Here the join should also be faster in most cases.

Both techniques are good and will improve many queries.

But consider a scenario where the subqueries query remote tables (via database link for example) or call PL/SQL or even Java stored procedures and the main table (customes in the examples) contains many more rows than the joined table or the where clause is less selective then the where clause of the subquery: The subquery will be executed as many times as there are rows in the main table. This can lead to very poor performance.

Luckily, the optimizer can be given hints:

To stop unnesting, use the NO_UNNEST hint:

SELECT C.cust_last_name, C.country_id 
FROM    customers C 
WHERE EXISTS (SELECT /*+ NO_UNNEST */ 1 
                          FROM sales S 
                          WHERE S.quantity_sold > 1000 AND 
                                        S.cust_id = C.cust_id);

Furthermore, i’d rewrite the query like so:

SELECT C.cust_last_name, C.country_id 
FROM    customers C 
WHERE C.cust_id =ANY (SELECT /*+ NO_UNNEST */ S.cust_id 
                                       FROM sales S 
                                       WHERE S.quantity_sold > 1000);

Where the EXISTS clause triggers the subquery for each item in customes, the =ANY clause will trigger the subquery only once. Both queries are semantically identical.

To stop the merging of derived tables, use the NO_MERGE hint in the surrounding select clause:

SELECT /*+ NO_MERGE(S) */ C.cust_last_name, C.country_id 
FROM    customers C, (SELECT f_compute_some_complicated_value(s.quantity_sold), S.cust_id
                          FROM sales S                          
                          WHERE S.quantity_sold > 1000) S
WHERE  S.cust_id = C.cust_id

The NO_MERGE hint takes the alias of the derived table as a parameter.

Combining both techniques brought a complex query down from 40s to just about 1s. We are using an Oracle 10g Database, but the hints also apply to 11g.

Oracle “sleep” procedure: DBMS_LOCK.SLEEP

07-Sep-10

There’s a nice little “sleep” procedure in Oracle: A procedure that stops the execution of the current thread for n seconds.

Strangely, this method can be called in SQL*Plus like so:

EXEC dbms_lock.sleep(10);

but not in another stored procedure or function like so

CREATE OR REPLACE PROCEDURE FOOBAR AS
BEGIN
  DBMS_LOCK.SLEEP(1);
END;
/

To use “sleep” in your procedures or functions, login as administrator to your database (or ask you admin to to so) and create the following objects:

CREATE OR REPLACE PROCEDURE sleep(seconds NUMBER) AS
BEGIN
  DBMS_LOCK.SLEEP(seconds);
END;
/
 
CREATE OR REPLACE public synonym sleep FOR sleep;
 
GRANT EXECUTE ON sleep TO public;

and in your procedure just use “sleep”.

Oracle NLS-Lang Settings

13-Aug-10

To use SQL*Plus Windows correctly, export NLS_LANG like so:

set NLS_LANG=GERMAN_GERMANY.WE8MSWIN1252

If you prefer the command line client SQL*Plus, export NLS_LANG like so:

set NLS_LANG=GERMAN_GERMANY.WE8PC850

Oracle: Drop table if exists replacement

16-Feb-10

Mysql has a nice “if exists” addition to the drop table statement. If the table to be dropped does not exists, it doesn’t raise an exception but only creates a warning.

In Oracle RDMBS you can emulate this behavior like so:

BEGIN EXECUTE immediate 'drop table INSERT_TABLE_NAME_HERE'; EXCEPTION WHEN others THEN IF SQLCODE != -942 THEN RAISE; END IF; END;
/

Ugly, but it works very well.

Different day, same shit, today: Java 5 on Oracle Enterprise Linux 5

19-Jan-09

Worlds collide: Oracle and Sun JDK. Perfect start to ruin a not so bad Monday morning.

Background: Need to have a Tomcat Server deployed on a Oracle Enterprise Linux 5 system.

I was happy, when i saw a tomcat5 package in the repositories. Great, i thought. All i need. Well. Not.

Under Windows you’ll get between one and ten JVM versions installed with on Oracle product (slight exaggerated), within the OEL5 there was only one ancient 1.4.2 JDK. *sigh* Did i mention that the application that is supposed to run on that thing uses a buttload of Java 5 features?

“yum search java-”… No Java 5. WtF?

There is no Java 5.

Again, do it yourself:

This is a nice entry that describes howto build rpms for the “official” Sun Java 5 jdk.

I used the following steps to build my rpms:

  • Downloaded this rpm
  • Downloaded jdk-1_5_0_15-linux-i586.bin from the Sun JDK archive page
  • Put the later one into /usr/src/redhat/SOURCES/
  • Built the rpms with rpmbuild –rebuild java-1.5.0-sun-1.5.0.15-1jpp.nosrc.rpm. If rpmbuild is not installed, it’s hidden in the package rpm-build, not rpmbuild.
  • Installed missing libXp
  • Installed the rpms:
    rpm -Uvh /usr/src/redhat/RPMS/i586/java-1.5.0-sun-1.5.0.15-1jpp.i586.rpm
    rpm -Uvh /usr/src/redhat/RPMS/i586/java-1.5.0-sun-devel-1.5.0.15-1jpp.i586.rpm
    rpm -Uvh /usr/src/redhat/RPMS/i586/java-1.5.0-sun-src-1.5.0.15-1jpp.i586.rpm
    rpm -Uvh /usr/src/redhat/RPMS/i586/java-1.5.0-sun-demo-1.5.0.15-1jpp.i586.rpm
    rpm -Uvh /usr/src/redhat/RPMS/i586/java-1.5.0-sun-plugin-1.5.0.15-1jpp.i586.rpm
    rpm -Uvh /usr/src/redhat/RPMS/i586/java-1.5.0-sun-fonts-1.5.0.15-1jpp.i586.rpm
    rpm -Uvh /usr/src/redhat/RPMS/i586/java-1.5.0-sun-alsa-1.5.0.15-1jpp.i586.rpm
    rpm -Uvh /usr/src/redhat/RPMS/i586/java-1.5.0-sun-jdbc-1.5.0.15-1jpp.i586.rpm
  • Last step: Choose the right java version with alternatives –config java

After that, everything could be fine. Well, it wasn’t:

sun.misc.InvalidJarIndexException: Invalid index

Jehova! Finally not a NPE but something new, at least to me. Sometimes i wonder why i always run into bugs like these.

Some script changes and repackages all jar files in some weird ways so that a standard JDK has funny problems.

My solution to it: Reindex everything in /usr/share/java after you’ve chosen your newly installed java with the following command:

find /usr/share/java/ -iname "jakarta*commons*.jar" -exec jar -i {} \;

I opted to reindex only the jakarta commons files, that got Tomcat up and running with Java 5.

One last note: The /usr/bin/dtomcat5 is broken imho, at least when run from /etc/init.d/tomcat5. In ignores /etc/tomcat5/tomcat5.conf and therefore cannot stop Tomcat.

My solution: Replace

if [ -z "$CATALINA_HOME" ]; then
    TOMCAT_CFG="/etc/tomcat5/tomcat5.conf"
fi

(in line 55 on my setup) with

if [ -z "$CATALINA_HOME" ]; then
    TOMCAT_CFG="/etc/tomcat5/tomcat5.conf"
    [ -r "$TOMCAT_CFG" ] && . "${TOMCAT_CFG}"
fi

and remove

if [ -z "$CATALINA_HOME" ]; then
    [ -r "$TOMCAT_CFG" ] && . "${TOMCAT_CFG}"
fi

(in line 105 on my setup).

I wonder why simple things like these always are a pain in the ass. Stupid nuisances that keeps people from getting their work done. *sigh* Not a good start for the week.

Close
E-mail It