Oracle and batch reporting

My first real exposure to a real database was Sybase.  It actually is a fairly nice “little” database that you can come to grips with.  I mean little when compared to behemoth that is Oracle.  I don’t want to go against the underdog, but it is pretty amazing the stuff that Oracle packs into their solution.  In addition to a relational database with all the friendly editors for creating tables and procedures, they have tools for the command line junkie as well.

How I always end up back at the command prompt is a mystery to me.  Yet, despite finding Oracle to be a pretty large system they did provide a simple little command line tool which can be used for querying data from the database.

This little command line tool is called sqlplus.  With this, I can extract some data from the database and manipulate it myself and then load it right back in.  The task tends to be a small one off, so there is not much point in writing a complicated program using JDBC.

As Sqlplus is a command line tool it is just as plain as you might imagine.  When you log in, you are simply left at a prompt to enter your SQL commands.  There are no GUI’s or wizards to help out, but either by design or accident those fine folks at Oracle tucked in quite a few formatting options to make this an excellent tool not only for performing queries but also for extracting data or even producing small reports.

Setup

At the end of the article is a list of all the scripts used to create these tables along with populating them.

Starting sqlplus is trivial if your environment is already properly setup.  Sorry, setting up Oracle is beyond the scope of this article.

sqlplus user/password@tnsid

<disclaimer>This is not even remotely safe to put the password into a script as some hacker or disgruntled employee will find the user and password information and undoubtedly do naughty things</disclaimer> This is not much better if used on the command prompt it could also be found by someone examining the password history.  That isn’t an important issue for a developer on a development machine just to keep in mind for production.

Command Line

More than just a simple command line tool, this one can also be used in scripts.  Simply run sqlplus but also pass the name of a parameter file to run as its argument.

sqlplus user/password@tnsid  @mysqlquery.sql

Sqlplus will take this file as a list of input and begin to process each line in the file as if you were typing it.  Don’t forget to add a “quit” statement as the last line of your script otherwise, sqlplus will stay at the prompt after running the script.

Nobody is perfect, not even the defaults that were chosen by oracle for outputting query data.

Oracle Database 11g Enterprise Edition Release 11.2.0.4.0 - 64bit Production
With the Partitioning, Real Application Clusters, Automatic Storage Management, OLAP,
Data Mining and Real Application Testing options

SQL> SELECT * FROM dbo.conair_emp;

FIRST
----------------------------------------
LAST                                             IQ
---------------------------------------- ----------
EMAIL
--------------------------------------------------------------------------------
max
musterman                                        98
max@musterman.com

mathew
doe                                             115
mathew@doe.com

FIRST
----------------------------------------
LAST                                             IQ
---------------------------------------- ----------
EMAIL
--------------------------------------------------------------------------------

mark
doe                                             115
mark@doe.com

luke
doe                                             115

It is good that there are options for doing a bit of formatting the raw query data because the default values that are chosen by default are pretty hideous.

When changing the default output format it is actually possible to create simple reports at the same time.

Simple reporting

The most important part of any report, formal or informal is some way to save the actual report data.  It is possible to run the sqlplus command for a given sql statement but unless the output is redirected to a file, it will be lost forever.  So, perhaps the most important statement is how to redirect the output to a file.  This is done using the spool command.  us has the spool command which when activated will redirect the output to the filename given in the current directory.

Command Parameter Description
spool <filename> Puts all screen output into the file named <filename>
spool off stops logging output to the spool file.

Never forget to turn off your spooling or otherwise your entire interactive session will end up in your output.

Beyond saving the output, we need to get it out of the database in a format that is not the ugly default format of sqlplus.  The simplest and most effective way to make the output more friendly is to change the format for each field of the report.  This is done by using the “column” command.

When using A32 or 999 is mainly just setting a length for the fields that will be displayed.  There is no real formatting take place.

Command Parameter Description
column format The actual column format to use
column format A32 Formats the output as a alpha numeric field 32 characters long.

That doesn’t mean it is not possible for some simple formatting of numeric values.  The comma and period can be used to format both larger numbers or fractional values to display in a more user friendly manner.

Command Parameter Description
column format 999.99 Formats the output as an floating value that will take up at most three columns (ie 0 – 999) with at most two decimal places
column format 9,999.99 Formats the output as a floating point value that is at most four digits with two decimal places.  This will also put a comma in for the thousands separator.

It might be possible to do this with European formatting but I didn’t find it necessary to do that and so I don’t have any further information on how do it.

Thus by simply adding formatting to the fields, the report becomes instantly friendly.

column first format A15
column last format A15
column iq format 9,999.99
column email format A32

spool report1.rpt
select * from conair_emp;
spool of

The information no longer wraps on the screen, the columns are not much wider than the actual information and everything looks pretty tidy.

FIRST           LAST            IQ        EMAIL
--------------- --------------- --------- --------------------------------
max             musterman           98.00 max@musterman.com
mathew          doe              2,115.00 mathew@doe.com
mark            doe              1,515.00 mark@doe.com
luke            doe                115.00 luke@doe.com
john            doe                978.00 john@mydomain.com
john            lennon             115.00 johnlennon@beatles.com
paul            mccartney        9,115.00 paulmccartney@beatles.com
george          harrison           115.00 georgeharrison@beatles.com
ringo           star               115.00 ringostar@beatles.com

9 rows selected 

This is by no means everything that can be done for the different columns of our report.  It is also possible to change the headings at the same time we are changing the formats.  This might be not as necessary in this particular report as the column names pretty much match the values that are selected.

column first heading 'First name' format A15
column last  heading 'Last name'  format A15
column iq    heading 'Hourly wage|in thousands' format 9,999.99
column email heading 'Email Addr' format A32

spool report1.rpt
select * from conair_emp;
spool of

Not every column name was quite perfect, this more a feature of the idiot person who actually created the database to begin with.

First name      Last name         Hourly wage  Email Addr 
                                  in thousands
--------------- ----------------- ------------ --------------------------------
max             musterman                98.00 max@musterman.com
mathew          doe                   2,115.00 mathew@doe.com
mark            doe                   1,515.00 mark@doe.com
luke            doe                     115.00 luke@doe.com
john            doe                     978.00 john@mydomain.com
john            lennon                  115.00 johnlennon@beatles.com
paul            mccartney             9,115.00 paulmccartney@beatles.com
george          harrison                115.00 georgeharrison@beatles.com
ringo           star                    115.00 ringostar@beatles.com

9 rows selected 

It is also possible to create multi-line descriptions for a column by using the pipe symbol to mark where the field name should be split.

If the report was just a very small report or used for some very casual internal purpose then this would probably be enough.  We have our data, each column is understandable and the important values are formatted in some manner.

This isn’t the limit of what can be done for reporting.  Just like most reports it is possible to add titles and footer information to the report.  this is done using the ttitle and btitle commands.  This report will actually be printed to paper so we change the length so it can be properly paginated.  This is done using the pagesize command.

set pagesize 20
set heading on 
set linesize 80
set trimspool on

ttitle center "company wage report " skip 2
btitle left "-----------------------------------------------------------------------------" skip 1 -
left "confidential information - internal " -
right "page " format 999 sql.pno

column first heading 'First name' format A15
column last  heading 'Last name'  format A15
column iq    heading 'Hourly wage|in thousands' format 9,999.99
column email heading 'Email Addr' format A32

spool report1.rpt
select * from conair_emp;
spool of

I have made the arbitrary decision that the page will only be 20 lines long as I don’t want to fill my blog up with a lot of blank lines.  however, this command will ensure that the page is the proper length and is padded to the end of the page.

                              company wage report

                                 Hourly wage
First name      Last name       in thousands Email Addr
--------------- --------------- ------------ --------------------------------
max             musterman              98.00 max@musterman.com
mathew          doe                 2,115.00 mathew@doe.com
mark            doe                 1,515.00 mark@doe.com
luke            doe                   115.00 luke@doe.com
john            doe                   978.00 john@mydomain.com
john            lennon                115.00 johnlennon@beatles.com
paul            mccartney           9,115.00 paulmccartney@beatles.com
george          harrison              115.00 georgeharrison@beatles.com
ringo           star                  115.00 ringostar@beatles.com



-----------------------------------------------------------------------------
confidential information - internal                                    page    1

9 rows selected.

There are two more commands that can be used depending on how big the report is expected to be.  The first is the “clear screen” command.  This will just clear the screen when it is encountered.  this is probably quite useful for some very tiny report that will give a few lines of information.  The cleared screen makes the information easy to find on your terminal session.

The second command is the “termout” command.  This when this command is set to off, then no more output will be displayed on the terminal screen.  This would be a good command when hundreds or thousands of lines of output are expected.

It is also possible to calculate totals and have breaks between types of data, but I will leave that for another time.

Extracting data

Actually most of the general reporting could be used in the extracting of data.  We might want to do some formatting of our data but more likely than not we might not be as interested in the headings.  It is possible to do a simple query and basically drop the headings and we have an output format.

Dropping the headings is done by setting the pagesize to zero.  This will then cause no page breaks and it will automatically suppress the headings.

max                     musterman                       98 max@musterman.com
mathew                  doe                           2115 mathew@doe.com
mark                    doe                           1515 mark@doe.com
luke                    doe                            115 luke@doe.com
john                    doe                            978 john@mydomain.com
john                    lennon                         115 johnlennon@beatles.com
paul                    mccartney                     9115 paulmccartney@beatles.com
george                  harrison                       115 georgeharrison@beatles.com
ringo                   star                           115 ringostar@beatles.com


9 rows selected.

This actually creates a very simple fixed length record that can be parsed by your program.  just break each line into its composite fields when extracting. I don’t know a lot of developers who are still interested in fixed length output data.  Sure you can parse it but why when you can have a comma separated file.

Here too, oracle helps.  it is possible to set the field separator.

set colsep ‘;’

This will add a semicolon to our output automatically turning it into csv data.

max                    ;musterman              ;        98;max@musterman.com
mathew                 ;doe                    ;      2115;mathew@doe.com
mark                   ;doe                    ;      1515;mark@doe.com
luke                   ;doe                    ;       115;luke@doe.com
john                   ;doe                    ;       978;john@mydomain.com
john                   ;lennon                 ;       115;johnlennon@beatles.com
paul                   ;mccartney              ;      9115;paulmccartney@beatles.com
george                 ;harrison               ;       115;georgeharrison@beatles.com
ringo                  ;star                   ;       115;ringostar@beatles.com

9 rows selected.

Yes, now our data has now been turned into the ugliest comma separated data ever.

Note: the semicolon is the csv file delimiter in excel in some countries.

Extra Credit

There is another way to run these scripts without adding a quit command.  Simply echo the password into the sqlplus command when running a script.  Sqlplus recognizes that it is in batch mode and quits when the commands are finished.

echo secretpass | sqlplus myuser@cdatabase @commands.sql

This might be marginally safer but not by much.  If this were a script, then your password is still in the script to be found by someone.

There is also a better way to create our csv file.  It requires a bit more effort when creating our script.  Simply concatenate each field that you wish to have into a single string.

SELECT first || ‘,’ || last|| ‘,’ || iq|| ‘,’ || email FROM dbo.conair_emp;

max       ,musterman ,98,max@musterman.com
mathew    ,doe       ,2115,mathew@doe.com
mark      ,doe       ,1515,mark@doe.com
luke      ,doe       ,115,luke@doe.com
john      ,doe       ,978,john@mydomain.com
john      ,lennon    ,115,johnlennon@beatles.com
paul      ,mccartney ,9115,paulmccartney@beatles.com
george    ,harrison  ,115,georgeharrison@beatles.com
ringo     ,star      ,115,ringostar@beatles.com

The data is really not much nicer to look at than our previous csv attempt but that is hardly the databases, nor even sqlplus’s fault.  If the table had been created with varchar fields instead of char fields, then the data would not contain the trailing spaces.

max,musterman,98,max@musterman.com
mathew,doe,2115,mathew@doe.com
mark,doe,1515,mark@doe.com
luke,doe,115,luke@doe.com
john,doe,978,john@mydomain.com
john,lennon,115,johnlennon@beatles.com
paul,mccartney,9115,paulmccartney@beatles.com
george,harrison,115,georgeharrison@beatles.com
ringo,star,115,ringostar@beatles.com

It isn’t the fact you can do a query from the command prompt that is so amazing but some of the options that you can turn on to turn a small query into a small report, or a small data extract.

 

Setup Scripts

create.sql

drop table conair_emp;
commit;
/
create table conair_emp
(
first char(10) not null,
last char(10) not null,
iq float not null,
email char(30) not null
);
commit;
/
prompt
prompt test table conair_emp created
describe conair_emp

 

fill.sql

begin
delete from conair_emp;
commit;
insert into conair_emp (first,last,iq,email) values('max','musterman',98,'max@musterman.com');

insert into conair_emp (first,last,iq,email) values('mathew','doe',2115,'mathew@doe.com');
insert into conair_emp (first,last,iq,email) values('mark','doe',1515,'mark@doe.com');
insert into conair_emp (first,last,iq,email) values('luke','doe',115,'luke@doe.com');
insert into conair_emp (first,last,iq,email) values('john','doe',978,'john@mydomain.com');

insert into conair_emp (first,last,iq,email) values('john','lennon',115,'johnlennon@beatles.com');
insert into conair_emp (first,last,iq,email) values('paul','mccartney',9115,'paulmccartney@beatles.com');
insert into conair_emp (first,last,iq,email) values('george','harrison',115,'georgeharrison@beatles.com');
insert into conair_emp (first,last,iq,email) values('ringo','star',115,'ringostar@beatles.com');
commit;
end;
/

 

report1.sql

clear screen

column first format A15
column last format A15
column iq format 9,999.99
column email format A32

set term on
spool report1.rpt
SELECT * FROM dbo.conair_emp;

spool off

 

report2.sql

clear screen

column first heading 'First name'  format A15
column last heading 'Last name'  format A15
column iq heading 'Hourly wage|in thousands'  format 9,999.99
column email heading 'Email Addr'  format A32

set term on
spool report2.rpt
SELECT * FROM dbo.conair_emp;

spool off

 

report3.sql

SET PAGESIZE 20
SET HEADING on

SET LINESIZE 80
set trimspool on
set termout off

ttitle center "company wage report " skip 2
btitle left "-----------------------------------------------------------------------------" skip 1 -
left "confidential information - internal " -
right "page " format 999 sql.pno

column first heading 'First name'  format A15
column last  heading 'Last name'  format A15
column iq    heading 'Hourly wage|in thousands'  format 9,999.99
column email heading 'Email Addr'  format A32

spool report3.rpt
SELECT * FROM dbo.conair_emp;
spool off

 

export1.sql

SET PAGESIZE 0
SET LINESIZE 999
set trimspool on

spool export1.out
SELECT * FROM dbo.conair_emp;
spool off

 

export2.sql

SET PAGESIZE 0
SET LINESIZE 999
set trimspool on
set colsep ';'

spool export2.out
SELECT * FROM dbo.conair_emp;
spool off

quit

 

export3.sql

SET PAGESIZE 0
SET LINESIZE 999
set trimspool on
set colsep ';'

spool export3.out
SELECT first || ',' || last|| ',' || iq|| ',' || email FROM dbo.conair_emp;
spool off

quit

 

This entry was posted in Command line, programming. Bookmark the permalink.

3 Responses to Oracle and batch reporting

  1. Quite nice post, I definitely adore this site, keep on it.

  2. Jane says:

    It is perfect time to make some plans for the future and it is time to be happy.
    I have read this post and if I could I wish to suggest
    you few interesting things or suggestions. Maybe you could write next articles referring to this article.
    I wish to read more things about it!

Comments are closed.