Showing posts with label Oracle. Show all posts
Showing posts with label Oracle. Show all posts

Nov 9, 2008

About sys_Context Function

In Oracle/PLSQL, the sys_context function can be used to retrieve information about the Oracle environment.
The syntax for the sys_context function is:

sys_context( namespace, parameter, [ length ] )

namespace is an Oracle namespace that has already been created. If the namespace of 'USERENV' is used,
attributes describing the current Oracle session can be returned.
parameter is a valid attribute that has been set using the DBMS_SESSION.set_context procedure.length is optional.
It is the length of the return value in bytes. If this parameter is omitted or if an invalid entry is provided,
the sys_context function will default to 256 bytes.

The valid parameters for the namespace called 'USERENV' are as follows:

Parameter Explanation Return Length
AUDITED_CURSORIDReturns the cursor ID of the SQL that triggered the auditN/A
AUTHENTICATION_DATAAuthentication data256
AUTHENTICATION_TYPEDescribes how the user was authenticated. Can be one of the following values: Database, OS, Network, or Proxy30
BG_JOB_IDIf the session was established by an Oracle background process, this parameter will return the Job ID. Otherwise, it will return NULL.30
CLIENT_IDENTIFIERReturns the client identifier (global context)64
CLIENT_INFOUser session information64
CURRENT_SCHEMAReturns the default schema used in the current schema30
CURRENT_SCHEMAIDReturns the identifier of the default schema used in the current schema30
CURRENT_SQLReturns the SQL that triggered the audit event64
CURRENT_USERName of the current user30
CURRENT_USERIDUserid of the current user30
DB_DOMAINDomain of the database from the DB_DOMAIN initialization parameter256
DB_NAMEName of the database from the DB_NAME initialization parameter30
ENTRYIDAvailable auditing entry identifier30
EXTERNAL_NAMEExternal of the database user256
FG_JOB_IDIf the session was established by a client foreground process, this parameter will return the Job ID. Otherwise, it will return NULL.30
GLOBAL_CONTEXT_MEMORYThe number used in the System Global Area by the globally accessed contextN/A
HOSTName of the host machine from which the client has connected54
INSTANCEThe identifier number of the current instance30
IP_ADDRESSIP address of the machine from which the client has connected30
ISDBAReturns TRUE if the user has DBA privileges. Otherwise, it will return FALSE.30
LANGThe ISO abbreviate for the language62
LANGUAGEThe language, territory, and character of the session. In the following format:
language_territory.characterset
52
NETWORK_PROTOCOLNetwork protocol used256
NLS_CALENDARThe calendar of the current session62
NLS_CURRENCYThe currency of the current session62
NLS_DATE_FORMATThe date format for the current session62
NLS_DATE_LANGUAGEThe language used for dates62
NLS_SORTBINARY or the linguistic sort basis62
NLS_TERRITORYThe territory of the current session62
OS_USERThe OS username for the user logged in30
PROXY_USERThe name of the user who opened the current session on behalf of SESSION_USER30
PROXY_USERIDThe identifier of the user who opened the current session on behalf of SESSION_USER30
SESSION_USERThe database user name of the user logged in30
SESSION_USERIDThe database identifier of the user logged in30
SESSIONIDThe identifier of the auditing session30
TERMINALThe OS identifier of the current session10

Applies To:

  • Oracle 8i, Oracle 9i, Oracle 10g, Oracle 11g
For example:
sys_context('USERENV', 'NLS_DATE_FORMAT') would return 'RR-MM-DD'
sys_context('USERENV', 'NLS_SORT') would return 'BINARY'

How to set the NLS_LANG in Unix Environments

Subject: The correct NLS_LANG setting in Unix Environments

In this Document
Goal
Solution
References
--------------------------------------------------------------------------------
Applies to: Oracle Server - Enterprise Edition - Version: 8.1.7.4 to 10.2.0.0
Information in this document applies to any platform.
GoalHow to set the NLS_LANG correctly in Unix environments:

-1- Check which locale is set and correct this if needed.
-2- Check if the choosen locale is indeed installed and install this if needed.
-3- Check if you telnet/ssh software is properly set up.
-4- Set the NLS_LANG and Test.
-5- What to do if this is not working?
-6- More in depth debugging.

Solution

-1- Check which locale is set and correct this if needed.
---------------------------------------------------------
Please note that the ouput of this command is not exactly the same on all the Unix environments.
Contact your Unix vendor if you have questions.
The example used here is to configure your unix environment so that you can use Unicode (UTF8) with sqlplus on your unix box.
To see your current setup, use the "locale" command like this:

$ locale

example of output:

LANG=fr_FR
LC_CTYPE="fr_FR.iso885915@euro"
LC_COLLATE="fr_FR.iso885915@euro"
LC_MONETARY="fr_FR.iso885915@euro"
LC_NUMERIC="fr_FR.iso885915@euro"
LC_TIME="fr_FR.iso885915@euro"
LC_MESSAGES="fr_FR.iso885915@euro"
LC_ALL=fr_FR.iso885915@euro

Most Unix versions have this as default:
$ locale

LANG=
LC_CTYPE="C"
LC_COLLATE="C"
LC_MONETARY="C"
LC_NUMERIC="C"
LC_TIME="C"
LC_MESSAGES="C"
LC_ALL=

"C" means US7ASCII , this implies that you can only display a-z, A-Z and 0-9 and nothing else.

We recommend to use UTF-8 when possible, this should look like:

$ locale

LANG=en_US
LC_CTYPE="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_ALL=en_US.UTF-8

When you have choosen a value, for example "en_US.UTF-8" on Linux, you can set it like this:

$ export LC_ALL=en_US.UTF-8
or
% setenv LC_ALL en_US.UTF-8

However you should set the needed variables in the user ".profile" file and then log out an in agian and check if the "locale" command returns the wanted output.
If you have questions on how to configure a user environment on your specific Unix/Linux flavor please consult your OS vendor.

-2- Check if the choosen locale is indeed installed and install this if needed.
-------------------------------------------------------------------------------
Please *do* check that you have the defined locale really installed.
To see all installed locales issue:
$ locale -a
sample output:
$ locale -a
POSIX
common
en_US.UTF-8
C
iso_8859_1
iso_8859_15
en_CA
en_CA.ISO8859-1
en_US
en_US.ISO8859-1
en_US.ISO8859-15
en_US.ISO8859-15@euro
fr_CA
fr_CA.ISO8859-1
th
th_TH
th_TH.TIS620
ja

This lists all installed locales for the unix box, for example "fr_FR.iso885915@euro" is missing in the list, so if you want to use it then you need to install this first.
If you set the user environment to a locale that is not installed you will not get an error but it will not work.
Please also note that you need to have installed the exact locale , if you have "fr_FR.UTF8" or "UTF-8" installed but want to use "en_US.UTF8" you need to install "en_US.UTF8".
In the example you see that "en_US.UTF-8" is listed , so we can use it on this server.

-3- Check if you telnet/ssh software is properly set up.
--------------------------------------------------------
You will need *also* to check that your TELNET/SSH software is correctly configured.
It's your telnet software is responible for the conversion of the Unix locale to the client's environment (most likly a windows system).
We suggest that you try first with the free PUTTY client.
Putty download site - http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html
When using the "en_US.UTF-8" like in our example on the unix side change the following setting in Putty:
Open the configuration window, navigate to "Window" then to "Translation" and set "Received data assumed to be in which character set" to "UTF-8".
Then navigate to "Window" then to "Appearance" and choose a font in the "Font used in the terminal window" that supports the languages you want to use/see.
For non-asian languages you can use the default "Courier New".
On windows you can use the windows tool "character map" to see what characters are know in a font.
Putty UTF8 how to - http://www.laug.org/2004/11/putty-utf8-howto.html
If it works with Putty but not with your telnet/ssh package then please consult the vendor of your telnet/ssh software.
If you do not use telnet but a "real" unix display please see Note 265090.1 - How to check Unix terminal Environments for the capability to display extended characters.

-4- Set the NLS_LANG and Test.
------------------------------
Once you have 1) configured the locale correctly , 2) have checked that the used locale is really installed and 3) configured your telnet/ssh client, then you can use a NLS_LANG set to LC_TYPE values.
In our "en_US.UTF-8" example this means setting NLS_LANG=AMERICAN_AMERICA.UTF8 (note the difference in notation between UTF-8 (unix) and UTF8 (oracle) )
so, login with your Unix user and then
a) check with locale if it's correctly set (assuming here en_US.UTF8)
b) set the NLS_LANG to match the locale settings
$ export NLS_LANG=AMERICAN_AMERICA.UTF8
or
% setenv NLS_LANG AMERICAN_AMERICA.UTF8
c) connect with sqlplus to your database and select some data.
For example "select UNISTR('\20AC') from dual;" will give the euro symbol when selecting using a 9i or up database that can handle the euro (UTF8,WE8MSWIN1252,...) and a correct UTF-8 or ISO8859-15 Unix enviroment.
If this works, then set the NLS_LANG also in the profile of your user.

-5- What to do if this is not working?
--------------------------------------
If you do not see the expected characters then please double check your settings.
However it's also possible that you have wrong data in your database.
The easy way to check:
Use a windows client, download and install SQL developer http://www.oracle.com/technology/products/database/project_raptor/, connect to your database and see if your data is correctly displayed in that tool.
If it is visible in SQL developer then the data is correct in the database, if not then you have wrong data in your database.

The harder way:
If for example "select ename from scott.emp where empno='7369';" is a select that returns one row of your data then do "select dump(ename,1016),ename from scott.emp where empno='7369';".
You can then look up if the codes match the characters you expect for your database characterset
( select value from NLS_DATABASE_PARAMETERS where parameter='NLS_CHARACTERSET'; )
in NOTE.282336 Charts of most current mono-byte Character sets or in case you have a (AL32)UTF8 database use Note:69518.1
Determining the codepoint for UTF8 characters.

If you can't figure out what's wrong log a tar, refer to this note and provide:
* the info asked in Note 226692.1 Finding out your NLS Setup.
* a spool (!not copy paste!) of the output of your version of the
"select dump(ename,1016),ename from scott.emp where empno='7369';" select .

It might also be good to follow Database Character Set Healthcheck,
same here, if you are stuck, log a tar and provide all gatherd information.
note to support: if a ct log's a TAR about this please *do* request a ARE review.

-6- More in depth debugging.
----------------------------
The steps 1-4 should be enough for 99% of the cases, the rest of the note is more in depth debugging
On some platforms, it can be usefull to use the following syntax to have more details about the codepage really used:

$ locale LC_CTYPE | head
example of output in a HP-UX env:
""
""
"iso885915"
""
example of output in a Linux env:
upper;lowe ;alpha;digit;xdigit;space;print;graph;blank;cntrl;punct;alnum;combining;combining_level3
toupper;tolower;totitle
16
1
ISO-8859-15
70
84
1
0
1

$ locale LC_CTYPE | head
upper;lower;alpha;digit;xdigit;space;print;graph;blank;cntrl;punct;alnum;combining;combining_level3
toupper;tolower;totitle
16
6
UTF-8
70
84
1
0
1

On Solaris, AIX, TRU64, this syntax doesn't give interesting complementary information.
To find more details about these settings:
on Solaris, have a look in /usr/lib/locale.
on AIX, have a look in /usr/lib/nls/README
on TRU64, have a look in /usr/lib/nls
on HP-UX, have a look in /usr/lib/nls/config
on Linux, have a look in /usr/share/locale/locale.alias

How to check the codepoints managed by the O.S.:
To know which code point is generated for a character in a Unix Environment,
you can use the "od" command like this (examples with a iso-8859-1 locale):

$ od -xc
é^D^D
0000000 00e9
351 \0
0000001

as you can see the hexa-decimal code point e9 is corresponding to the "é" (lower e acute)
351 is the corresponding Octal value (Octal is the native mode of the od command).
You can also check the character corresponding to a code point using the "echo" command like this:
for Solaris, AIX, HP-UX, TRU64:
$echo '\0351'
é

for Linux:
$echo -e '\0351'
é

As you can see, echo uses the Octal value, so you need to convert in octal the value you want to check.

ReferencesNote 158577.1 - NLS_LANG Explained (How does Client-Server Character Conversion Work?)
Note 179133.1 - The correct NLS_LANG in a Windows Environment
Note 265090.1 - How to check Unix terminal Environments for the capability to display extended characters.
http://www.xfree86.org/4.2.0/xfontsel.1.html - to see which fonts are known in the xwin env
http://www.xfree86.org/4.2.0/xlsfonts.1.html - to see what fonts are installed on the system
http://publib.boulder.ibm.com/infocenter/pseries/index.jsp?topic=/com.ibm.aix.doc/aixprggd/nlsgdrf/locale_env.htm - IBM locale settings reference
http://docs.hp.com/en/5187-3605/ch02.html - HP Encoding Characters Reference
http://docs.hp.com/en/UXL10N-90302/UXL10N-90302.pdf - HP Language configuration reference
http://developers.sun.com/dev/gadc/faq/locale.html - Sun Solaris locale reference
http://www.opengroup.org/onlinepubs/007908799/xbd/locale.html - UNIX Open Group locale specifications reference
Note 119119.1 - AL32UTF8 / UTF8 (unicode) Database Character Set Implications



Nov 3, 2008

Several sql using to check tablespace usage

--I will add my comments when I'm free:)

select t.tablespace_name, round(t.free) free, f.extended_size, f.max_size,round((extended_size-free)*100/max_size,1) "pct_used%"
from (SELECT tablespace_name, SUM(bytes / 1024 / 1024) free
FROM DBA_FREE_SPACE
group by tablespace_name) t
join (select tablespace_name,
--file_name,
sum(bytes / (1024 * 1024)) as extended_size,
sum(greatest(maxbytes, bytes) / 1024 / 1024) as max_size
from dba_data_files
group by tablespace_name) f on t.tablespace_name =
f.tablespace_name
where (extended_size-free)/max_size >0
order by 1,5;

select * from dba_free_space

select tablespace_name,
file_name,
bytes / (1024 * 1024) as extended_size,
greatest(maxbytes, bytes) / 1024 / 1024 as max_size
from dba_data_files
order by 1

select * from dba_tablespaces

select q2.other_tname ts_name, pieces, ts_size ts_size,
nvl(largest_chunk,0) largestpc, nvl(total_free,0) totalfree,
nvl(round((total_free/ts_size)*100,2),0) pct_free,
ts_size-total_free whatsused,
nvl(100-round((total_free/ts_size)*100,2),100) pct_used,
decode(nvl(100-round((total_free/ts_size)*100,0),100),
85,'+',86,'+',87,'+',88,'+',89,'++',90,'++',91,'++',
92,'++',93,'++',94,'+++',95,'+++',96,'+++',97,'++++',
98,'+++++',99,'+++++',100,'+++++','') problem
from (select dfs.tablespace_name,count(*) pieces,
round(max(dfs.bytes)/1024/1024,2) largest_chunk,
round(sum(dfs.bytes)/1024/1024,2) total_free
from dba_free_space dfs group by tablespace_name) q1,
(select tablespace_name other_tname,
round(sum(ddf2.bytes)/1024/1024,2) ts_size
from dba_data_files ddf2 group by tablespace_name) q2
where q2.other_tname = q1.tablespace_name(+)
order by nvl(100-round((total_free/ts_size)*100,0),100) desc;


A wonderful PLSQL script is used to generate a index creation script.

--we want to create the index, using the same index name and table name with another schema
--the following procedure is used to generate the create index script

declare
val varchar2(10);
sql_val varchar2(100);
begin
for i in (select distinct index_name
from dba_ind_columns where table_owner='USR_LT' and table_name in
(select table_name from dba_tables where owner='LTYZDZ')
or 'ACCT_'table_name in (select table_name from dba_tables where owner='LTYZDZ')) loop
Select 'create index 'index_name' on 'table_name '('Substr(Sys_Connect_By_Path(column_name, ','), 2)')' into sql_val
From (Select Rownum rid, Rownum + 1 Next_Rid, column_name ,table_name,index_name From dba_ind_columns where index_name=i.index_name)
Where rid = (select count(*) from dba_ind_columns where index_name=i.index_name)
Start With Rid = 1
Connect By Prior Next_Rid = Rid;
dbms_output.put_line(sql_val);
end loop;
end;

--========output result===============================
create index IDX_ASSURE_NO on INFO_ASSURE(ASSURE_NO)
create index IDX_CDMA_SPEC_001 on CDMA_SPEC(USER_NO,DESTROY_DATE)
create index IDX_CHARGE_DEPOSIT_001 on CHARGE_DEPOSIT(USER_NO)
create index IDX_CODE_GROUP_001 on CODE_GROUP(GROUP_CODE)
create index IDX_CUSTOMER_NO on INFO_CUSTOMER(CUSTOMER_NO)
create index IDX_DEVICE_NUMBER on INFO_IVPN(DEVICE_NUMBER)
create index IDX_DIY_LIFECYCLE_001 on DIY_LIFECYCLE(USER_NO,DINNER_EXP_DATE)
create index IDX_INCDMA_SPEC on INCDMA_SPEC(USER_NO,DESTROY_DATE)
create index IDX_INCDMA_SPEC_002 on INCDMA_SPEC(USER_NO)
create index IDX_INFO_ASSURE_001 on INFO_ASSURE(LOCAL_NET)
create index IDX_INFO_BANK_001 on INFO_BANK(PAY_NO)
create index IDX_INFO_CDMA_001 on INFO_CDMA(LOCAL_NET)
create index IDX_INFO_CDMA_002 on INFO_CDMA(DEVICE_NUMBER)
create index IDX_INFO_CDMA_TEMP_001 on INFO_CDMA_TEMP(LOCAL_NET)
create index IDX_INFO_CUSTOMER_002 on INFO_CUSTOMER(LOCAL_NET)
create index IDX_INFO_FF_001 on INFO_FF(USER_NO,DESTROY_DATE)
create index IDX_INFO_INCDMA_001 on INFO_INCDMA(LOCAL_NET)
create index IDX_INFO_INCDMA_002 on INFO_INCDMA(USER_NO)
create index IDX_INFO_NAI_001 on INFO_NAI(USER_NO,FLAG)
create index IDX_INFO_PAY_001 on INFO_PAY(LOCAL_NET)
create index IDX_PAY_NO on INFO_PAY(PAY_NO)
create index IDX_PAY_NO_INVALID on PAY_RELATION(PAY_NO,INVALID_DATE)
create index IDX_PAY_RELATION_01 on PAY_RELATION(USER_NO)
create index IDX_PAY_RELATION_USER_NO on PAY_RELATION(USER_NO)
create index IDX_RES_INUIM_NUMBER_001 on RES_INUIM_NUMBER(UIM_NUMBER)
create index IDX_UIM_NUMBER on RES_UIM_NUMBER(UIM_NUMBER)
create index IDX_USER_NO on DINNER_LIFECYCLE(USER_NO)
create index IDX_USER_NO_001 on INFO_IVPN(USER_NO)
create index IDX_USER_NO_INVALID on PAY_RELATION(USER_NO,INVALID_DATE)
create index IDX_USER_NO_STATUS on INFO_CDMA(USER_NO,STATUS_CODE)
create index INDEX_PAY_NO on PAY_RELATION(PAY_NO)
create index INDEX_USER_DINNER on DINNER_USER(USER_DINNER)
create index INDEX_USER_NO on INFO_CDMA(USER_NO)

/*
the author of this script is by lusheng.
I give this task to him, then he created it. looks it works well.
wenderful.*/



Nov 2, 2008

Continue talk about ORA-01110 error

From Oracle Error Tips by Burleson Consulting (S. Karam)


The Oracle docs note this on the ora-01110 error:

ORA-01110: data file string: "string"
Cause: Reporting file name for details of another error
Action: See associated error message
ORA-01110 is thrown in conjunction with other errors.
Resolving ORA-01110 consists of viewing error message associated with the string you are running.
--------------------------------------------------------------------------------
Don Burleson offers great advice on retrieving the associateded errors on
BC Praetoriate Oracle Support in an excerpt from the book
<Oracle Internals Monitoring & Tuning Scripts>:
Statement processed.
ALTER DATABASE OPEN resetlogs
*
ORA-01194: file 1 needs more recovery to be consistent
ORA-01110: data file 1: '/u03/oradata/tstc/dbsyst01.dbf'
Or:
ORA-01547: warning: RECOVER succeeded but OPEN RESETLOGS would get error below
ORA-01194: file 48 needs more recovery to be consistent
ORA-01110: data file 48: '/vol06/oradata/testdb/ard01.dbf'

If all available archive logs and online redo logs are applied, and the error is not corrected, then the use of the _allow_resetlogs_corruption parameter should be considered. Make sure a good backup of the database in a closed state (all files) is taken before attempting recovery using this parameter.

It cannot be emphasized enough that the database will no longer be supported by Oracle until it is rebuilt after using _allow_resetlogs_corruption for recovery.
Another helpful link to resolving ORA-01110 and ORA-01116 is on the IBM website:

Problem

The ITM for Oracle agent may fails in retrieving data and the TEP workspaces show up empty. The log file shows errors ORA-01116 and ORA-01110

Solution

1. Problem:

Oracle Workspace do not report any data from the oracle agent being monitored

2. Cause:

The log files called *_col.out may shows ORA-01116 and ORA-01110 errors when retrieving info for some cursors, for example:

=============================================
ORU0007T (161958) Stmt = SELECT /*+RULE*/ COUNT(*) EXTENTS FROM
SYS.DBA_EXTENTS
ORU0008W (161958) ORACLE Message ORA-01116: error in opening database
file 26
ORA-01110: data file 26: for OPEN
=============================================
3. Solutions:

It could be due to authorization issue or due to the fact the data files does not exists anymore

1) check the data file corresponding to ID 26 in the sys.dba_data_files table
2) check the user specified in korgrant.sql or korgrantcustom.sql can access that directory
--> if cannot be access make sure to that the user has read access to it
3) check that the files corresponding that ID still exist in the reported location
--> if not you need to contact the Oracle DBA to re-establish consistency across info stored in Oracle system table.


A Case of ORA-01110 error

1.Copy from AA1 to AB1
cloned by copying a restore from AA1
2.Create controlfile

STARTUP NOMOUNT
CREATE CONTROLFILE SET DATABASE "AB1" RESETLOGS ARCHIVELOG
...
alter database open resetlogs;

Then got the following message after the db-copy!
ORA-01190: controlfile or data file 75 is from before the last RESETLOGS
ORA-01110: data file 75: '/oracle/AB1/data2/data15/data15'

i make now a restore from the file 75: '/oracle/AB1/data2/data15/data15'
--file 75 from the last cold backup from the AA1 database
then i wolud to create new controlfile
and got the following message
ORA-01503: CREATE CONTROLFILE failed
ORA-01161: database name AA1 in file header does not match given name of AB1
ORA-01110: data file 75: '/oracle/AB1/data2/data15/data15'

Can anybody advise me how i can solve tis problem?

Marcio Paiva's advise on this issue:

Flow these steps:
1. Take a physicall backup of the ur primary database
2. take a backup controlfile trace file from primary
3. restore only the datafiles and plan backup controlfile to secondary
4. recreate controlfile changing ur database name and file locations
5. issue recover automatic database until cancel using backup controlfile
6. alter database open resetlogs
7. add tempfiles

About Oracle's NLS_DATE_FORMAT

NLS_DATE_FORMAT is used if a date format mask is not specified in application code. The effective NLS_DATE_FORMAT is determined by the following (in order of precedence):

1. Session NLS_DATE_FORMAT (via ALTER SESSION command)
2. Client side NLS_DATE_FORMAT (from client environment variables/registry settings)
3. Instance NLS_DATE_FORMAT (from init.ora file)
4. Database NLS_DATE_FORMAT

Session NLS_DATE_FORMAT is set to client side NLS_DATE_FORMAT (explicit or implicit) ONLY if NLS_LANG is set. Another words, if NLS_LANG is set, then session NLS_DATE_FORMAT will be taken from the client. If clent NLS_LANG is set and client NLS_DATE_FORMAT is not, session NLS_DATE_FORMAT will default to DD-MON-YY (and that was exactly what you experienced). If NLS_LANG is not specified on the client side, NLS_DATE_FORMAT will be taken from instance NLS_DATE_FORMAT which is NLS_DATE_FORMAT from INIT.ORA. If NLS_DATE_FORMAT is not set in INIT.ORA, session NLS_DATE_FORMAT will default to DD-MON-YY.


Nov 1, 2008

Get the SQL using ORA_SQL_TXT via Trigger


From 9i,you can using the system event ORA_SQL_TXT to get the SQL by Trigger.

SQL> CREATE TABLE T (ID NUMBER, NAME VARCHAR2(30));

SQL> CREATE TABLE T_LOG (EXECUTE_DATE DATE, SQL_STATMENT CLOB);

SQL> CREATE OR REPLACE TRIGGER T
2 BEFORE INSERT OR UPDATE OR DELETE ON T
3 FOR EACH ROW
4 DECLARE
5 V_SQL_OUT ORA_NAME_LIST_T;
6 V_NUM NUMBER;
7 V_SQL_STATMENT VARCHAR2(32767);
8 BEGIN
9 V_NUM := ORA_SQL_TXT(V_SQL_OUT);
10 FOR I IN 1 .. V_NUM LOOP
11 V_SQL_STATMENT := V_SQL_STATMENT || V_SQL_OUT(I);
12 END LOOP;
13 INSERT INTO T_LOG (EXECUTE_DATE, SQL_STATMENT)
14 VALUES (SYSDATE, V_SQL_STATMENT);
15 END;
16 /

SQL> INSERT INTO T VALUES (1, 'A');

SQL> INSERT INTO T (ID) VALUES (2);

SQL> UPDATE T SET NAME = 'B' WHERE ID = 2;

SQL> DELETE T;

SQL> ALTER SESSION SET NLS_DATE_FORMAT = 'YYYY-MM-DD HH24:MI:SS';

SQL> SELECT * FROM T_LOG;

EXECUTE_DATE SQL_STATMENT
------------------- -----------------------------------------------
2007-09-13 22:55:35 INSERT INTO T VALUES (1, 'A')
2007-09-13 22:55:46 INSERT INTO T (ID) VALUES (2)
2007-09-13 22:55:54 UPDATE T SET NAME = 'B' WHERE ID = 2
2007-09-13 22:56:00 DELETE T
2007-09-13 22:56:00 DELETE T

After 9206,Oracle no more support this function in trigger for DML.just only support in system event.

--DML触发器中使用ORA_SQL_TXT函数,这个函数返回NULL,而且传入的OUT变量将不会被初始化。

From yangtinkun




How to do a full database backup automatically by RMAN -- shell script show

#!/bin/ksh
# function: backup database(cdmaetl) or archive with rman
# usage: crontab on AIX

#env
export ORACLE_SID=cdmaetl
#export SHELL=/usr/bin/ksh
export ORACLE_HOME=/ora9i/OraHome1
export NLS_LANG=american_america.zhs16gbk
export PATH=$PATH:$ORACLE_HOME/bin
#variable
RMAN=$ORACLE_HOME/bin/rman
SQLPLUS=$ORACLE_HOME/bin/sqlplus
TEE=/usr/bin/tee

LOGFILE=/ora9i/logs/rmanback.log

#dictionary structure
DBDEST=/oradata/backup/rmanbak/${ORACLE_SID}/datafiles
CTLFILE=/oradata/backup/rmanbak/${ORACLE_SID}/ctlfiles/${ORACLE_SID}_ctl_`date +%Y%m%d%H%M`.ctl

#start running RMAN to backup database and redo log, also have a log for those actions
echo "----------------backup start----------------------" > $LOGFILE
date
$RMAN <<EOF $TEE -a $LOGFILE
connect target

#allocate channel for backup, and only bak database
Run{
allocate channel c1 type disk format '$DBDEST/%U_%s.bak' maxpiecesize = 2000M;
backup database filesperset = 4;
backup archive log delete input;

}

#save just only one bak
delete noprompt obsolete redundancy = 1 device type disk;
exit;
EOF

#start bak control file
$SQLPLUS "/ as sysdba" <<EOF $TEE -a $LOGFILE
alter database backup controlfile to '$CTLFILE';
exit
EOF
date
echo "-----------------backup end-----------------------" >> $LOGFILE

How to switch the Autotrace and Explain Plan functions of Oracle

$cd $ORACLE_HOME/sqlplus/admin
$ls
$sqlplus /nolog
sql> @connect sys/passwd as sysdba
sys@ora9i> @plustrce.sql
sys@ora9i> grant plustrace to public;
sys@ora9i> exit
$ cd $ORACLE_HOME/rdbms/admin
sql> @connect sys/passwd as sysdba
sys@ora9i> @utlxplan.sql
sys@ora9i> exit
 
SQL> set autotrace on
SQL> select * from all_users where rownum<3;
USERNAME                          USER_ID CREATED
------------------------------ ---------- ---------
SYS                                     0 28-SEP-08
SYSTEM                                  5 28-SEP-08

Execution Plan
----------------------------------------------------------
   0      SELECT STATEMENT Optimizer=CHOOSE
   1    0   COUNT (STOPKEY)
   2    1     NESTED LOOPS
   3    2       NESTED LOOPS
   4    3         TABLE ACCESS (FULL) OF 'USER$'
   5    3         TABLE ACCESS (CLUSTER) OF 'TS$'
   6    5           INDEX (UNIQUE SCAN) OF 'I_TS#' (NON-UNIQUE)
   7    2       TABLE ACCESS (CLUSTER) OF 'TS$'
   8    7         INDEX (UNIQUE SCAN) OF 'I_TS#' (NON-UNIQUE)
 

Statistics
----------------------------------------------------------
          7  recursive calls
          0  db block gets
         16  consistent gets
          0  physical reads
          0  redo size
        717  bytes sent via SQL*Net to client
        655  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          2  rows processed
SQL> explain plan for select * from all_objects where rownum<3;
 
         Explained.
SQL> select * from table(dbms_xplan.display);
...



Oct 30, 2008

Using UTL_FILE package to dump data to a text file

--Using UTL_FILE package to dump data to a text file
--First, you should create a dictionary and grant write privilege on this dir to the user you wanted.
--Then you can call this procedure like this: dump2text('select * from all_users','','utldir','test.txt')
--Good luck!
create or replace procedure dump2text(sp_query in varchar2,
sp_separator in varchar2 default '',
sp_dir in varchar2,
sp_filename in varchar2) authid current_user is
ol_filehandle utl_file.file_type;
cl_cursor integer default dbms_sql.open_cursor;
sl_coltext varchar2(2000);
nl_status integer;
nl_colcnt number default 0;
sl_separator varchar2(10) default '';
nl_cnt number default 0;
begin
ol_filehandle := utl_file.fopen(sp_dir, sp_filename, 'w');
dbms_sql.parse(cl_cursor, sp_query, dbms_sql.native);
for i in 1 .. 255 loop
begin
dbms_sql.define_column(cl_cursor, i, sl_coltext, 2000);
nl_colcnt := i;
exception
when others then
if (sqlcode = -1007) then
exit;
else
raise;
end if;
end;
end loop;
dbms_sql.define_column(cl_cursor, 1, sl_coltext, 2000);
nl_status := dbms_sql.execute(cl_cursor);
loop
exit when(dbms_sql.fetch_rows(cl_cursor) <= 0);
sl_separator := '';
for i in 1 .. nl_colcnt loop
dbms_sql.column_value(cl_cursor, i, sl_coltext);
utl_file.put(ol_filehandle, sl_separator sl_coltext);
if i < 2 then
sl_separator := sp_separator;
end if;
end loop;
utl_file.new_line(ol_filehandle);
nl_cnt := nl_cnt + 1;
end loop;
dbms_sql.close_cursor(cl_cursor);
utl_file.fclose(ol_filehandle);
dbms_output.put_line('total export rows: ' nl_cnt);
exception
when no_data_found then
return;
when utl_file.invalid_path then
dbms_output.put_line('invalid path');
when utl_file.invalid_filehandle then
dbms_output.put_line('invalid filehandle');
when utl_file.invalid_operation then
dbms_output.put_line('invalid operation');
when others then
dbms_output.put_line('error number: ' sqlcode
'\n error message: ' sqlerrm);
end dump2text;


Oct 26, 2008

How to connect to the Oracle server via PLSQL developer tool with out installation Oracle Client.

/*不用安装Oracle Client,Plsql developer 连接到Oracle server.
1: Seach the following files at your ORACLE server end. and copy those
files to a folder. for example: D:\oraclient
oci.dll
ocijdbc10.dll
ociw32.dll
orannzsbb10.dll
oraocci10.dll
oraociei10.dll
/*if have not found, then plsql develop tools can login via any
account and password. you should seach and download it by google or
baidu:)*/
sqlnet.ora
tnsnames.ora
classes12.jar
ojdbc14.jar

2、create a file: tnsnames.ora in the previous folder. eg. D:\oraclient.
like this:
oracledata =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.1.111)(PORT = 1521))
)
(CONNECT_DATA =
(SERVICE_NAME = orcl)
)
)

3、Config the env. add TNS_ADMIN=D:\oraclient. if you have install the
oracle client. it should be listed under %ORACLE_HOME%/network/admin/。

4、Set the language env. NLS_LANG = SIMPLIFIED CHINESE_CHINA.ZHS16GBK
or AMERICAN_AMERICA.US7ASCII
5、Open your PLSQL developer and set the connection string. then save
it and quit.
check tools->preferences->connection
Oracle Home=D:\oracleclient
OCI library=D:\oracleclient\oci.dll

6、Ok, you can try it now, enjoy it!!! if have any issue, please feel
free contact me: Gtalk & Gmail: zhouxiangzhen@gmail.com

Oct 25, 2008

Oracle -- How to export a table to a plane file by UTL_FILE

--author; Zhou xiangzhen
--created: 2008-10-25
--function: export table to a plane file
--you should call this procedure like this: dump2text('select * from all_users','|','EXPORTTABLEDIR','test.txt')
--
CREATE OR REPLACE procedure dump2text(sp_query     in varchar2,
                                      sp_separator in varchar2 default '|',
                                      sp_dir       in varchar2,
                                      sp_filename  in varchar2) authid current_user is
  ol_filehandle utl_file.file_type;
  cl_cursor     integer default dbms_sql.open_cursor;
  sl_coltext    varchar2(2000);
  nl_status     integer;
  nl_colcnt     number default 0;
  sl_separator  varchar2(10) default '';
  nl_cnt        number default 0;
begin
  ol_filehandle := utl_file.fopen(sp_dir, sp_filename, 'w');
  dbms_sql.parse(cl_cursor, sp_query, dbms_sql.native);
  for i in 1 .. 255 loop
    begin
      dbms_sql.define_column(cl_cursor, i, sl_coltext, 2000);
      nl_colcnt := i;
    exception
      when others then
        if (sqlcode = -1007) then
          exit;
        else
          raise;
        end if;
    end;
  end loop;
  dbms_sql.define_column(cl_cursor, 1, sl_coltext, 2000);
  nl_status := dbms_sql.execute(cl_cursor);
  loop
    exit when(dbms_sql.fetch_rows(cl_cursor) <= 0);
    sl_separator := '';
    for i in 1 .. nl_colcnt loop
      dbms_sql.column_value(cl_cursor, i, sl_coltext);
      utl_file.put(ol_filehandle, sl_separator || sl_coltext);
      if i < 2 then
        sl_separator := sp_separator;
      end if;
    end loop;
    utl_file.new_line(ol_filehandle);
    nl_cnt := nl_cnt + 1;
  end loop;
  dbms_sql.close_cursor(cl_cursor);
  utl_file.fclose(ol_filehandle);
  dbms_output.put_line('total export rows: ' || nl_cnt);
exception
  when no_data_found then
    return;
  when utl_file.invalid_path then
    dbms_output.put_line('invalid path');
  when utl_file.invalid_filehandle then
    dbms_output.put_line('invalid filehandle');
  when utl_file.invalid_operation then
    dbms_output.put_line('invalid operation');
  when others then
    dbms_output.put_line('error number: ' || sqlcode ||
                         ' error message: ' || sqlerrm);
end dump2text;
/

--
Thanks
Xiangzhen



Oct 21, 2008

Undo 空间计算公式


UndoSpace = Undo Retention * Undo Per Second + overhead(24 undo blocks)


--
Thanks
Xiangzhen


Feb 2, 2008

OraFusion.com - Designing Efficient ETL Processes in Oracle

OraFusion.com - Designing Efficient ETL Processes in Oracle

Oracle Articles

Designing Efficient ETL Processes in Oracle:


Introduction:

Data conversion - the process of migrating application data - is a major component of many database related projects. This process essentially entails three sub-processes: 1) Extracting data from the source system, 2) Transforming data to the format required by the target system and 3) Loading data into the target system - hence the abbreviation ETL. Oracle offers a variety of database features for implementing ETL processes. Additionally, there is the relatively new Oracle Warehouse Builder - a full featured ETL tool. There are also other tools by third party vendors such as Informatica. However, despite this recent proliferation of ETL tools, many Oracle based data migration projects continue to use homegrown programs (mainly PL/SQL) for ETL. This is mainly because smaller projects cannot justify the cost and learning time required for effective use of commercial ETL tools. Besides, doing it oneself offers much more control over the process.

As with any programming project there are efficient and (infinitely more) inefficient ways to design and implement ETL processes. This article offers some tips for the design of efficient ETL programs in an Oracle environment. The tips are listed in no particular order. I hope you find them helpful in your projects

Tip 1: Use external tables to load data from external flat files

The traditional workhorse for loading external (flat file) data into Oracle is the SQL*Loader utility. Since version 9i, external tables offer an easier, and almost as performant, route to load data. Essentially, external tables enable us to access data in an external file via an Oracle table. Here's a simple example:
The following data is available in a text file called test.txt:

scott,cio
kailash,dba
jim,developer

We want to pull this data into Oracle using an external table. The first step is to create a directory object in Oracle, which points to the operating system directory in which the above file resides. Assume this is an operating system directory called c:\test. The SQL to make this directory accessible from Oracle is:

create or replace directory test_dir as 'c:\test';

The user must have the create any directory privilege in order to execute this SQL. We then define an external table based on our text file:

create table ext_test(
name varchar2(10),
job varchar2(10))
organization external
(type oracle_loader
default directory test_dir
access parameters (fields terminated by ',')
location (test_dir:'test.txt'))
reject limit 0

It would take us too far afield to go into each of the clauses in the above statement. Check the Oracle docs for further details. The relevant book is the Oracle Utilities Guide. There are also several third-party articles available online - do a google search on "oracle external table" to locate some of these.

To access the external data we simply issue the following SQL statement from within Oracle:

select * from ext_test;

There are several access parameter options available to handle more complicated record formats - check the Oracle documentation for details.

Tip 2: Use set-based operations to process data

Most ETL processes deal with large numbers of records. Each record must be cleaned at the field level and then loaded into the target system. Programmers unfamiliar with SQL tend to use procedural methods ("for" loops, for example) to deal with such situations. With some thought, however, it is often possible to construct an SQL statement that is equivalent to the procedural technique. The method using SQL should be preferred, as SQL processes the entire batch of records as a single set. Provided the statement isn't too complex, the SQL statement will generally run faster than the equivalent procedural code. Here's an (admittedly contrived) example to illustrate the point:

--execute from SQL Plus

set timing on

create table test (
id number,
name varchar2(128));

alter table test add constraint pk_test primary key (id);

declare
cursor c_test is
select
object_id,
object_name
from
all_objects;
begin
for r_test in c_test loop
insert into test
(id, name)
values
(r_test.object_id,lower(r_Test.object_name));
end loop;
commit;
end;
.
/

truncate table test;

declare
begin
insert into test (id, name)
select
object_id,
lower(object_name)
from
all_objects;
commit;
end;
.
/

You may need a larger number of rows to see an appreciable difference between the two methods. The advantage of the SQL based technique becomes apparent with greater data volumes.

Here are some tips that may help in converting procedural code to SQL:

  1. Replace procedural control statements with SQL decode or case: Often a procedural statement such as if....then....else can be replaced by a single SQL statement that uses the decode function or case statement. For simple conditionals the SQL option will almost always outperform procedural code. The decode function is useful when your branching criterion is based on discrete values - for example "Male" or "Female". The case statement would be preferred if the criterion is based on a continuous range - eg: any criterion that uses > or &lt. Here's an example of the two in action:

    --DECODE: Classify by gender

    select
    ename,decode(gender,'M','Male','F','Female','Unknown')
    from
    employees;

    --CASE: is the current day a weekend or weekday?

    select
    case when to_char(sysdate,'D')>'1' and to_char(sysdate,'D')<'6' then
    'Weekday'
    else
    'Weekend'
    end as day_type
    from
    dual;

  2. Pivoting: A common requirement of ETL processes is to pivot a table about a particular column, i.e. to convert rows to columns based on values in a specific column. This is easily achieved using the decode function. Consider the following example: we want to flatten out data in a table called SALES based on the MONTH column (which contains numbers ranging from 1 to 12). The SALES table has the following columns: REGION, YEAR, MONTH and SALES_DOLLARS (primary key REGION, YEAR and MONTH). The sales data is to be flattened out with each month's dollars appearing in a separate column - JAN_SALES, FEB_SALES .... DEC_SALES - appearing as a separate column. Here's how this can be done:

    select
    region
    year,
    min(decode(month,1,amount)) jan_sales,
    min(decode(month,2,amount)) feb_sales,
    min(decode(month,3,amount)) mar_sales,
    min(decode(month,4,amount)) apr_sales,
    min(decode(month,5,amount)) may_sales,
    min(decode(month,6,amount)) jun_sales,
    min(decode(month,7,amount)) jul_sales,
    min(decode(month,8,amount)) aug_sales,
    min(decode(month,9,amount)) sep_sales,
    min(decode(month,10,amount)) oct_sales,
    min(decode(month,11,amount)) nov_sales,
    min(decode(month,12,amount)) dec_sales
    from
    sales
    group by
    region,
    year

    The grouping is required to collapse twelve records for the region / year into a single record. Note that you can use any grouping function (AVG, MAX, SUM, for example) if all key columns other than MONTH are to be preserved in the flattened table. If this isn't so, you will have to use SUM as you will be summing over records for a fixed month. In either case you should be sure you understand what you're getting!

  3. "Unpivoting": This is the converse of the above. Here we want to move data from several columns into a single column, based on some criterion. Say we have a table called FLAT_SALES with columns REGION, YEAR, JAN_SALES,....DEC_SALES (produced by the above query). An ETL process requires us to get the data in normalised format, i.e. columns REGION, YEAR, MONTH, SALES_DOLLARS, as in the original SALES table. Here's a query that will achieve this:

    select
    t1.region region,
    t1.year year,
    t2.r month,
    decode(t2.r,
    1,jan_sales,
    2,feb_sales,
    3,mar_sales,
    4,apr_sales,
    5,may_sales,
    6,jun_sales,
    7,jul_sales,
    8,aug_sales,
    9,sep_sales,
    10,oct_sales,
    11,nov_sales,
    12,dec_sales) sales_dollars
    from
    flat_sales t1,
    (select
    rownum r
    from
    all_objects
    where
    rownum<=12) t2

    The table t2 serves to produce 12 rows for each original row via a cartesian product (no joins). The query uses a decode on rownum to pick out the sales value for each month from the flattened table. Note that we could have used any table or view with 12 or more rows in the definition of t2. I chose ALL_OBJECTS as this view is generally available to all schemas, and contains a large number of rows.

  4. Using built-in functions: One can achieve fairly complex data transformations using Oracle's character SQL functions. The nice thing about these functions is that they can be embedded in SQL, so you get the full advantage of set-based operations. The usual suspects include: LENGTH, LOWER, LPAD, REPLACE, RPAD, SUBSTR, UPPER, (L)(R)TRIM. Some "lesser known" functions I have found useful in my ETL efforts are:
    • CHR Returns the character associated with the specified ASCII code. This is useful when you want to remove non-printable characters such as carriage return (CR) and linefeed (LF) from input strings. You would do this using CHR in tandem with REPLACE like so:
      REPLACE(input_string,CHR(13)||CHR(10)), 13 and 10 being the ASCII codes for CR and LF respectively.
    • INITCAP This capitalizes the first letter in each word of the input string. Every other letter is lower cased.
    • INSTR Returns the location of a specified string within the input string.
    All these functions have a range of invocation options. Check the Oracle SQL documentation for full details.

  5. Analytics: Since version 8i, Oracle has introduced analytical extensions to SQL. These enable one to do a range of procedural operations using SQL (yes, you read that right - procedural operations using SQL). Some operations possible using analytical SQL include: sub-grouping (different levels of grouping within a statement), ranking and other statistics over subsets of data, comparing values in different rows, and presenting summary and detailed data using a single SQL statement. Check out my article on analytic SQL to find out more.
Tom Kyte's site is a great resource for tips on converting procedural code to SQL - try searching on the keywords "ETL" and "analytic functions" for a start.

OK, after extolling the virtues of using SQL we have to admit that there are situations in which procedural code becomes unavoidable. In such cases you can still take advantage of set based processing by using bulk binding within your procedural code. Sections of the Oracle documentation on bulk binding are available here and here (free registration required). An introductory article on bulk binding is available here (dbasupport.com).

Tip 3: TRUNCATE tables when deleting all data

Deleting all data from a table is best done using the truncate statement. A truncate runs faster than a delete because it simply resets the table's high water mark to zero. Truncate is a very quick operation. Indexes are also truncated along with the table. A truncate is DDL and therefore cannot be rolled back - be sure you consider this before using truncate. Note that tables referenced by enabled foreign key constraints cannot be truncated unless the keys are disabled first. Here's a block of code that uses truncate to clean out the SALES table (which is referenced by enabled foreign key constraints):

declare

cursor c_referenced_by is
select
t1.constraint_name constraint_name,
t1.table_name table_name
from
user_constraints t1,
user_constraints t2
where
t1.constraint_type='R'
and
t1.r_constraint_name=t2.constraint_name
and
t2.table_name ='SALES';

begin

for r_referenced_by in c_referenced_by loop

execute immediate
'alter table '||r_referenced_by.table_name||' disable constraint '
||r_referenced_by.constraint_name;

end loop;

execute immediate
'truncate table sales';

end;

Tip 4: Use direct path inserts for loading data

A direct path insert offers a quick way to load large volumes of data into a table. It does this by bypassing the buffer cache and writing directly to datafiles. A direct path insert is done using the insert into ...select.. idiom, together with an append hint. The append hint causes the rows to inserted above the table's high water mark - any free space below the high water mark is not used. Here is the syntax for a direct path insert into SALES from SALES_STAGE:

insert /*+ append*/ into
sales
(region,
year,
month,
sales_dollars)
select
region,
year,
month,
sales_dollars
from
sales_stage;

A direct path insert by itself is faster than a conventional insert. Just how much mileage one gets depends on the volume of data loaded. The performance of direct path inserts can be further enhanced by doing the following before the load:
  1. Bypassing constraint checks by disabling all constraints. Remember to re-enable these after the load is done.
  2. Suppressing redo generation by putting the table in nologging mode. If your table has a large number of non-unique indexes, you might also consider setting these to unusable state. This will suppress redo generation associated with index maintenance. The indexes can then be rebuilt after the load. Note that unique indexes should not be set unusable because the load will fail with an ORA-26026: unique index initially in unusable state error. Unique indexes can, however, be dropped before and recreated after the load. This is, in effect, what happens when the primary key constraint is disabled (as discussed in the previous point) - the unique index associated with the primary key is dropped.
    An important consequence of suppressing redo is that the operation is unrecoverable. Be sure to coordinate such operations with your DBA so that she can schedule a backup of the database or relevant tablespaces after the load.
Here's a block of code that bypasses constraints checks and suppresses redo generation for the above insert:

declare

cursor c_constraints is
select
table_name,
constraint_name
from
user_constraints
where
table_name='SALES';

cursor c_indexes is
select
index_name
from
user_indexes
where
table_name='SALES'
and
uniqueness<>'UNIQUE';

begin

for r_constraints in c_constraints loop

execute immediate
'alter table '||r_constraints.table_name||' disable constraint '
||r_constraints.constraint_name;

end loop;

--optional truncate to clean out the table
--disable referencing constraints, if needed (see tip 3)

--execute immediate
--'truncate table sales';

for r_indexes in c_indexes loop

execute immediate
'alter index '||r_indexes.index_name||' unusable';

end loop;

execute immediate
'alter session set skip_unusable_indexes=true';

execute immediate
'alter table sales nologging';

insert /*+ append*/ into
sales
(region,
year,
month,
sales_dollars)
select
region,
year,
month,
sales_dollars
from
sales_stage;

execute immediate
'alter session set skip_unusable_indexes=false';

for r_indexes in c_indexes loop

execute immediate
'alter index '||r_indexes.index_name||' rebuild nologging';

end loop;

execute immediate 'alter table sales logging';

for r_constraints in c_constraints loop

execute immediate
'alter table '||r_constraints.table_name||' enable constraint '

||r_constraints.constraint_name;

end loop;

commit;

end;

For small data volumes, the overhead of disabling constraints and indexes, and making the table nologging will swamp the benefits gained. In general, the larger the load the greater the benefit of the foregoing actions. As always, benchmark before implementation in your loads.
The above code uses a lot of dynamic SQL so it can be heavy on database resources. The use of dynamic SQL is unavoidable because we need to perform the operations within a module (PL/SQL block, procedure or package). However, this should not cause performance problems for business users because ETL batch processes normally run during off-peak hours.

Tip 5: Use the MERGE command for upserts

A common requirement is the need to perform an "upsert" - i.e.: update a row if it exists, insert it if it doesnt. In Oracle (versions 9i and better) this can be done in one step using the MERGE statement. Here's an example that uses the EMP table of SCOTT schema. The example uses a table EMP_STAGE that holds updated and new records that are to be upserted into EMP. You need to be logged into the SCOTT schema (or any other schema with a copy of SCOTT.EMP) in order to run the example:

--create EMP_STAGE

create table emp_stage as select * from emp where 1=2;

--insert update records in EMP_STAGE

insert into emp_stage values
(7369,'SMITH','CLERK',7902,to_date('17-12-1980','dd-mm-yyyy'),1800,NULL,20);

insert into emp_stage values
(7499,'ALLEN','SALESMAN',7698,to_date('20-2-1981','dd-mm-yyyy'),2200,300,30);

insert into emp_stage values
(7521,'WARD','SALESMAN',7698,to_date('22-2-1981','dd-mm-yyyy'),1250,500,30);

insert into emp_stage values
(7839,'KING','PRESIDENT',NULL,to_date('17-11-1981','dd-mm-yyyy'),9500,NULL,10);

insert into emp_stage values
(7782,'CLARK','MANAGER',7839,to_date('9-6-1981','dd-mm-yyyy'),8500,NULL,10);

--insert new records in EMP_STAGE

insert into emp_stage values
(7940,'WEBSTER','DBA',7782,to_date('23-1-1985','dd-mm-yyyy'),7000,NULL,10);

insert into emp_stage values
(7945,'HAMILL','DEVELOPER',7782,to_date('21-5-1985','dd-mm-yyyy'),6000,NULL,10);

insert into emp_stage values
(7950,'PINCHON','ANALYST',7782,to_date('20-10-1985','dd-mm-yyyy'),6000,NULL,10);

commit;

--MERGE records into EMP

merge into
emp e
using
emp_stage es
on
(e.empno=es.empno)
when matched then
update
set
e.sal=es.sal
when not matched then
insert
(e.empno,
e.ename,
e.job,
e.mgr,
e.hiredate,
e.sal,
e.comm,
e.deptno)
values
(es.empno,
es.ename,
es.job,
es.mgr,
es.hiredate,
es.sal,
es.comm,
es.deptno);

commit;

Three rows are inserted and five existing rows are updated by the above.
In Oracle 10g one can also add a conditional clauses to the insert and update portions of the merge statement. For example:

--MERGE records into EMP, except for DEVELOPERS

merge into
emp e
using
emp_stage es
on
(e.empno=es.empno)
when matched then
update
set
e.sal=es.sal
when not matched then
insert
(e.empno,
e.ename,
e.job,
e.mgr,
e.hiredate,
e.sal,
e.comm,
e.deptno)
values
(es.empno,
es.ename,
es.job,
es.mgr,
es.hiredate,
es.sal,
es.comm,
es.deptno)
where
es.job<>'DEVELOPER'
;

In this case the record for employee 7945 is not inserted.

The conditional clause can be in the update portion as well. In 10g it is also possible to delete rows from the destination table based on conditional criteria. Check the documentation for details.

Tip 6: Use heterogeneous services to access data in non-Oracle relational databases

Database links are often used to transfer small volumes of data between Oracle databases. It is less well known that database links can also be set up between Oracle and non-Oracle databases. This is a useful feature, as ETL processes often need to access and transfer data to Oracle from third-party databases such as MS SQL Server. The standard way to do this is by exporting data from the non-Oracle database to a flat file, and then importing the data into Oracle via SQL Loader or external tables. Oracle Heterogeneous Services provides a single-step option to achieve the transfer. Heterogeneous Services come in two flavours:
  1. Generic Heterogeneous Services: This option, which is bundled with the Oracle Server uses ODBC to connect to the non-Oracle database. A tutorial on accessing SQL Server using Generic Heterogeneous Services is available here .
  2. Transparent Gateways: These are extra cost options that are optimised for specific databases. They offer better performance compared to the Generic option because they are designed to exploit optimisations and features specific to particular databases. The mechanics of setting up Transparent Gateways is quite similar to the Generic option. Check the documentation that comes with the specific Transparent Gateway for further details. The documentation is hidden away in the relevant gateway installation directory. For example, the documentation for the MS SQL Server gateway sits in ORACLE_HOME/tg4msql.A tutorial on accessing SQL Server using Transparent Gateways is available here .
A warning on data transfer techniques that uses database links: the efficiency of the transfer depends on your network bandwidth and the quantity of data to be transferred. I have used it (with great success) to transfer small to moderate quantities of data (~100000 rows, rowsize ~1000 bytes) over co-located machines within corporate networks.

Closing Remarks

ETL processes present a technical challenge as they entail complex transformations and loads of large quantities of data within ever-shrinking time windows. As developers we need to use all the tricks in the book to speed up our ETL procedures. In this article I've outlined some of the techniques that I have used, with success, in several projects. I hope you find them useful in your work.

Back to the top

Jan 16, 2008

Ask Tom "problem with long table name"

It's good stuff From Ask Tom "problem with long table name"

Tom,
We had problem with the long table name:
SQL> sho user
USER is "REF"
SQL> select table_name from user_tables;
TABLE_NAME
------------------------------
ACCOUNT_ADDR_USAGE_TYPES
ACCOUNT_ASSOC_TYPES
ACCOUNT_STATUSES
ACCOUNT_TRANSACTION_STATUSES
ACCOUNT_TRANSACTION_TYPES
ACCOUNT_TYPES
APPLICATIONS
BUSINESS_CODES
CATEGORY_DEPARTMENTS
CERTIFICATION_STATUS
CLIENT_SEGMENTS
COMPANIES
CONTACT_POINT_USAGE_TYPES
CURRENCIES
DEPARTMENTS
FUNCTIONS
GEOGRAPHIC_REGIONS
GEOGRAPHIC_REGION_TYPES
GEO_REGION_ROLLUPS
GEO_REGION_ROLLUP_TYPES
JOBS
LANGUAGES
LEGACY_OFFICE_DEPARTMENTS
MARKETING_SEGMENTS
OFFICES
OFFICE_DEPARTMENTS
PARTY_ACCOUNT_ROLES
PARTY_ASSOC_TYPES
PARTY_ASSOC_TYPE_GROUPS
PARTY_IDENTIFICATION_TYPES
PARTY_IMPORTANCE_LEVELS
PARTY_TYPES
TABLE_NAME
------------------------------
PAYMENT_CALC_METHOD
PAY_TERM_TYPES
PHONE_TYPES
PROCESSES
ROLES
ROLE_FUNCTIONS
STANDARD_HINT_QUESTIONS
TRIGGER_ERRORS
40 rows selected.
SQL> drop table ACCOUNT_TRANSACTION_STATUSES;
drop table ACCOUNT_TRANSACTION_STATUSES
*
ERROR at line 1:
ORA-00604: error occurred at recursive SQL level 1
ORA-01401: inserted value too large for column
ORA-06512: at line 22
SQL> drop table ref.jobs;
Table dropped.
We only may drop the table with shot name or use drop user username
cascade to drop the table with long name.
Is this a bug? What is the workaround for it.
Thanks for your help.
Pauline
and we said...Is this a bug -- you betcha.
Its a bug in YOUR DDL trigger!!!
You have a trigger, it has at least 22 lines of code and on line 22 you are inserting the
ora_dict_obj_name into an audit table. Audit table is too small to hold it. It fails.
We fail.
Solution... Fix trigger or fix audit table.
Example:
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table ACCOUNT_TRANSACTION_STATUSES ( x int );
Table created.
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table ACCOUNT_TRANSACTION_STATUSES;
Table dropped.
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table ACCOUNT_TRANSACTION_STATUSES ( x int );
Table created.
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table log;
drop table log
*
ERROR at line 1:
ORA-00942: table or view does not exist
ops$tkyte@ORA817DEV.US.ORACLE.COM> create table log ( msg varchar2(20) );
Table created.
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> create trigger ddl_trigger before drop on schema
2 begin
3 insert into log values ( ora_dict_obj_name );
4 end;
5 /
Trigger created.
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table ACCOUNT_TRANSACTION_STATUSES;
drop table ACCOUNT_TRANSACTION_STATUSES
*
ERROR at line 1:
ORA-00604: error occurred at recursive SQL level 1
ORA-01401: inserted value too large for column
ORA-06512: at line 2
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop trigger ddl_trigger;
Trigger dropped.
ops$tkyte@ORA817DEV.US.ORACLE.COM>
ops$tkyte@ORA817DEV.US.ORACLE.COM> drop table ACCOUNT_TRANSACTION_STATUSES;
Table dropped.
ops$tkyte@ORA817DEV.US.ORACLE.COM>


Dec 28, 2007

Oracle 数据仓库和集成特性


用于更高效管理物化视图、查询重写、可传输表空间以及表分区的新特性使数据仓库变得更加强大且占用资源更少。
这一部分涉及:·


分区更改跟踪:不需要 MV 日志
要了解此增强功能,首先必须了解物化视图 (MV) 刷新过程中的分区修整概念。
假设基于列 ACC_MGR_ID 对表 ACCOUNTS 进行了分区,每个 ACC_MGR_ID 值一个分区。您根据 ACCOUNTS 创建了一个名为 ACC_VIEW 的 MV,该 MV 也根据列 ACC_MGR_ID 进行了分区,每个 ACC_MGR_ID 一个分区,如下图所示:

假设已经更新了表 ACCOUNTS 中的记录,但只在分区 P1 中进行了此更新。要快速刷新此 MV,您只需刷新分区 P1 而非整个表,这里正是与 ACC_MGR_ID 相关的数据所在的分区。Oracle 自动执行此任务,通过一个名为分区更改跟踪 (PCT) 的特性跟踪对分区的更改。但有一个问题需稍加注意:要在快速刷新的过程中启用 PCT,必须创建 MV 日志,当表中的行发生变化会填充这些日志。发出刷新命令后,刷新进程将读取 MV 日志以识别这些更改。
不用说,该要求增加了操作的总执行时间。此外,附加的插入操作将消耗 CPU 周期和 I/O 带宽。
幸好,在 Oracle 数据库 10g 第 2 版中,PCT 不需要 MV 日志即可工作。让我们看一看它的作用方式。首先,确认表 ACCOUNTS 中没有 MV 日志。 SQL> select *
2 from dba_mview_logs
3 where master = 'ACCOUNTS';
no rows selected
现在,更新该表中的某个记录。 update accounts set last_name = '...'
where acc_mgr_id = 3;
该记录位于分区 P3 中。
现在,您就可以刷新此 MV 了。但首先记录表 ACCOUNTS 所有段的段级统计信息。稍后,您将使用这些统计信息了解使用了哪些段。 select SUBOBJECT_NAME, value from v$segment_statistics
where owner = 'ARUP'
and OBJECT_NAME = 'ACCOUNTS'
and STATISTIC_NAME = 'logical reads'
order by SUBOBJECT_NAME
/
SUBOBJECT_NAME VALUE
------------------------------ ----------
P1 8320
P10 8624
P2 12112
P3 11856
P4 8800
P5 7904
P6 8256
P7 8016
P8 8272
P9 7840
PMAX 256
11 rows selected.
使用快速刷新刷新物化视图 ACC_VIEW。 execute dbms_mview.refresh('ACC_VIEW','F')
'F' 参数指示快速刷新。但如果表没有 MV 日志,它是否可以起作用?
刷新完成后,再次检查表 ACCOUNTS 的段统计信息。结果如下所示: SUBOBJECT_NAME VALUE
------------------------------ ----------
P1 8320
P10 8624
P2 12112
P3 14656
P4 8800
P5 7904
P6 8256
P7 8016
P8 8272
P9 7840
PMAX 256
这些段统计信息显示了在一个逻辑读取过程中选择的段。由于这些统计信息是累积的,因此您必须查看值(而非绝对值)中的更改。如果仔细查看以上值,您便会发现只有分区 P3 的值发生了变化。因此,在刷新过程中只选择了分区 P3 而非整个表,确认 PCT 能否在表即使没有 MV 日志的情况下工作。
即使在基表没有 MV 日志的情况下也可以快速刷新 MV 的能力是一个强大而有用的特性,从而允许您可以在已分区的 MV 中执行快速刷新而不会增加性能开销。我认为,该特性是 Oracle 数据库 10g 第 2 版中最有用的数据仓库增强功能。


使用多个 MV 进行查询重写
Oracle8i 中引入的查询重写特性在数据仓库开发人员和 DBA 中轰动一时。从本质上而言,它将用户查询重写为从 MV 而非表中进行选择以利用现成的摘要。例如,请考虑以下一家大型连锁酒店的数据库中的三个表。 SQL> DESC HOTELS
Name Null?Type
----------------------------------------- -------- -------------
HOTEL_ID NOT NULL NUMBER(10)
CITY VARCHAR2(20)
STATE CHAR(2)
MANAGER_NAME VARCHAR2(20)
RATE_CLASS CHAR(2)
SQL> DESC RESERVATIONS
Name Null?Type
----------------------------------------- -------- -------------
RESV_ID NOT NULL NUMBER(10)
HOTEL_ID NUMBER(10)
CUST_NAME VARCHAR2(20)
START_DATE DATE
END_DATE DATE
RATE NUMBER(10)
SQL> DESC TRANS
Name Null?Type
----------------------------------------- -------- -------------
TRANS_ID NOT NULL NUMBER(10)
RESV_ID NOT NULL NUMBER(10)
TRANS_DATE DATE
ACTUAL_RATE NUMBER(10)
表 HOTELS 保存酒店的相关信息。当顾客预订酒店时,将在表 RESERVATIONS(包含房间价格报价)中创建一个记录。当顾客在酒店结帐时,将在另一个表 TRANS 中记录现金交易。
但在结帐前,酒店可能决定根据订房情况、升级、优惠等因素向顾客提供不同的房价。因此,最终的房价可能与预订时的报价不同,而且可以每天都各不相同。为正确记录这些价格变化,表 TRANS 有一行专门用来保存每天的房价信息。
为缩短查询响应时间,您可能决定根据用户发出的不同查询构建 MV,如: create materialized view mv_hotel_resv
refresh complete
enable query rewrite
as
select city, resv_id, cust_name
from hotels h, reservations r
where r.hotel_id = h.hotel_id;
和 create materialized view mv_actual_sales
refresh complete
enable query rewrite
as
select resv_id, sum(actual_rate) from trans group by resv_id;
因此,如果设置了某些参数(如 query_rewrite_enabled = true),则类似如下所示的查询 select city, cust_name
from hotels h, reservations r
where r.hotel_id = h.hotel_id;
将重写为 select city, cust_name
from mv_hotel_resv;
。您可以通过运行该查询并启用自动跟踪来确认 MV。 SQL> set autot traceonly explain
SQL> select city, cust_name
2> from hotels h, reservations r
3> where r.hotel_id = h.hotel_id;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=3 Card=80 Bytes=2480)
1 0 MAT_VIEW ACCESS (FULL) OF 'MV_HOTEL_RESV' (MAT_VIEW) (Cost=3 Card=80 Bytes=2480)
注意,查询是如何从物化视图 MV_HOTEL_RESV 而非表 HOTELS 和 RESERVATIONS 中进行选择的。这正是您所需要的。同样,当您编写一个查询来汇总每个预订编号的实际价格时,将使用物化视图 MV_ACTUAL_SALES 而非表 TRANS。
我们来采用一个不同的查询。如果要查明每个城市的实际销售额,则将发出 select city, sum(actual_rate)
from hotels h, reservations r, trans t
where t.resv_id = r.resv_id
and r.hotel_id = h.hotel_id
group by city;
注意此查询结构:从 MV_ACTUAL_SALES 中,您可以获得 RESV_ID 和预订的总销售额。从 MV_HOTEL_RESV 中,您可以获得 CITY 和 RESV_ID。
您能将这两个 MV 连接在一起吗?当然可以,但在 Oracle 数据库 10g 第 2 版之前,查询重写机制只使用两个 MV 中的一个(而非两个)自动重写用户查询。
以下是 Oracle9i 数据库中的执行计划输出。您可以看到,只使用了 MV_HOTEL_RESV 和 TRANS 的整表扫描。 Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=8 Card=6 Bytes=120)
1 0 SORT (GROUP BY) (Cost=8 Card=6 Bytes=120)
2 1 HASH JOIN (Cost=7 Card=516 Bytes=10320)
3 2 MAT_VIEW REWRITE ACCESS (FULL) OF 'MV_HOTEL_RESV' (MAT_VIEW REWRITE)
(Cost=3 Card=80 Bytes=1040)
4 2 TABLE ACCESS (FULL) OF 'TRANS' (TABLE)
(Cost=3 Card=516 Bytes=3612)
即使 MV 可用,该方法也将生成一个非最优的执行计划。唯一的救济就是创建另一个将所有三个表连接在一起的 MV。但该方法将导致 MV 的增多,从而大大增加刷新 MV 所需的时间。
Oracle 数据库 10g 第 2 版解决了此问题。现在,以上查询将重写为使用两个 MV,如执行计划中所示。 Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=ALL_ROWS (Cost=8 Card=6 Bytes=120)
1 0 SORT (GROUP BY) (Cost=8 Card=6 Bytes=120)
2 1 HASH JOIN (Cost=7 Card=80 Bytes=1600)
3 2 MAT_VIEW REWRITE ACCESS (FULL) OF 'MV_ACTUAL_SALES' (MAT_VIEW REWRITE)
(Cost=3 Card=80 Bytes=560)
4 2 MAT_VIEW REWRITE ACCESS (FULL) OF 'MV_HOTEL_RESV' (MAT_VIEW REWRITE)
(Cost=3 Card=80 Bytes=1040)
注意,该执行计划是如何只使用了 MV 而未使用任何其他基表的。
该增强功能在数据仓库中具有显著的优点,这是因为您不必为每个可能的查询创建和刷新 MV。相反,你可以在关键地方创建几个没有太多连接和聚合的 MV,Oracle 将使用它们来重写查询。


通过备份实现可传输表空间
Oracle8i 中引入的可传输表空间为实现更快的跨数据库数据传输提供了迫切需要的支持。使用此特性,您可以只导出表空间的元数据、传输数据文件并将转储文件导出到目标数据库主机以及导入元数据以将表空间“插入”到目标数据库中。该表空间中的数据在目标数据库中随即可用。该方法解决了数据仓库中曾一度存在的一个很棘手的问题:快速、高效地跨数据库移动数据。
但在 OLTP 数据库中,该条件通常是不可能存在的,因此传输表空间也是不可能的。如果 OLTP 数据库是数据仓库的数据源,则您可能始终无法使用可传输表空间加载它。
在 Oracle 数据库 10g 第 2 版中,可以传输表空间并从另一个数据源(即备份)中插入它。例如,如果要传输表空间 ACCDATA,则可以发出 RMAN 命令 RMAN> transport tablespace accdata
2> TABLESPACE DESTINATION = '/home/oracle'
3> auxiliary destination = '/home/oracle';
该命令在位置 /home/oracle 中创建一个辅助实例,并从其中的备份恢复文件。此辅助实例的名称是随机生成的。创建实例后,该过程将基于目录创建一个目录对象,并恢复表空间 ACCDATA(我们正在传输的表空间)的文件 - 所有操作均自动完成,您不必发出任何命令!
目录 /home/oracle 将包含表空间 ACCDATA 的所有数据文件、表空间元数据的转储文件以及脚本 impscrpt.sql(最重要的)。该脚本包含将此表空间插入目标表空间所必需的所有命令。该表空间并非由 impdp 命令进行传输,而是通过对 dbms_streams_tablespace_adm.attach_tablespaces 程序包的调用进行传输。可以在该脚本中找到所有必要的命令。
您可能会问,如果出现错误该怎么办?这种情况下,可以轻松地进行诊断。首先,该辅助实例在 $ORACLE_HOME/rdbms/log 中创建警报日志文件,以便您可以检查该日志以查明潜在的问题。其次,在提供 RMAN 命令时,您可以通过发出 RMAN 命令(该命令将所有输出置于文件 tts.log 中)将命令和输出重定向到日志文件 rman target=/ log=tts.log
然后,您便可以检查该文件来查明故障的确切原因。
最后,将把这些文件恢复到 /home/oracle 的 TSPITR__ 目录中。例如,如果主数据库的 SID 为 ACCT,RMAN 创建的辅助实例的 SID 为 KYED,则目录名为 TSPITR_ACCT_KYED。该目录还包含两个其他子目录:datafile(用于数据文件)和 onlinelog(用于重做日志)。在完成新表空间的创建之前,可以查看该目录以了解恢复了哪些文件。(这些文件在该过程结束时会被删除。)
长期以来,DBA 一直期待着能够通过 RMAN 备份创建一个可传输的表空间。但请注意,您是从备份(而不是从联机表空间)中插入传输的表空间。因此,它将不是最新的。


对已分区的按索引组织的表实现快速的分区分割
考虑这样一种情况:假设您拥有一个已分区的表。月末到了,但您忘了为下一个月定义分区。您现在有哪些选择呢?
您唯一的救济方法就是将最大值分区分割为两个部分:一个用于新月份的分区和一个新的最大值分区。但将该方法用于已分区的按索引组织的表时将遇到一个小问题。这种情况下,将先创建物理分区,并将行从最大值分区移动到该分区,这样将消耗 I/O 和 CPU 周期。
在 Oracle 数据库 10g 第 2 版中,该过程得到显著简化。如下图所示,假设您将分区一直定义到 5 月份,然后已经将 PMAX 分区定义为一个通用分区。由于 6 月份没有特定分区,因此 6 月份数据进入 PMAX 分区。灰显的方框显示了填充到该段中的数据。由于只填充了部分 PMAX 分区,因此您只看到一部分灰色区域。

现在,在 6 月 30 日对分区 PMAX 进行分割,以创建 6 月分区和新的 PMAX 分区。由于当前 PMAX 中的所有数据都将进入新的 6 月分区,因此 Oracle 数据库 10g 第 2 版只创建新的最大值分区,并使现有分区成为新创建的月分区。这就导致了根本不会发生数据移动(因此没有“空”的 I/O 和 CPU 周期)。而最好之处在于,ROWID 不会发生变化。


通过联机重新定义将 LONG 转换为 LOB
如果数据仓库数据库已经存在一段时间,并且您要处理大型文本数据,则您可能拥有大量数据类型为 LONG 的列。毋庸质疑,LONG 数据类型在大多数数据操作环境(如通过 SUBSTR 进行搜索)中是没有用处的。您肯定需要将它们转换为 LOB 列。
可以使用 DBMS_REDEFINITION 程序包联机执行该操作。但在 Oracle 数据库 10g 第 2 之前,有一个很大的限制。
将 LONG 列转换为 LOB 列时,您很希望获得高性能;您需要使该过程尽可能地快。如果将表进行了分区,则该过程将跨分区并行执行。但如果未将表进行分区,则该过程将串行执行,从而可能持续很长时间。
幸好,在 Oracle 数据库 10g 第 2 版中,即使表未分区也可以在 DBMS_REDEFINITION 程序包内部执行从 LONG 到 LOB 的联机转换。我们通过一个示例来了解该转换的过程。以下是一个用于保存发送给客户的电子邮件的表。由于邮件正文(存储在 MESG_TEXT 中)通常是较长的文本数据,因此已将该列定义为 LONG。 SQL> desc acc_mesg
Name Null?Type
----------------------------------------- -------- ---------
ACC_NO NOT NULL NUMBER
MESG_DT NOT NULL DATE
MESG_TEXT LONG
您需要将该列转换为 CLOB。首先,创建一个结构相同的(最后一列除外,它被定义为 CLOB)空临时表。 create table ACC_MESG_INT
(
acc_no number,
mesg_dt date,
mesg_text clob
);
现在,启动重新定义过程。 1 begin
2 dbms_redefinition.start_redef_table (
3 UNAME => 'ARUP',
4 ORIG_TABLE => 'ACC_MESG',
5 INT_TABLE => 'ACC_MESG_INT',
6 COL_MAPPING => 'acc_no acc_no, mesg_dt mesg_dt, to_lob(MESG_TEXT) MESG_TEXT'
7 );
8* end;
注意第 6 行,该行已经对列进行了映射。前两列保持不变,但第三列 MESG_TEXT 已被映射,以便通过对源表的列应用函数 TO_LOB 来填充目标表的 MESG_TEXT 列。
如果要重新定义的表很大,则需要定期对源表和目标表之间的数据进行同步。该方法加快了最终同步的速度。 begin
dbms_redefinition.sync_interim_table(
uname => 'ARUP',
orig_table => 'ACC_MESG',
int_table => 'ACC_MESG_INT'
);
end;
/
根据表的大小,您可能需要多次执行以上命令。最后,使用以下代码完成重新定义过程 begin
dbms_redefinition.finish_redef_table (
UNAME => 'ARUP',
ORIG_TABLE => 'ACC_MESG',
INT_TABLE => 'ACC_MESG_INT'
);
end;
/
表 ACC_MESG 已经发生了变化: SQL> desc acc_mesg
Name Null?Type
----------------------------------------- -------- ---------
ACC_NO NOT NULL NUMBER
MESG_DT NOT NULL DATE
MESG_TEXT
注意,MESG_TEXT 列现在为 CLOB 而非 LONG。
该特性对于将错误定义的数据结构或原先遗留的数据结构转换为更容易管理的数据类型非常有用。


联机重组单个分区
假设您有一个包含事务历史的表 TRANS。该表基于 TRANS_DATE 进行分区,每个季度作为一个分区。在正常的业务过程中,最新的分区经常更新。某个季度过后,该分区上可能没有很多活动了,因此可以将它移动到其他位置。但移动本身将需要对表进行锁定,从而拒绝对分区的公共访问。如何在不影响其可用性的情况下移动分区?
在 Oracle 数据库 10g 第 2 版中,可以对单个分区使用联机重新定义。您可以像对整个表执行重新定义(使用 DBMS_REDEFINITION 程序包)一样执行此任务,但底层机制并不相同。常规表是通过对源表创建物化视图重新定义的,而单个分区是通过交换分区方法重新定义的。
我们来看一下它的工作原理。以下是 TRANS 表的结构: SQL> desc trans
Name Null?Type
--------------------------------- -------- -------------------------
TRANS_ID NUMBER
TRANS_DATE DATE
TXN_TYPE VARCHAR2(1)
ACC_NO NUMBER
TX_AMT NUMBER(12,2)
STATUS
该表已经按如下所示进行了分区: partition by range (trans_date)
(
partition y03q1 values less than (to_date('04/01/2003','mm/dd/yyyy')),
partition y03q2 values less than (to_date('07/01/2003','mm/dd/yyyy')),
partition y03q3 values less than (to_date('10/01/2003','mm/dd/yyyy')),
partition y03q4 values less than (to_date('01/01/2004','mm/dd/yyyy')),
partition y04q1 values less than (to_date('04/01/2004','mm/dd/yyyy')),
partition y04q2 values less than (to_date('07/01/2004','mm/dd/yyyy')),
partition y04q3 values less than (to_date('10/01/2004','mm/dd/yyyy')),
partition y04q4 values less than (to_date('01/01/2005','mm/dd/yyyy')),
partition y05q1 values less than (to_date('04/01/2005','mm/dd/yyyy')),
partition y05q2 values less than (to_date('07/01/2005','mm/dd/yyyy'))
)
在某个时刻,您决定将分区 Y03Q2 移动到另一个表空间 (TRANSY03Q2),该表空间可能位于一个不同类型的磁盘(一个慢一点、便宜一点的磁盘)上。为此,请首先确认您可以联机重新定义该表: begin
dbms_redefinition.can_redef_table(
uname => 'ARUP',
tname => 'TRANS',
options_flag => dbms_redefinition.cons_use_rowid,
part_name => 'Y03Q2');
end;
/
此处没有输出,因此您确认可以联机重新定义该表。接下来,创建一个临时表保存该分区的数据: create table trans_temp
(
trans_id number,
trans_date date,
txn_type varchar2(1),
acc_no number,
tx_amt number(12,2),
status varchar2(1)
)
tablespace transy03q2
/
请注意,由于表 TRANS 进行了范围分区,因此您已经将该表定义为未分区表。该表在所需的表空间 TRANSY03Q2 中创建。如果表 TRANS 包含一些本地索引,则表示您已经对表 TRANS_TEMP 创建了这些索引(当然是创建为未分区索引)。
现在,您就可以启动重新定义过程: begin
dbms_redefinition.start_redef_table(
uname => 'ARUP',
orig_table => 'TRANS',
int_table => 'TRANS_TEMP',
col_mapping => NULL,
options_flag => dbms_redefinition.cons_use_rowid,
part_name => 'Y03Q2');
end;
/
该调用有几个注意事项。第一,将参数 col_mapping 设置为 NULL;在单个分区重新定义中,该参数没有意义。第二,一个新参数 part_name 指定了要重新定义的分区。第三,注意其中没有 COPY_TABLE_DEPENDENTS 参数,该参数也没有意义,原因是表本身无法更改;只移动分区。
如果该表很大,此操作可能持续很长时间;因此请在操作过程中对它进行同步。 begin
dbms_redefinition.sync_interim_table(
uname => 'ARUP',
orig_table => 'TRANS',
int_table => 'TRANS_TEMP',
part_name => 'Y03Q2');
end;
/
最后,使用以下代码完成该过程 begin
dbms_redefinition.finish_redef_table(
uname => 'ARUP',
orig_table => 'TRANS',
int_table => 'TRANS_TEMP',
part_name => 'Y03Q2');
end;
此时,分区 Y03Q2 位于表空间 TRANSY03Q2 中。如果该表存在任何全局索引,则它们将被标记为 UNUSABLE 并且必须被重新构建。
单个分区重新定义对于跨表空间移动分区(一个常见的信息生命周期管理任务)很有用。但显而易见,其中存在几个限制。例如,您无法在重新定义过程中更改分区方法(即从范围更改为散列)或更改表的结构。


逐块地删除表
您注意到过删除一个分区的表需要多长时间吗?这是因为每个分区都是一个必须删除的段。在 Oracle 数据库 10g 第 2 版中,当您删除分区的表时,分区将逐个被删除。由于每个分区是单独删除的,因此所需的资源要比删除整个表少。
要演示这个新行为,您可以使用 10046 跟踪跟踪该会话。 alter session set events '10046 trace name context forever, level 12';
然后,删除该表。如果查看跟踪文件,则将看到分区表删除的代码: delete from tabpart$ where bo# = :1
delete from partobj$ where obj#=:1
delete from partcol$ where obj#=:1
delete from subpartcol$ where obj#=:1
请注意,分区是按顺序删除的。该方法最大限度地降低了删除过程中的资源使用率并增强了性能。