Posts Tagged ‘Oracle’

Oracle ‘isnumeric’ function

Thursday, November 12th, 2009

Hello,

To me a simple, yet glaring, omission from the Oracle PL/SQL language, which is in many ways excellent, is a function to test to see if a string represents numeric data as can be found in many other languages (e.g is_numeric in PHP, isNumeric in Java and .Net).

A possible implementation is to simply try to assign the string to a number and catch any exceptions.  If something goes wrong then it’s not a number!

  create or replace function isnumeric(p_value in varchar2) return number
  as
    l_ret number;
  begin
    -- Try to cast to a number
    begin
      l_ret := to_number(p_value);
      -- if the line above worked then just reset to 1
      l_ret := 1;
    exception
      when others then
        -- Something, anything, went wrong
        l_ret := 0;
    end;
    -- Return
    return l_ret;
  end isnumeric;

e.g

  select isnumeric('10.1.1') from dual
  select isnumeric('10.1') from dual
  select isnumeric('-10.1') from dual

We hope this helps someone.

Returning Oracle SQL data as XML

Thursday, September 24th, 2009

Hi

An earlier post illustrated how to extract information from XML inside PL/SQL.  This post is an example of the reverse of this, in that it shows how to create an XML document from a SQL statement.  These examples are based on creating an XML document providing details of the tables in the SCOTT schema but could easily be adapted to suit your needs.

This first example extract the list of tables names and uses a combination of the XMLRoot, XMLElement, XMLAgg, XMLAttributes and XMLForest functions to create the document :

SELECT XMLRoot(
         XMLElement("Tables",
           XMLAgg(XMLElement("Table",
             XMLAttributes(table_name),
               XMLForest(owner,
                         tablespace_name,
                         status,
                         num_rows)))
       ), VERSION '1.0', STANDALONE YES).getClobVal() as XMLDATA
  from all_tables
 where owner = 'SCOTT'

This example returns a document like so :
<?xml version="1.0" standalone="yes"?>
<Tables>
  <Table TABLE_NAME="DEPT">
    <OWNER>SCOTT</OWNER>
    <TABLESPACE_NAME>USERS</TABLESPACE_NAME>
    <STATUS>VALID</STATUS>
    <NUM_ROWS>4</NUM_ROWS>
    </Table>

etc etc.

To make this a little more useful (and complex!) we can add a nested query to return the column details in-line with the table information, like this :

SELECT XMLRoot(
         XMLElement("Tables",
          XMLAgg(XMLElement("Table",
             XMLAttributes(at.table_name),
               XMLForest(at.owner,
                         at.tablespace_name,
                         at.status,
                         at.num_rows,
                         (select XMLAgg(XMLElement("Column",
                                   XMLAttributes(atc.column_name),
                                     XMLForest(atc.data_type,
                                               atc.data_length,
                                               atc.data_scale,
                                               atc.data_precision,
                                               atc.nullable)))
                           from all_tab_columns atc
                          where atc.owner = at.owner
                            and atc.table_name = at.table_name) as columns)))
         ), VERSION '1.0', STANDALONE YES).getClobVal() as XMLDATA
  from all_tables at
 where owner = 'SCOTT'

which returns an XML document like so :

<?xml version="1.0" standalone="yes"?>
<Tables>
  <Table TABLE_NAME="DEPT">
    <OWNER>SCOTT</OWNER>
    <TABLESPACE_NAME>USERS</TABLESPACE_NAME>
    <STATUS>VALID</STATUS>
    <NUM_ROWS>4</NUM_ROWS>
    <COLUMNS>
      <Column COLUMN_NAME="DEPTNO">
        <DATA_TYPE>NUMBER</DATA_TYPE>
        <DATA_LENGTH>22</DATA_LENGTH>
        <DATA_SCALE>0</DATA_SCALE>
        <DATA_PRECISION>2</DATA_PRECISION>
        <NULLABLE>N</NULLABLE>
      </Column>
      <Column COLUMN_NAME="DNAME">
        <DATA_TYPE>VARCHAR2</DATA_TYPE>
        <DATA_LENGTH>14</DATA_LENGTH>
        <NULLABLE>Y</NULLABLE>
      </Column>
      <Column COLUMN_NAME="LOC">
       <DATA_TYPE>VARCHAR2</DATA_TYPE>
       <DATA_LENGTH>13</DATA_LENGTH>
       <NULLABLE>Y</NULLABLE>
     </Column>
  </COLUMNS>
</TABLE>
etc etc.
I hope this is helpful to someone.  Most of the documentation that supports this post can be found here.

Creating and using primary keys on views in Oracle

Wednesday, September 2nd, 2009

Hi,

Occasionally it may be useful to have a primary / foreign key relationship between a view and table, where the view is the primary key referenced by a column in the table.

Oracle doesn’t let you do this quite as ‘completely’ as we’d probably like, but it at least let’s you put in the building blocks which may help the CBO and make your database design more concise or elegant in places.

Here is a quick example :

SQL> create view pk_view
(item_id, item_name,
constraint pk_test_view primary key (item_id) rely disable novalidate) 
as
select 1, 'One' from dual
union
select 2, 'Two' from dual
union
select 3, 'Three' from dual;
View created.
SQL> create table fk_view (
item_id number references pk_view disable novalidate);
Table created.
SQL>

There, that’s it.  Hope this helps someone as the create view syntax can be a little tricky.

oui.exe crash on Oracle 10.2.0.4 patch

Tuesday, May 12th, 2009

This is just a simple little post but one which was frustrating for us for a while earlier today.  When trying to run setup.exe to install the 10.2.0.4 patchset for Oracle 10g on a Windows XP Pro machine (this may happen on other flavours of Windows, I’m not sure) it pretty much immediately crashed with an error in oui.exe.

After much digging it turns out that the problem is simply that the path from which the setup.exe was ran had a space in it (as it was just on the Desktop so was actually in C:\Documents and Settings\<user>\Desktop.  Simply moving the file to a folder with no spaces (e.g C:\temp) fixed the issue and the upgrade proceeded as you would expect.

Returning an SQL result to a Shell script

Tuesday, March 10th, 2009

Hi

Often it can be useful to return the result of a simple SQL*Plus query to a shell script variable, for example to check for a value before deciding wether or not to proceed.  The syntax is actually pretty simple :

#!/bin/sh
count=$(sqlplus -s 'scott/tiger@xe'<<EOF
  set pages 0 feed off
  select count(*) from tab;
EOF
)
echo $count

Enjoy!

Updating Oracle tables using a ‘join’

Friday, January 9th, 2009

Doing a ‘join update’ in Oracle can sometime cause a few headaches. By ‘join update’ I mean the situation where we have two tables and want to update some of the rows in the first table based on values that exist in the second table.  

Imagine this scenario :

drop table t;
drop table s;
create table t (id number, name varchar2(32));
create table s (id number, name varchar2(32));

 

insert into t values (1,'1');
insert into t values (2,'2');
insert into t values (3,'3');
insert into t values (4,'4');
insert into t values (5,'5');

 

insert into s values (3,'three');
insert into s values (5,'five');
select * from t;
update t
   set name = (select name
                 from s
                where t.id = s.id);
select * from t;

Our first table (t) simply has the numbers 1-5 in the ‘id’ and ‘name’ column but we want to update some of the names based on the contents of the second table (s) but this table only has values for two of the rows.  This is OK so long as we can preserve the ‘default’ numeric values.  However the script above gives the following output :

drop table t succeeded.

drop table s succeeded.

create table succeeded.

create table succeeded.

1 rows inserted

1 rows inserted

1 rows inserted

1 rows inserted

1 rows inserted

1 rows inserted

1 rows inserted

ID                     NAME                             
---------------------- -------------------------------- 
1                      1                                
2                      2                                
3                      3                                
4                      4                                
5                      5                                

5 rows selected

5 rows updated

ID                     NAME                             
---------------------- -------------------------------- 
1                                                       
2                                                       
3                      three                            
4                                                       
5                      five                             
 
5 rows selected

The problem here is that the update isn’t a true ‘join’ as every row is going to be updated and if the ‘join’ fails then a null is written.  The traditional solution to this is to rewrite the SQL to be something like :

update t
   set name = (select name
                 from s
                where t.id = s.id)
 where exists (select 1
                 from s
                where t.id = s.id);
select * from t;

which does the job correctly :

2 rows updated
ID                     NAME 
---------------------- -------------------------------- 
1                      1 
2                      2 
3                      three 
4                      4 
5                      five

 

5 rows selected

However this isn’t a very good solution for updates involving very large tables as it can significantly slow things down.  One alternative is the MERGE statement :

merge into t
 using (select id, name
          from s) s
    on (t.id = s.id)
 when matched then
   update set t.name = s.name;

select * from t;
2 rows merged
ID                     NAME 
---------------------- -------------------------------- 
1                      1 
2                      2 
3                      three 
4                      4 
5                      five
5 rows selected

An other option is to use the NVL() function which allows you to deal with the problem of the nulls above :

update t
   set name = nvl((select name
                    from s
                   where t.id = s.id), t.name);

select * from t;

which also works :

5 rows updated

ID                     NAME 
---------------------- -------------------------------- 
1                      1 
2                      2 
3                      three 
4                      4 
5                      five
5 rows selected

 

Building an OCCI project using XCode 3.1

Thursday, October 2nd, 2008

With the release of the Instant Client for Mac OS X you can now build OCCI based applications targetting modern Macs using Leopard on Intel.  However, there is very little help available for building these apps using XCode, and if you hit the same problem we did (see below) then perhaps this post may be helpful.

Firstly download the Instant Client.  You will need the ‘Basic’ and ‘SDK’ packages.  We have unzipped them into ‘/instantclient_10_2′ on the root of the hard disk.  The ‘SDK’ should unzip into a folder within this one. The OCCI headers (and others) should then be in this path : ‘/instantclient_10_2/sdk/include’.  You can of course change this to suit your needs but this is what the walkthrough below is based on.

Before getting ’stuck in’ though you will need to create a couple if ‘links’ so that GCC can link to the OCCI libraries.  You can do this using a ‘Run Script’ in the project but I find it easier just to do this as a ‘one off’ now.  

  $ ln /instantclient_10_2/libocci.dylib.10.1 /instantclient_10_2/libocci.dylib
  $ ln /instantclient_10_2/libclntsh.dylib.10.1 /instantclient_10_2/libclntsh.dylib

So let's get started!

Firstly, create a new 'C++ Tool' project using XCode :

and give it a name, in this case, ‘occi’ :

You will then have your project open in XCode :

Next, double click on the item under ‘Targets’, in this case ‘occi’ to bring up the Target Info window.  Switch to the ‘Build’ tab :

Change the following settings (note the ‘Search’ box in the top right to help you find the settings ):

Other Linker Flags : -locci -lclntsh

Header Search Paths : /instantclient_10_2/sdk/include

Library Search Paths : /instantclient_10_2

Next, open your ‘main.cpp’ file and try this sample code :

#include <iostream>

/* Oracle */
#include <occi.h>

/* namespaces */
using namespace std;
using namespace oracle::occi;

/* Globals */
Environment * env;
Connection * conn;
 
int main(int argc, char * argv)
{
  // Connect
  env = Environment::createEnvironment(Environment::OBJECT);
  conn = env->createConnection("scott", "tiger", "//lcoalhost:1521/xe");
  Statement *stmt = conn->createStatement("SELECT COUNT(*) FROM TAB");
  ResultSet *rs=stmt->executeQuery();
  rs->next();
  string ntabs=rs->getString(1);
  cout << "Number of tables " << ntabs << endl;  
  conn->terminateStatement(stmt);
  // Close connection etc
  env->terminateConnection(conn);
  Environment::terminateEnvironment(env);  
  return 0;
}

Don’t forget to change the connection details to the relevant values for your setup.

Next, navigate to the ‘Executables’ folder of your project and double click on ‘occi’ to bring up the Executable Info page.  Change to the ‘Arguments’ tab and add an environment variable for DYLD_LIBRARY_PATH :

Set the value to ‘/instantclient_10_2′.

You should then be able to ‘Build and Go’.   You may want to set a breakpoint or open the Console (’Run -> Console’ from the XCode menu) so you can see the results :

And that should be it!   However…..

If you do something a little more advanced, which in our case meant using ‘ott’ (supplied as part of the ‘SDK’ package for the Instant Client) and building OCCI code based on the Oracle Spatial type (SDO_GEOMETRY) then you will probably get this error at link time :

Undefined symbols:  "oracle::occi::setVector(oracle::occi::AnyData&, 
                    __gnu_debug_def::vector<oracle::occi::Number, 
                    std::allocator<oracle::occi::Number> > const&)", referenced from:
      _main in occi.o
ld: symbol(s) not found
collect2: ld returned 1 exit status

After much digging the problem is with a couple of Preprocessor macros defined by default in XCode, _GLIBCXX_DEBUG=1 and _GLIBCXX_DEBUG_PEDANTIC=1.

Go into the ‘Build Settings’ for the target as described above, find the Preprocessor Macros entry and remove the two macro definitions.  Note that setting them to 0 does not seem to work.

You should then be good to go!  

Using Ajax to populate one field based on another in an ApEx form

Friday, June 20th, 2008

Often it can be useful to populate one field on a form based on the content of another in a live and dynamic fashion. Luckily this is easily achieved using the Javascript API supplied as part of ApEx 3.1.

This post illustrates an example where we wanted to lookup a value (called IMD Rank) based on the Postcode entered in another field. Users can either enter it manually, or click on a ‘Lookup’ button next to the field.

Firstly, define your process that will calculate the required value. This is most easily achieved by creating an Application Process (under Shared Components) which will run ‘on demand’.

This PL/SQL is just retrieving a value (RANKOFIMD) from a table based on a page item (P27_P9_POSTCODE). Note the ‘htp.prn()’ function call which will actually ’send’ the result to our page.

Next, define the JavaScript required to execute the process and return a value.  We’ll be displaying the returned value in the page item called P27_P16_IMDRANK. Add this script to the HTML Header section of your page.

Note : it is OK to append this script to any code which may already be in this field.

Next, add a button to invoke the JavaScript. I just added a standard HTML button after the element :

Clicking on the Lookup button now populates the field based on the value retrieved by the GetIMD application process.

Editting ApEx theme images using Mac OS X and WebDAV on XE

Friday, June 6th, 2008

Recently we had to make a couple of minor tweaks to a couple of ApEx theme images stored in an XE installation.  At first we scratched heads a little but then found a helpful post on the ApEx forum which made it much easier.  All we needed to do was work out how to do this using Mac OS X Leopard.  Luckily, it’s also very easy!

Firstly open a Finder winder then choose Go -> Connect to Server from the Finder Menu :

Connect to Server

Enter the server name and port that the embedded HTTP gateway is listening on (8080 by default) and click on Connect.  You will then need a database user name that has permission to modify the ApEx schema (FLOWS_*).  A DBA user name or the FLOWS_* user name and password should work fine :

WebDAV File System Authentication

With this done, click OK and logon to the database. A new Finder window will then appear showing the ‘root’ of the database WebDAV file system :

You can now navigate through the tree to find the image in question by following through i -> themes -> theme X :

WebDAV theme path

I haven’t yet worked out how to edit the images ‘in place’ but it’s easy enough to copy the required images out to a folder on your hard disk, edit them, and then copy them back into the appropriate folder.

Change the background colour of an ApEx page

Wednesday, June 4th, 2008

The simplest way to change the background colour of an ApEx page is to edit the <body> template using the Application Builder.

Login to ApEx and navigate to your application.  Click on ‘Shared Components’, and then ‘Templates’ to view the templates for the current application theme.

ApEx templates

Navigate down to find the appropriate page template that you are using (tip: the ‘References’ column holds a count of the number of uses for a template, so if you’re not sure look for something greater than zero).  When you’ve found your template, click on it to edit the HTML elements of the template :

Edit template HTML header

Here you can see we’re using the ’style’ tag to set the background colour (using background-color) to the hex colour #46C6BA.  Standard HTML colour names such as ‘white’, ‘blue’ etc can also be used here.