Wednesday, May 28, 2008

Tip#13 Big Vs Little Endian

Little Endian means that the low-order byte of the number is stored in memory at the lowest address, and the high-order byte at the highest address. (The little end comes first.) For example, a 4 byte LongInt

Byte3 Byte2 Byte1 Byte0

will be arranged in memory as follows:
Base Address+0 Byte0
Base Address+1 Byte1
Base Address+2 Byte2
Base Address+3 Byte3

Linux, Windows use "Little Endian" byte order.


Big Endian means that the high-order byte of the number is stored in memory at the lowest address, and the low-order byte at the highest address. (The big end comes first.) Our LongInt, would then be stored as:

Base Address+0 Byte3
Base Address+1 Byte2
Base Address+2 Byte1
Base Address+3 Byte0

Solaris, HPUX, Apple Mac use "Big Endian" byte order.


In Oracle 10g, following SQL should tell you which operating systems follow which byte order,

select * from v$transportable_platform order by platform_id;

To transport tablespaces between OS with same byte orders (endianness), we don’t need conversion in other cases we do which can be done using RMAN. E.g. I need to transport a Tablespace from Linux (Little Endian) to Solaris (Big Endian)

RMAN> convert tablespace XXX to
platform ‘Solaris[tm] OE (64-bit)'
db_file_name_convert '/app/oracle/oradata/mbs’,
'/app/oracle/rman_bkups';

Now this file can be copied over to the target Solaris system, and the rest of the steps are easy.

Monday, April 28, 2008

Tip#12 STATUS column of dba_undo_extents

I am sure you might have seen loads of information regarding how to resize/drop/recreate UNDO tablspace but point to note during dropping (ex-default/previous) UNDO tablespace is to check a view dba_undo_extents to see if any undo statments is still used by any session or needed for flashback. Following SQL should tell you if you can go ahead and drop the ex-default/previous UNDO tablespace or not (assuming undo tablespace I want to drop is UNDOTBS2),

SELECT tablespace_name, status, COUNT (*)
FROM SYS.dba_undo_extents
WHERE tablespace_name = 'UNDOTBS2'
GROUP BY tablespace_name, status

What is of our interest is status column of dba_undo_extents, 3 possible values - ACTIVE, EXPIRED, UNEXPIRED. What are the meanings ?

ACTIVE means that this undo segment contains active transactions

EXPIRED means that this segment is not rqeuired at all after considering Undo retention period

UNEXPIRED means that this segment does not contain any active transactions but it contains transactions which are still required for Flashback option (after considering Undo retention period).

So if you have all extents EXPIRED then go ahead and drop the tablespace, as usual make sure that you have done proper backup before and after.

Wednesday, April 9, 2008

Tip#11 SQL*Plus - Ignore Blank Lines in the Script

Recently I came across a situation where during our database deploy one of our View script was exiting in SQLplus due to a blank line inside the script. To fix it, we had to set sqlblanllines setting in sqlplus to ignore the blank lines and that did the trick for us.

SQL> create or replace view xx
2 as
3 select
4 sysdate as x
5
SQL> from
SP2-0042: unknown command "from" - rest of line ignored.
SQL> dual
SP2-0042: unknown command "dual" - rest of line ignored.
SQL> set sqlblanklines on
SQL> create or replace view xx
2 as
3 select
4 sysdate as x
5
6 from
7 dual
8 ;

View created.

SQL>

Friday, April 4, 2008

Tip#10 Dump to text file using UTL_FILE

Dump the data from table/sql to text file (CSV or PIPE delimated etc) on the server using UTL_FILE

Note :
A) Specify the accessible directories for the UTL_FILE functions in the initialization file using the UTL_FILE_DIR parameter. e.g. : UTL_FILE_DIR = /app/oracle/my_dir
FYI, UTL_FILE_DIR = * means turn off directory access checking, and it makes any directory accessible to the UTL_FILE (NOT RECOMMANDED).

EDIT: if you are using > 9i , you can use DIRECTORY instead of UTL_FILE_DIR.

B) Since we have data with special character, we open file with FOPEN_NCHAR to make sure we don’t loose special characters in the text dump.

C) I have just tested it on Redhat Linux - Oracle 9.2.0.6

This is the Function which I use (adapted to my requirements but thanks to Tom Kyte to get me started)

CREATE OR REPLACE FUNCTION dump_to_file (
p_query IN VARCHAR2,
p_separator IN VARCHAR2 DEFAULT '|',
p_dir IN VARCHAR2,
p_filename IN VARCHAR2
)
RETURN NUMBER
IS
l_filehandle UTL_FILE.file_type;
l_thecursor INTEGER DEFAULT DBMS_SQL.open_cursor;
l_columnvalue VARCHAR2 (32767);
l_col_hdr VARCHAR2 (32767);
l_status INTEGER;
l_colcnt NUMBER DEFAULT 0;
l_separator VARCHAR2 (10) DEFAULT '';
l_cnt NUMBER DEFAULT 0;
col_cnt PLS_INTEGER;
rec_tab DBMS_SQL.desc_tab;
col_num NUMBER;
BEGIN
-- open a writable file in the UTL_FILE_DIR directory
l_filehandle := UTL_FILE.fopen_nchar (p_dir, p_filename, 'w', 32767);

-- parse the sql we got
DBMS_SQL.parse (l_thecursor, p_query, DBMS_SQL.native);

-- execute the sql cursor
l_status := DBMS_SQL.EXECUTE (l_thecursor);

-- get the column information l_colcnt OUT param to get us number of columns
-- and rec_tab is plsql table with all column related information like name, length,schema, precision, scale etc
DBMS_SQL.describe_columns (l_thecursor, l_colcnt, rec_tab);

-- we need to print the column names as the first line
l_separator := '';
l_col_hdr := '';


FOR i IN 1 .. l_colcnt
LOOP
-- we define which columns we need in the fetch
DBMS_SQL.define_column (l_thecursor, i, l_columnvalue, 4000);
-- print the column name in the file
l_col_hdr := l_col_hdr || l_separator || rec_tab (i).col_name;
l_separator := p_separator;
END LOOP;

UTL_FILE.put_nchar (l_filehandle, l_col_hdr);
UTL_FILE.new_line (l_filehandle);

-- lets print the data now
LOOP
EXIT WHEN (DBMS_SQL.fetch_rows (l_thecursor) <= 0); l_separator := ''; FOR i IN 1 .. l_colcnt LOOP DBMS_SQL.COLUMN_VALUE (l_thecursor, i, l_columnvalue); UTL_FILE.put_nchar (l_filehandle, l_separator || l_columnvalue); l_separator := p_separator; END LOOP; UTL_FILE.new_line (l_filehandle); l_cnt := l_cnt + 1; END LOOP; DBMS_SQL.close_cursor (l_thecursor); UTL_FILE.fclose (l_filehandle); RETURN l_cnt; EXCEPTION WHEN UTL_FILE.invalid_operation THEN UTL_FILE.fclose (l_filehandle); raise_application_error (-20061, 'Dump To File Error: Invalid Operation'); WHEN UTL_FILE.invalid_filehandle THEN UTL_FILE.fclose (l_filehandle); raise_application_error (-20062, 'Dump To File Error: Invalid File Handle'); WHEN UTL_FILE.write_error THEN UTL_FILE.fclose (l_filehandle); raise_application_error (-20063, 'Dump To File Error: Write Error'); WHEN OTHERS THEN UTL_FILE.fclose (l_filehandle); RAISE; END dump_to_file;


How to Use above function ?

To generate pipe (‘ | ’) delimated file called emp.txt in /app/oracle/my_dir for my query, I execute something like following

DECLARE
l_rows NUMBER := 0;
l_dir VARCHAR2 (100) := '/app/oracle/my_dir';
BEGIN
l_rows := dump_to_file ('select * FROM scott.emp ', '|', l_dir, 'emp.txt');
DBMS_OUTPUT.put_line ('Number of rows dumped to emp.txt : ' || l_rows);
END;

EDIT:
if you want to use DIRECTORY instead of UTL_FILE_DIR, just create one like following,

create or replace directory my_dir as '/app/oracle/my_dir';

then use above code with 'my_dir' instead of l_dir.


Tuesday, April 1, 2008

Tip#9 Some OS utilities (Unix)

NOHUP :

nohup (no hangups) is a UNIX command that allows you to execute a command in the background while you are logged into UNIX. The background process will continue to run until completion, even if you log off. For long running processes, this is a nice way to execute them. In my case, it allows me to log into a client server remotely, execute a process interactively with nohup, and then disconnect. Another benefit of nohup is that by default, a log of all activity is recorded in the current directory in the file nohup.out . e.g.

nohup sqlplus user/pwd @sqlscript &

Note : & at the end is for running the process in the background

SKILL:

Sometime I have a situations where I find that a process is consuming a lot of CPU and memory, but I don't want to kill it and I just wished if I could just 'pause' the process and that's where SKILL really helps me. e.g Process ID 1234 is taking too much resources and I need to somehow get other 'important' things done I will execute following command,

skill -STOP 1234

Once I have finished other 'important' things done I can free the process with following command

skill -CONT 1234

The nice feature is you can pass not just PID but also User, Terminal ID OR Command which makes it more powerful then I initially thought. e.g. You can 'pause' all RMAN commands on DB server with

skill -STOP rman

SNICE:

The command snice is similar to skill but Instead of stopping a process it makes its priority a lower one. e.g. for the same heavy process 1234 I could decrease the priority by using something like this,

snice +4 -p 1234

Note: +4 means we are increasing the Nice Value and effectively the higher the number, the lower the priority.

This utility is quite useful in reducing priorities.

Wednesday, September 26, 2007

Tip#8 Cursor best practice

Cursor best practice

Oracle provides three possible way to loop through the cursor rows and these are,

  1. loop with explicit cursor i.e. OPEN, FETCH one row at a time and CLOSE
  1. Using cursor FOR-LOOP
  1. Bulk collect and then loop through the collection.

Which of the above methods is most efficient for a cursor?

Without a doubt, you should use BULK COLLECT whenever possible to query information from your tables. It will be significantly faster than either a cursor FOR loop or loop with explicit cursor. However, a BULK COLLECT of many rows can consume a large amount of memory. But you can balance memory utilization against performance improvements by using the LIMIT clause with BULK COLLECT. Here's an example:

DECLARE
CURSOR allrows_cur
IS
SELECT *
FROM order_header_scratch;

TYPE order_header_scratch_aat IS TABLE OF order_header_scratch%ROWTYPE
INDEX BY BINARY_INTEGER;

l_order_header_scratch order_header_scratch_aat;
l_row PLS_INTEGER;
BEGIN
OPEN allrows_cur;

LOOP
FETCH allrows_cur
BULK COLLECT INTO l_order_header_scratch LIMIT 100;

-- Remember: BULK COLLECT will NOT raise NO_DATA_FOUND if no rows
-- are queried. Instead, check the contents of the collection to
-- see if you have anything left to process.
EXIT WHEN l_order_header_scratch.COUNT = 0;

-- Process the data, if any.
l_row := l_order_header_scratch.FIRST;

WHILE (l_row IS NOT NULL)
LOOP
-- Some processing ....
process_order_header_scratch (l_order_header_scratch (l_row));

-- get the next row
l_row := l_order_header_scratch.NEXT (l_row);
END LOOP;
END LOOP;

-- Clean up when done: close the cursor and delete everything
-- in the collection.
CLOSE allrows_cur;

l_order_header_scratch.DELETE;
END;


EDIT : I just happen to read an interesting article regarding cursor for-loop and would certainly suggest to read it. LINK .

Also I forgot to mention why bulk collect is faster compare to other, the biggest advantage of BULK COLLECT is: BULKING! obviously :)

In the cursor Oracle must refresh the binds every loop with the new data. Also the Insert is executed each time! With BULK COLLECT Oracle can put all the values into a collection at once. No need to refresh something again and again hence no soft parses, reading of the new values of the binds…


Monday, September 17, 2007

Tip#7 SQL*Plus Command Line History


Thanks to Howard Rogers( Link ) , I got a very useful utility to have command line history for SQL*Plus on Linux. This small utility makes life so much easier for me especially as most of the time I do work on Linux platform. All you have to do is

1. Download a rpm (Link)

2. As root user, Install it -> rpm -ivh rlwrap-0.18-1.i386.rpm

3. Create alias for SQL*Plus in the user profile (Bash -> .bash_profile , Ksh -> .profile )
alias sqlplus='rlwrap sqlplus'

This will make sure that every time you login and run sql*Plus, "rlwrap" captures all SQL*Plus commands which you can just recall with up/down arrow key. Other nice feature is search capability of this utility, you can press Ctrl+R and type in key word to search for in your historical SQLs.

I really find this utility very helpful, hope it helps you too!!!