오라클 (45)

기본적으로 오라클에는 특수기호에 대해서 검사를 하고 대체할 수 있도록 정의를 해놓은 것이 있는데 이 중에 특수기호 '&'도 포함이 되어있다. 


따라서 insert into table1 values ( 'a & b'); 라는 문장을 실행하면 SQLDeveloper에서는 대체문자를 설정하라고 팝업창이 뜬다.


처음에는 이게 뭐지? 하고서 그냥 엔터를 쳤었는데 나중에 보니 그렇게 엔터쳤던 것 때문에 insert할 때 &가 공백으로 바뀌어져서 내용이 들어가있었다.


그런데 이렇게 특수기호가 들어간 문장을 수 백개 insert해야 한다면....???


일일히 다 대체문자를 입력할 수가 없다!!!


이럴 때 유용하게 쓸 수 있는게


SQL> set define off;


이다.


위 명령어는 현재 세션에 대해서 "특수기호 대체 검사"를 하지 않도록 해준다. 

💻 Programming/Oracle 11g

오라클 프로세스 수 늘리기

How to increase the number of processes at database level in Oracle?

 


1.  Log on to the database as system administrator

  •  In SQL Plus enter: "connect / as sysdba"

2. Set the number of processes to be desired value <num_processes>

  • In SQL Plus "alter system set processes=<num_processes> scope=spfile"

3. Shutdown the database

  • In SQL Plus "shutdown immediate"


4. Startup the database 

  • In SQL Plus "startup"


5. Check the changes have taken effect.

  • In SQL Plus "show parameter sessions" and "show parameter processes"






​출처 : http://knowledgebase.progress.com/articles/Article/P164971

 

CentOS 6에 오라클 11g 설치시에 설정해주는 커널 파라미터 의미


ㅇ 커널 파라미터 설정

/etc/sysctl.conf 파일

fs.aio-max-nr = 1048576
fs.file-max = 6815744
#kernel.shmall = 2097152
#kernel.shmmax = 536870912
kernel.shmmni = 4096
kernel.sem = 250 32000 100 128
net.ipv4.ip_local_port_range = 9000 65500
net.core.rmem_default = 262144
net.core.rmem_max = 4194304
net.core.wmem_default = 262144
net.core.wmem_max = 1048576

아래 링크를 참조하시길 바랍니다.


https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/5/html-single/Tuning_and_Optimizing_Red_Hat_Enterprise_Linux_for_Oracle_9i_and_10g_Databases/#sect-Oracle_9i_and_10g_Tuning_Guide-Setting_Semaphores-Setting_Semaphore_Parameters


https://docs.oracle.com/cd/E11882_01/install.112/e24326/toc.htm#BHCCADGD



select 

owner, segment_name, segment_type, sum(bytes)/1024/1024 as MB

from 

dba_segments

where

owner='{username in uppercase}' 

group by owner, segment_name, segment_type order by MB

💻 Programming/Oracle 11g

ORA-01157: cannot identify/lock data file

ORA-01157: cannot identify/lock data file

데이타 파일을 실수로 삭제해버렸다가 DB재시작하게되면 아래처럼 오류가 발생하게 된다.

내가 아닌 누군가가 dbf생성했다가 drop명령을 쓰지않고 unix 콘솔에서 dbf파일을 삭제하게 되면 발생하게되는 오류인데 생각보다 쉽게 해결할 수 있습니다.



ERROR at line 1:
ORA-01157: cannot identify/lock data file 29 - see DBWR trace file
ORA-01110: data file 29:
'/opt/oracle/product/oracle9i/dbs/C:oracleproduct10.2.0oradatapentahoptho_ts.dbf
'

from above datafile name you have realized that its a kinda jerk :s someone has made a datafile with no sense and then he/she have removed the file by O.S command, but he/she did'nt updated database about it !

So during the test when we were starting this database it came untill mount stage and then got stuck !!! i.e.

Problem:

SQL> alter database open;
alter database open
*
ERROR at line 1:
ORA-01157: cannot identify/lock data file 29 - see DBWR trace file
ORA-01110: data file 29:
'/opt/oracle/product/oracle9i/dbs/C:oracleproduct10.2.0oradatapentahoptho_ts.dbf
'

so to fix it, i did following:

Solution:


SQL>alter database datafile 29 OFFLINE DROP;
SQL>alter database open;


출처 : http://nayyares.blogspot.kr/2009/08/ora-01157-cannot-identifylock-data-file.html


여러가지 이유로 테이블을 삭제할 상황이 있다.

Drop table로 하나하나 지우기에 너무 양이 많을때 테이블 전체를 한번에 삭제하는 방법이 있다.

그 방법을 아래에 소개하겠다.

 

먼저 sql에 접속한 후

 

SQL> Begin

2    for c in (select table_name from user_tables) loop

3    execute immediate ('drop table '||c.table_name||' cascade constraints');

4    end loop;

5    End;

6    . <- 쩜 찍어야 합니다.

엔터

 

이렇게 입력하면

 

SQL>

 

다시 이렇게 아무것도 안뜨는 명령어가 뜬다.

 

SQL> run

을 실행한다.

실행하면 아래 구문이 나올것이다.

 

1    Begin

 

2    for c in (select table_name from user_tables) loop

3    execute immediate ('drop table '||c.table_name||' cascade constraints');

4    end loop;

5    End;

PL/SQL procedure successfully completed.<--메시지가 나오면 성공

 

그 후에

SQL> purge recyclebin; 

Recyclebin purged.<===휴지통이 비워졌다는 문구가 나온다.

 

그후 select * from tab;을 해보면 깨끗히 지워진 테이블을 볼 수 있을것이다.




출처 : http://munjee.tistory.com/16

일반 사용자 계정으로 제약사항 목록을 조회하려면 아래 쿼리를 사용하면 됩니다.


select * from user_constraints;



여러개의 테이블을 한꺼번에 삭제하고 싶은데 like 조건절을 쓸 수가 없다.

뭐 결국 테이블 하나하나를 각각 삭제해줘야 한다는 건데....그렇다고 테이블이 수 십개가 넘는데 이걸 다 손으로 타이핑하기란 짜증하는 일이 아닐 수 없다.


이럴 때 다음 쿼리로 특정 사용자의 전체 테이블을 삭제 할 수 있는 쿼리를 얻어올 수 있다.

 

SELECT 'DROP TABLE ' || TABLE_NAME || ';' FROM ALL_TABLES WHERE OWNER = 'user1';


조건절은 마음대로 바꿀 수 있고 위에서 사용한 ALL_TABLES는 DBA권한이 있어야 접근이 가능하다.


따라서 일반 사용자이면서 자신이 소유한 테이블을 삭제할 때는


ALL_TABLES 대신 USER_TABLES 에서 조회한 테이블명을 사용하면 된다.


SELECT 'DROP TABLE ' || TABLE_NAME || ';' FROM USER_TABLES WHERE OWNER = 'user2';


테이블을 삭제하려면
SQL > drop table 테이블명;

하면 됩니다. 그런데 사실 이게 물리적으로 공간을 다시 내주는 것이 아닙니다.
오라클의 drop table관련 문서를 보면 테이블을 drop할 때 purge라는 옵션을 주지 않으면 휴지통으로 넣기만 하고 물리적인 공간은 그대로 가지고 있게 됩니다. 윈도우의 휴지통 개념과 동일하다고 보면 됩니다.

오라클이 10g에서 새롭게 추가한 '휴지통' 이라는 기능.

테이블을 DROP 하더라도 언제던지 복원할 수 있게 하고자 하는 것이고요,
윈도우의 휴지통과 같다고 보시면 됩니다.

 

휴지통에 들은 테이블을 조회.
SQL> show recyclebin;

 

휴지통의 모든 내용이 비워집니다.
SQL> purge recyclebin;

 

삭제된 테이블을 되살리고 싶다면
SQL> flashback table 테이블명 to before drop;

 

만약, 특정 테이블을 휴지통에 남기지 않고 모두 삭제하려면..
SQL> drop table 테이블명 purge;

 

purge문 없이 그냥 drop 한 후에는
SQL> purge table 테이블명;


instr함수는 어떤 스트링의 특정위치에서 시작해서 특정 케릭터가 위치한 곳까지의 위치를 계산해서 반환해주는 함수입니다.


Java의 split 메소드와 같은 기능을 구현할때 오라클의 substr함수와 함께 같이 사용될 수 있죠.


오라클 문서에서 설명하는 instr함수에 대한 정의 및 문법은 아래와 같습니다.


The INSTR functions (INSTR, INSTRB, INSTRC, INSTR2, and INSTR4) searches a string for a substring using characters and returns the position in the string that is the first character of a specified occurrence of the substring. The functions vary in how they determine the position of the substring to return.

  • INSTR calculates lengths using characters as defined by the input character set.

  • INSTRB calculates lengths using bytes.

  • INSTRC calculates lengths using Unicode complete characters.

  • INSTR2 calculates lengths using UCS2 code points.

  • INSTR4 calculates lengths using UCS4 code points.

반환값

A nonzero INTEGER when the search is successful or 0 (zero) when it is not.


문법 

{INSTR | INSTRB | INSTRC | INSTR2 | INSTR4} (string , substring [, position [, occurrence]])


그럼 간단한 예제를 한번 보시죠.


abc.def.ghi.jkl 이라는 패키지명이 있다고 해봅시다.


이 패키지명에서 abc.def ( 두번째 depth 까지)만 추출해내고 싶은데 그러려면 substr을 생각하실 수도 있겠죠.


select substr(packageNm, 1, 7) from ClassTable;


이렇게 하면 결과는 abc.def 가 나올 것입니다.


하지만 만약 패키지명이 3글자.3글자 형태가 아니면 어떻게 될까요?


a.b.c.d.e.f 라는 패키지명을 위처럼 자르면 a.b.c.d 라고 잘려서 나오겠죠?


두번째 depth가 아니라 7번째까지 출력이 되버리겠군요.


이때 instr 함수를 사용을 합니다.


select substr( packageNm, 1, instr( packageNm, '.', 1, 2) - 1 ) ) from ClassTable;


이렇게 함수를 써줍니다.


instr( packageNm, '.', 1, 2 ) 라는 부분은 packageNm이라는 스트링을 '.' (마침표)로 구분을 짓고 1번째 인덱스에서 시작해서 2번째 마침표가 나오는 인덱스를 반환을 합니다. 그러면 마침표가 있는 인덱스가 나오기 때문에 -1을 해준 값을 substr함수의 length에 넣어주면 두번째 depth까지 substr한 결과가 나오게 됩니다.

### 캐릭터셋 변경전 확인 사항 :

sqlplus '/as sysdba'

sql>select instance from v$thread;

INSTANCE
----------------
ora9i


oracle\ora92\network\admin\snmp_ro.ora 를 봐도 된다.
=> snmp.SID.Oracle = ORACLE




select parameter, value from nls_database_parameters where parameter like '%CHAR%';

PARAMETERVALUE
NLS_NUMERIC_CHARACTERS.,
NLS_CHARACTERSETKO16KSC5601
NLS_NCHAR_CONV_EXCPFALSE
NLS_NCHAR_CHARACTERSETUTF8


########### 캐릭터셋 변경 ##########################

****  connect sys as sysdba;

NLS CHARACTERSET 변경방법 (DB REBUILD 없이)
Bulletin no : 10016

--------------------------------------------------------------------------------

  데이타베이스의  CHARACTER SET은 데이타 딕셔너리 테이블인 sys.props$에
 들어 있다

   SQL>desc sys.props$
   Name                                Null?                  Type
   -------------------------------  -----------------      ---------------
   NAME                               NOT NULL             VARCHAR2(30)
   VALUE$                                                  VARCHAR2(2000)
   COMMENT$                                                VARCHAR2(2000)

   SQL>column c1 format a30
   SQL>select name c1, value$ c1 from sys.props$;

   C1                              C1
   -----------------------------   ------------------------------
   DICT.BASE                        2
   NLS_LANGUAGE                     AMERICAN
   NLS_TERRITORY                    AMERICA
   NLS_CURRENCY                     $
   NLS_ISO_CURRENCY                 AMERICA
   NLS_NUMERIC_CHARACTERS           .,
   NLS_DATE_FORMAT                  DD-MON-YY
   NLS_DATE_LANGUAGE                AMERICAN
   NLS_CHARACTERSET                 US7ASCII
   NLS_SORT                         BINARY
   GLOBAL_DB_NAME                   NLSV7.WORLD
   
  여기서 NLS_CHARACTERSET에 현재 DB의 CHARACTER SET이 들어 있는데 이 값을
 변경하여 DB의 CHARACTER SET을 변경할 수 있다. 여기서는 US7ASCII에서
 KO16KSC5601 로 옮기는 경우를 알아보자.

 우선 바꾸고자 하는 CHRACTER SET이 지원되는 지를 다음 명령으로 확인한다.

         select convert('a','WE8DEC','KO16MSWIN949') from dual; 

  만약 이 Select 문에서 ORA-01482 에러가 발생하면 지정한 CHARACTER SET이
 지원되지 않는  경우이며  에러가 발생하지 않으면 CHARACTER SET을 변경할 수
 있다.
 
  작업을 하기전에는 만약을 위해서 DB 전체를 백업 받아두도록 한다.
  CHARACTER SET 을 잘못 변경하면 DB 를 OPEN 할수가 없기 때문이다.
  ---------------------------------------------------------------

1.  다음의 Update문을 실행하여 CHARACTER SET을 변경한다.

   UPDATE sys.props$
   SET value$ = 'WE8DEC'
   WHERE name = 'NLS_CHARACTERSET';

  Update 시에 NLS_CHARACTERSET을 지원되지 않는 값으로 잘못 설정하거나
 실수로 콘트롤 문자 같은 것이 들어가게 되면 DB가 Shutdown 된 다음에는
 Startup 이 안 되므로 Update 후에 다음 명령으로 확인을 한 다음에  Commit을
 하도록 한다.

        select name, value$
        from sys.props$
        where value$ = 'KO16KSC5601';

 Select   제대로 출력되면 Commit 하고 Shutdown 했다가 Startup 하게 되면
 새로운 CHARACTER SET 값을 갖게 된다. SELECT가 안 되면 ROLLBACK하고 UPDATE
 부터 다시 하도록 한다.

2. 환경 변수 NLS_LANG 을 변경한다.

.profile ( or .cshrc) 에서

NLS_LANG=American_America.KO16KSC5601; export NLS_LANG

or

setenv NLS_LANG American_America.KO16KSC5601






출처 : http://develop.sunshiny.co.kr/201



SQL 커맨드라인에다가 2줄짜리 쿼리문을 한참 치고서 실행했더니 오타가 나서 다시 쳐야한다면???


OMG !!!!!!!!!!


이런 경우를 대비해서 오라클은 SQL 에디터를 제공해준다. 그런데 기본적으로 제공해주는게 뭔진 몰라도 불편하다.


좀더 편하게 하자면 우리가 유닉스 환경에서 자주 사용하는 vi 에디터를 SQL 기본 에디터로 설정해서 사용할 수 있는데


이 기본 에디터는 _editor 라는 변수안에 들어가있다.


SQL > define _editor = vi  


위 명령어를 실행하면 다음부터는 ed라는 명령어를 이용해서 방금 쳤던 명령어를 vi에디터 모드에서 다시 보여준다.


짱이다.

DB작업을 하다가보면 내 계정에 어떤 권한이 있는지 확인해야할 때가 있다.

권한자체가 없으면 작업자체가 안될 수도 있으니까 말이다.

아래 쿼리문은 현재 접속한 계정의 권한을 보여준다.


select * from user_sys_privs;



user_sys_privs외에도 권한과 관련된 다양한 테이블 또는 뷰(view)들이 있다. 아래는 그 테이블들의 목록이다.

각 테이블들이 어떠한 정보를 가지고 있는지는 한번 보면서 확인해보길 바란다.



ALL_COL_PRIVS
ALL_COL_PRIVS_MADE
ALL_COL_PRIVS_RECD
ALL_REPGROUP_PRIVILEGES
ALL_TAB_PRIVS
ALL_TAB_PRIVS_MADE
ALL_TAB_PRIVS_RECD


USER_AQ_AGENT_PRIVS
USER_COL_PRIVS
USER_COL_PRIVS_MADE
USER_COL_PRIVS_RECD
USER_REPGROUP_PRIVILEGES

TNAME
------------------------------
USER_ROLE_PRIVS
USER_SYS_PRIVS
USER_TAB_PRIVS
USER_TAB_PRIVS_MADE
USER_TAB_PRIVS_RECD


💻 Programming/Oracle 11g

[Oracle/SQL] 특정 사용자의 권한 조회

사용자계정이 SAMPLE 인 사용자의 권한을 조회하려면 아래와 같이 하시면 됩니다.

select privilege
from dba_sys_privs
where grantee='SAMPLE'
order by 1;

Oracle 10g Character set 변경(EUC-KR을 UTF8 변경) :
참고

kor 버전으로 받는다면 기본적으로 설치되는 버전은 KO16MSWIN949 일 경우가 많다.
한글을 지원하는 Character Set을 비교해보자.

1. KO16KSC5601
- 한글 지원상태 : 한글 2350자
- 지원버전 : 7.x
- 한글바이트 : 2바이트

2. KO16MSWIN949
- 한글 지원상태 : KO16KSC5601 + 확장 ( 총 11172자 )
- 지원버전 : 8.0.6 이상
- 한글바이트 : 2바이트

3. UTF8
- 한글 지원상태 : 한글 11172자
- 지원버전 : 8.0 이후
- 한글바이트 : 3바이트

4. AL32UTF8
- 한글 지원상태 : 한글 11172자
- 지원버전 : 9i Release 1 이상
- 한글바이트 : 3바이트

UTF8은 많은 문자를 지원하지만 한글을 3바이트 소모한다는 단점이 있다.
(못느낄 정도로 빠른 컴퓨터가 별 차이가 없을 듯함)
캐릭터셋이 어떻게 설정되어 있나 확인 쿼리는 다음과 같다ㅣ

SELECT * FROM sys.props$ where name='NLS_CHARACTERSET';

Oracle 10g Character Set 변경
SQLPLUS 접속후 (system 계정으로 로그인 혹시 모른다면 sqlplus /nolog; 후 conn /as sysdba; 로 접속한다)

C:\>sqlplus /nolog;
sql>conn /as sysdba;
변경하고자하는 캐릭터셋을 수정

sql>update sys.props$ set value$='UTF8' where name='NLS_CHARACTERSET';
sql>update sys.props$ set value$='UTF8' where name='NLS_NCHAR_CHARACTERSET';
sql>
update sys.props$ set value$='KOREAN_KOREA.UTF8' where name='NLS_LANGUAGE';
sql>commit;
sql>shutdown immediate;
sql>startup mount;
sql>alter system enable restricted session;
sql>alter system set job_queue_processes=0;
sql>alter system set aq_tm_processes=0;
sql>alter database open;
sql>alter database character set UTF8;
sql>shutdown immediate;
sql>startup;

출처 : http://www.garubi.com/11

💻 Programming/Oracle 11g

[오라클/oracle] select ... for update

테이블에 어떤 데이타를 insert, update, delete를 할 때는 한 명의 사용자만이 해당 테이블을 변경할 수 있도록 해준다.


그 런데 만약 A테이블에서 데이타를 읽어와서 이와 관련된 내용을 B테이블에 넣으려는데 B테이블에 FK로 A테이블의 PK가 걸려있다고 가정해보자. 나는 A테이블에서 pk가 1인 데이타를 읽어와서 B테이블에 업데이트를 해줘야하는데 데이타를 읽어와서 업데이트 하기 전에 누군가가 데이타 1을 지워버렸다면?? 내가 B테이블에 업데이트를 시도할 때 fk 오류가 날 것이다. pk가 사라졌으니 말이다.


자 이럴 때 사용할 수 있는 것이 select ... for update 이다.

 


for update는 읽어온 모든 rows와 그 rows와 관련된 인덱스 엔트리까지 lock을 한다. 이는 update문을 사용했을 때와 동일한 것이다. for update로 실행된 select문은 다른 모든 업데이트 관련 쿼리를 사용할 수 없다. 따라서 또 다른 select ... for update 문 역시 기존 세션에서 commit이나 rollback이 실행되기 전까지 기다렸다가 최신정보를 select하게 된다.


사용법은 단순하다.


SQL > select * from emp for update;


SQL FOREIGN KEY Constraint on ALTER TABLE

- 외래키 추가하기

MySQL / SQL Server / Oracle / MS Access:

ALTER TABLE Orders
ADD FOREIGN KEY (P_Id)
REFERENCES Persons(P_Id)


- 외래키 이름 지정해주기

MySQL / SQL Server / Oracle / MS Access:

ALTER TABLE Orders
ADD CONSTRAINT fk_PerOrders
FOREIGN KEY (P_Id)
REFERENCES Persons(P_Id)


To DROP a FOREIGN KEY Constraint

- 외래키 삭제하기 


MySQL:

ALTER TABLE Orders
DROP FOREIGN KEY fk_PerOrders

SQL Server / Oracle / MS Access:

ALTER TABLE Orders
DROP CONSTRAINT fk_PerOrders


프로젝트를 진행하다 보면 테이블 모델링을 수정해야 할 때가 있다. 이런 저런 추가요구가 들어오면 말이다.

 

그럴 때는 간단하게 SQL 한문장으로 후딱 해치워 버리면 된다.

 

§ 아래는 테이블에 하나의 컬럼을 추가로 만들어 주기위한 SQL문이다.

 

ALTER TABLE table_name ADD column_name datatype

 

§ 테이블에서 필요없는 컬럼을 삭제하려면 아래와 같은 명령어를 사용하면 된다.

 

ALTER TABLE table_name DROP COLUMN column_name

§ 또한 특정 컬럼의 데이타 타입을 변경하려면 아래와 같은 명령어를 사용하면 된다.

Oracle 10G and later: ALTER TABLE table_name MODIFY column_name datatype




§ 오라클이 아닌 다른 데이타베이스 시스템의 경우 아래와 같이 사용하면 된다.

SQL Server / MS Access:

ALTER TABLE table_name ALTER COLUMN column_name datatype

My SQL / Oracle:

ALTER TABLE table_name MODIFY COLUMN column_name datatype


💻 Programming/Oracle 11g

[오라클/oracle] Insert 속도 높이기

대량의 데이터를 처리해야하는 프로그램을 만들다보면 성능문제를 인식하지 않을 수 없다. JMeter를 이용하여 웹애플리케이션을 테스트 하면서 성능개선을 위해서 이것 저것 해보다가 오라클에서 제공하는 " Insert 문의 속도 "에 관한 문서를 발견하였다.  

 

아래 그 문서의 내용이 있으며 insert문의 속도와 관련된 사항들 및 어떻게 속도를 높일 수 있는지에 대하여 설명을 하고 있다. 

출처 : https://docs.oracle.com/cd/E17952_01/refman-5.1-en/insert-speed.html

Speed of INSERT Statements

The time required for inserting a row is determined by the following factors, where the numbers indicate approximate proportions:

  • Connecting: (3)

  • Sending query to server: (2)

  • Parsing query: (2)

  • Inserting row: (1 × size of row)

  • Inserting indexes: (1 × number of indexes)

  • Closing: (1)

This does not take into consideration the initial overhead to open tables, which is done once for each concurrently running query.

The size of the table slows down the insertion of indexes by log N, assuming B-tree indexes.

You can use the following methods to speed up inserts:

  • If you are inserting many rows from the same client at the same time, use INSERT statements with multiple VALUES lists to insert several rows at a time. This is considerably faster (many times faster in some cases) than using separate single-row INSERT statements. If you are adding data to a nonempty table, you can tune the bulk_insert_buffer_size variable to make data insertion even faster. See Section 5.1.4, “Server System Variables”.

  • If multiple clients are inserting a lot of rows, you can get higher speed by using the INSERT DELAYED statement. See Section 13.2.5.2, “INSERT DELAYED Syntax”.

  • For a MyISAM table, you can use concurrent inserts to add rows at the same time that SELECT statements are running, if there are no deleted rows in middle of the data file. See Section 8.7.3, “Concurrent Inserts”.

  • When loading a table from a text file, use LOAD DATA INFILE. This is usually 20 times faster than using INSERT statements. See Section 13.2.6, “LOAD DATA INFILE Syntax”.

  • With some extra work, it is possible to make LOAD DATA INFILE run even faster for a MyISAM table when the table has many indexes. Use the following procedure:

    1. Optionally create the table with CREATE TABLE.

    2. Execute a FLUSH TABLES statement or a mysqladmin flush-tables command.

    3. Use myisamchk --keys-used=0 -rq /path/to/db/tbl_name. This removes all use of indexes for the table.

    4. Insert data into the table with LOAD DATA INFILE. This does not update any indexes and therefore is very fast.

    5. If you intend only to read from the table in the future, use myisampack to compress it. See Section 14.5.3.3, “Compressed Table Characteristics”.

    6. Re-create the indexes with myisamchk -rq /path/to/db/tbl_name. This creates the index tree in memory before writing it to disk, which is much faster that updating the index during LOAD DATA INFILE because it avoids lots of disk seeks. The resulting index tree is also perfectly balanced.

    7. Execute a FLUSH TABLES statement or a mysqladmin flush-tables command.

    LOAD DATA INFILE performs the preceding optimization automatically if the MyISAM table into which you insert data is empty. The main difference between automatic optimization and using the procedure explicitly is that you can let myisamchk allocate much more temporary memory for the index creation than you might want the server to allocate for index re-creation when it executes the LOAD DATA INFILE statement.

    You can also disable or enable the nonunique indexes for a MyISAM table by using the following statements rather than myisamchk. If you use these statements, you can skip the FLUSH TABLE operations:

    ALTER TABLE tbl_name DISABLE KEYS;
    ALTER TABLE tbl_name ENABLE KEYS;
    
  • To speed up INSERT operations that are performed with multiple statements for nontransactional tables, lock your tables:

    LOCK TABLES a WRITE;
    INSERT INTO a VALUES (1,23),(2,34),(4,33);
    INSERT INTO a VALUES (8,26),(6,29);
    ...
    UNLOCK TABLES;
    

    This benefits performance because the index buffer is flushed to disk only once, after all INSERT statements have completed. Normally, there would be as many index buffer flushes as there are INSERT statements. Explicit locking statements are not needed if you can insert all rows with a single INSERT.

    To obtain faster insertions for transactional tables, you should use START TRANSACTION and COMMIT instead of LOCK TABLES.

    Locking also lowers the total time for multiple-connection tests, although the maximum wait time for individual connections might go up because they wait for locks. Suppose that five clients attempt to perform inserts simultaneously as follows:

    • Connection 1 does 1000 inserts

    • Connections 2, 3, and 4 do 1 insert

    • Connection 5 does 1000 inserts

    If you do not use locking, connections 2, 3, and 4 finish before 1 and 5. If you use locking, connections 2, 3, and 4 probably do not finish before 1 or 5, but the total time should be about 40% faster.

    INSERT, UPDATE, and DELETE operations are very fast in MySQL, but you can obtain better overall performance by adding locks around everything that does more than about five successive inserts or updates. If you do very many successive inserts, you could do a LOCK TABLES followed by an UNLOCK TABLES once in a while (each 1,000 rows or so) to permit other threads to access table. This would still result in a nice performance gain.

    INSERT is still much slower for loading data than LOAD DATA INFILE, even when using the strategies just outlined.

  • To increase performance for MyISAM tables, for both LOAD DATA INFILE and INSERT, enlarge the key cache by increasing the key_buffer_size system variable. See Section 8.9.2, “Tuning Server Parameters”.



Partitioned Tables and Indexes

This chapter describes partitioned tables and indexes. It covers the following topics:

Introduction to Partitioning

Partitioning addresses key issues in supporting very large tables and indexes by letting you decompose them into smaller and more manageable pieces called partitions. SQL queries and DML statements do not need to be modified in order to access partitioned tables. However, after partitions are defined, DDL statements can access and manipulate individuals partitions rather than entire tables or indexes. This is how partitioning can simplify the manageability of large database objects. Also, partitioning is entirely transparent to applications.

Each partition of a table or index must have the same logical attributes, such as column names, datatypes, and constraints, but each partition can have separate physical attributes such as pctfree, pctused, and tablespaces.

Partitioning is useful for many different types of applications, particularly applications that manage large volumes of data. OLTP systems often benefit from improvements in manageability and availability, while data warehousing systems benefit from performance and manageability.


Note:

All partitions of a partitioned object must reside in tablespaces of a single block size.


See Also:

Partitioning offers these advantages:

  • Partitioning enables data management operations such data loads, index creation and rebuilding, and backup/recovery at the partition level, rather than on the entire table. This results in significantly reduced times for these operations.
  • Partitioning improves query performance. In many cases, the results of a query can be achieved by accessing a subset of partitions, rather than the entire table. For some queries, this technique (called partition pruning) can provide order-of-magnitude gains in performance.
  • Partitioning can significantly reduce the impact of scheduled downtime for maintenance operations.

    Partition independence for partition maintenance operations lets you perform concurrent maintenance operations on different partitions of the same table or index. You can also run concurrent SELECT and DML operations against partitions that are unaffected by maintenance operations.

  • Partitioning increases the availability of mission-critical databases if critical tables and indexes are divided into partitions to reduce the maintenance windows, recovery times, and impact of failures.
  • Partitioning can be implemented without requiring any modifications to your applications. For example, you could convert a nonpartitioned table to a partitioned table without needing to modify any of the SELECT statements or DML statements which access that table. You do not need to rewrite your application code to take advantage of partitioning.

Figure 11-1 offers a graphical view of how partitioned tables differ from nonpartitioned tables.

Figure 11-1 A View of Partitioned Tables


Text description of the illustration cncpt162.gif


Partition Key

Each row in a partitioned table is unambiguously assigned to a single partition. The partition key is a set of one or more columns that determines the partition for each row. Oracle9i automatically directs insert, update, and delete operations to the appropriate partition through the use of the partition key. A partition key:

  • Consists of an ordered list of 1 to 16 columns
  • Cannot contain a LEVEL, ROWID, or MLSLABEL pseudocolumn or a column of type ROWID
  • Can contain columns that are NULLable

Partitioned Tables

Tables can be partitioned into up to 64,000 separate partitions. Any table can be partitioned except those tables containing columns with LONG or LONG RAW datatypes. You can, however, use tables containing columns with CLOB or BLOB datatypes.

Partitioned Index-Organized Tables

You can range partition index-organized tables. This feature is very useful for providing improved manageability, availability and performance for index-organized tables. In addition, data cartridges that use index-organized tables can take advantage of the ability to partition their stored data. Common examples of this are the Image and interMedia cartridges.

For partitioning an index-organized table:

  • Only range and hash partitioning are supported
  • Partition columns must be a subset of primary key columns
  • Secondary indexes can be partitioned -- locally and globally
  • OVERFLOW data segments are always equipartitioned with the table partitions

Partitioning Methods

Oracle provides the following partitioning methods:

Figure 11-2 offers a graphical view of the methods of partitioning.

Figure 11-2 List, Range, and Hash Partitioning


Text description of the illustration cncpt158.gif


Composite partitioning is a combination of other partitioning methods. Oracle currently supports range-hash and range-list composite partitioning. Figure 11-3 offers a graphical view of range-hash and range-list composite partitioning.

Figure 11-3 Composite Partitioning


Text description of the illustration cncpt168.gif


Range Partitioning

Range partitioning maps data to partitions based on ranges of partition key values that you establish for each partition. It is the most common type of partitioning and is often used with dates. For example, you might want to partition sales data into monthly partitions.

When using range partitioning, consider the following rules:

  • Each partition has a VALUES LESS THAN clause, which specifies a noninclusive upper bound for the partitions. Any binary values of the partition key equal to or higher than this literal are added to the next higher partition.
  • All partitions, except the first, have an implicit lower bound specified by the VALUES LESS THAN clause on the previous partition.
  • A MAXVALUE literal can be defined for the highest partition. MAXVALUE represents a virtual infinite value that sorts higher than any other possible value for the partition key, including the null value.

A typical example is given in the following section. The statement creates a table (sales_range) that is range partitioned on the sales_date field.

Range Partitioning Example

CREATE TABLE sales_range 
(salesman_id  NUMBER(5), 
salesman_name VARCHAR2(30), 
sales_amount  NUMBER(10), 
sales_date    DATE)
PARTITION BY RANGE(sales_date) 
(
PARTITION sales_jan2000 VALUES LESS THAN(TO_DATE('02/01/2000','DD/MM/YYYY')),
PARTITION sales_feb2000 VALUES LESS THAN(TO_DATE('03/01/2000','DD/MM/YYYY')),
PARTITION sales_mar2000 VALUES LESS THAN(TO_DATE('04/01/2000','DD/MM/YYYY')),
PARTITION sales_apr2000 VALUES LESS THAN(TO_DATE('05/01/2000','DD/MM/YYYY'))
);

List Partitioning

List partitioning enables you to explicitly control how rows map to partitions. You do this by specifying a list of discrete values for the partitioning key in the description for each partition. This is different from range partitioning, where a range of values is associated with a partition and from hash partitioning, where a hash function controls the row-to-partition mapping. The advantage of list partitioning is that you can group and organize unordered and unrelated sets of data in a natural way.

The details of list partitioning can best be described with an example. In this case, let's say you want to partition a sales table by region. That means grouping states together according to their geographical location as in the following example.

List Partitioning Example

CREATE TABLE sales_list
(salesman_id  NUMBER(5), 
salesman_name VARCHAR2(30),
sales_state   VARCHAR2(20),
sales_amount  NUMBER(10), 
sales_date    DATE)
PARTITION BY LIST(sales_state)
(
PARTITION sales_west VALUES('California', 'Hawaii'),
PARTITION sales_east VALUES ('New York', 'Virginia', 'Florida'),
PARTITION sales_central VALUES('Texas', 'Illinois')
PARTITION sales_other VALUES(DEFAULT)
);

A row is mapped to a partition by checking whether the value of the partitioning column for a row falls within the set of values that describes the partition. For example, the rows are inserted as follows:

  • (10, 'Jones', 'Hawaii', 100, '05-JAN-2000') maps to partition sales_west
  • (21, 'Smith', 'Florida', 150, '15-JAN-2000') maps to partition sales_east
  • (32, 'Lee', 'Colorado', 130, '21-JAN-2000') does not map to any partition in the table

Unlike range and hash partitioning, multicolumn partition keys are not supported for list partitioning. If a table is partitioned by list, the partitioning key can only consist of a single column of the table.

The DEFAULT partition enables you to avoid specifying all possible values for a list-partitioned table by using a default partition, so that all rows that do not map to any other partition do not generate an error.

Hash Partitioning

Hash partitioning enables easy partitioning of data that does not lend itself to range or list partitioning. It does this with a simple syntax and is easy to implement. It is a better choice than range partitioning when:

  • You do not know beforehand how much data maps into a given range
  • The sizes of range partitions would differ quite substantially or would be difficult to balance manually
  • Range partitioning would cause the data to be undesirably clustered
  • Performance features such as parallel DML, partition pruning, and partition-wise joins are important

The concepts of splitting, dropping or merging partitions do not apply to hash partitions. Instead, hash partitions can be added and coalesced.

Hash Partitioning Example

CREATE TABLE sales_hash
(salesman_id  NUMBER(5), 
salesman_name VARCHAR2(30), 
sales_amount  NUMBER(10), 
week_no       NUMBER(2)) 
PARTITION BY HASH(salesman_id) 
PARTITIONS 4 
STORE IN (data1, data2, data3, data4);

The preceding statement creates a table sales_hash, which is hash partitioned on salesman_id field. The tablespace names are data1, data2, data3, and data4.

Composite Partitioning

Composite partitioning partitions data using the range method, and within each partition, subpartitions it using the hash or list method. Composite range-hash partitioning provides the improved manageability of range partitioning and the data placement, striping, and parallelism advantages of hash partitioning. Composite range-list partitioning provides the manageability of range partitioning and the explicit control of list partitioning for the subpartitions.

Composite partitioning supports historical operations, such as adding new range partitions, but also provides higher degrees of parallelism for DML operations and finer granularity of data placement through subpartitioning.

Composite Partitioning Range-Hash Example

CREATE TABLE sales_composite 
(salesman_id  NUMBER(5), 
 salesman_name VARCHAR2(30), 
 sales_amount  NUMBER(10), 
 sales_date    DATE)
PARTITION BY RANGE(sales_date) 
SUBPARTITION BY HASH(salesman_id)
SUBPARTITION TEMPLATE(
SUBPARTITION sp1 TABLESPACE data1,
SUBPARTITION sp2 TABLESPACE data2,
SUBPARTITION sp3 TABLESPACE data3,
SUBPARTITION sp4 TABLESPACE data4)
(PARTITION sales_jan2000 VALUES LESS THAN(TO_DATE('02/01/2000','DD/MM/YYYY'))
 PARTITION sales_feb2000 VALUES LESS THAN(TO_DATE('03/01/2000','DD/MM/YYYY'))
 PARTITION sales_mar2000 VALUES LESS THAN(TO_DATE('04/01/2000','DD/MM/YYYY'))
 PARTITION sales_apr2000 VALUES LESS THAN(TO_DATE('05/01/2000','DD/MM/YYYY'))
 PARTITION sales_may2000 VALUES LESS THAN(TO_DATE('06/01/2000','DD/MM/YYYY')));

This statement creates a table sales_composite that is range partitioned on the sales_date field and hash subpartitioned on salesman_id. When you use a template, Oracle names the subpartitions by concatenating the partition name, an underscore, and the subpartition name from the template. Oracle places this subpartition in the tablespace specified in the template. In the previous statement, sales_jan2000_sp1 is created and placed in tablespace data1 while sales_jan2000_sp4 is created and placed in tablespace data4. In the same manner, sales_apr2000_sp1 is created and placed in tablespace data1 while sales_apr2000_sp4 is created and placed in tablespace data4. Figure 11-4 offers a graphical view of the previous example.

Figure 11-4 Composite Range-Hash Partitioning


Text description of the illustration cncpt157.gif


Composite Partitioning Range-List Example

CREATE TABLE bimonthly_regional_sales
(deptno NUMBER, 
 item_no VARCHAR2(20),
 txn_date DATE, 
 txn_amount NUMBER, 
 state VARCHAR2(2))
PARTITION BY RANGE (txn_date)
SUBPARTITION BY LIST (state)
SUBPARTITION TEMPLATE(


SUBPARTITION east VALUES('NY', 'VA', 'FL') TABLESPACE ts1,
SUBPARTITION west VALUES('CA', 'OR', 'HI') TABLESPACE ts2,
SUBPARTITION central VALUES('IL', 'TX', 'MO') TABLESPACE ts3)
( PARTITION janfeb_2000 VALUES LESS THAN (TO_DATE('1-MAR-2000','DD-MON-YYYY')), PARTITION marapr_2000 VALUES LESS THAN (TO_DATE('1-MAY-2000','DD-MON-YYYY')), PARTITION mayjun_2000 VALUES LESS THAN (TO_DATE('1-JUL-2000','DD-MON-YYYY')) );

This statement creates a table bimonthly_regional_sales that is range partitioned on the txn_date field and list subpartitioned on state. When you use a template, Oracle names the subpartitions by concatenating the partition name, an underscore, and the subpartition name from the template. Oracle places this subpartition in the tablespace specified in the template. In the previous statement, janfeb_2000_east is created and placed in tablespace ts1 while janfeb_2000_central is created and placed in tablespace ts3. In the same manner, mayjun_2000_east is placed in tablespace ts1 while mayjun_2000_central is placed in tablespace ts3. Figure 11-5 offers a graphical view of the table bimonthly_regional_sales and its 9 individual subpartitions.

Figure 11-5 Composite Range-List Partitioning


Text description of the illustration cncpt167.gif


When to Partition a Table

Here are some suggestions for when to partition a table:

  • Tables greater than 2GB should always be considered for partitioning.
  • Tables containing historical data, in which new data is added into the newest partition. A typical example is a historical table where only the current month's data is updatable and the other 11 months are read-only.

Partitioned Indexes

Just like partitioned tables, partitioned indexes improve manageability, availability, performance, and scalability. They can either be partitioned independently (global indexes) or automatically linked to a table's partitioning method (local indexes).

See Also:

Oracle9i Data Warehousing Guide for more information about partitioned indexes

Local Partitioned Indexes

Local partitioned indexes are easier to manage than other types of partitioned indexes. They also offer greater availability and are common in DSS environments. The reason for this is equipartitioning: each partition of a local index is associated with exactly one partition of the table. This enables Oracle to automatically keep the index partitions in sync with the table partitions, and makes each table-index pair independent. Any actions that make one partition's data invalid or unavailable only affect a single partition.

You cannot explicitly add a partition to a local index. Instead, new partitions are added to local indexes only when you add a partition to the underlying table. Likewise, you cannot explicitly drop a partition from a local index. Instead, local index partitions are dropped only when you drop a partition from the underlying table.

A local index can be unique. However, in order for a local index to be unique, the partitioning key of the table must be part of the index's key columns. Unique local indexes are useful for OLTP environments.

Figure 11-6 offers a graphical view of local partitioned indexes.

Figure 11-6 Local Partitioned Index


Text description of the illustration cncpt161.gif


Global Partitioned Indexes

Global partitioned indexes are flexible in that the degree of partitioning and the partitioning key are independent from the table's partitioning method. They are commonly used for OLTP environments and offer efficient access to any individual record.

The highest partition of a global index must have a partition bound, all of whose values are MAXVALUE. This ensures that all rows in the underlying table can be represented in the index. Global prefixed indexes can be unique or nonunique.

You cannot add a partition to a global index because the highest partition always has a partition bound of MAXVALUE. If you wish to add a new highest partition, use the ALTER INDEX SPLIT PARTITION statement. If a global index partition is empty, you can explicitly drop it by issuing the ALTER INDEX DROP PARTITION statement. If a global index partition contains data, dropping the partition causes the next highest partition to be marked unusable. You cannot drop the highest partition in a global index.

Maintenance of Global Partitioned Indexes

By default, the following operations on partitions on a heap-organized table mark all global indexes as unusable:

ADD (HASH) 
COALESCE (HASH) 
DROP 
EXCHANGE 
MERGE 
MOVE 
SPLIT 
TRUNCATE 

These indexes can be maintained by appending the clause UPDATE GLOBAL INDEXES to the SQL statements for the operation. The two advantages to maintaining global indexes:

  • The index remains available and online throughout the operation. Hence no other applications are affected by this operation.
  • The index doesn't have to be rebuilt after the operation.
Example:
ALTER TABLE DROP PARTITION P1 UPDATE GLOBAL INDEXES


Note:

This feature is supported only for heap organized tables.


See Also:

Oracle9i SQL Reference for more information about the UPDATE GLOBAL INDEX clause

Figure 11-7 offers a graphical view of global partitioned indexes.

Figure 11-7 Global Partitioned Index


Text description of the illustration cncpt160.gif


Global Nonpartitioned Indexes

Global nonpartitioned indexes behave just like a nonpartitioned index. They are commonly used in OLTP environments and offer efficient access to any individual record.

Figure 11-8 offers a graphical view of global nonpartitioned indexes.

Figure 11-8 Global Nonpartitioned Index


Text description of the illustration cncpt159.gif


Partitioned Index Examples

Example of Index Creation: Starting Table Used for Examples

CREATE TABLE employees
(employee_id NUMBER(4) NOT NULL,
 last_name VARCHAR2(10), 
 department_id NUMBER(2))
PARTITION BY RANGE (department_id)
(PARTITION employees_part1 VALUES LESS THAN (11) TABLESPACE part1, 
 PARTITION employees_part2 VALUES LESS THAN (21) TABLESPACE part2, 
 PARTITION employees_part3 VALUES LESS THAN (31) TABLESPACE part3);

Example of a Local Index Creation

CREATE INDEX employees_local_idx ON employees (employee_id) LOCAL;

Example of a Global Index Creation

CREATE INDEX employees_global_idx ON employees(employee_id);

Example of a Global Partitioned Index Creation

CREATE INDEX employees_global_part_idx ON employees(employee_id)
GLOBAL PARTITION BY RANGE(employee_id)
(PARTITION p1 VALUES LESS THAN(5000),
 PARTITION p2 VALUES LESS THAN(MAXVALUE));

Example of a Partitioned Index-Organized Table Creation

CREATE TABLE sales_range
(
salesman_id   NUMBER(5), 
salesman_name VARCHAR2(30), 
sales_amount  NUMBER(10), 
sales_date    DATE, 
PRIMARY KEY(sales_date, salesman_id)) 
ORGANIZATION INDEX INCLUDING salesman_id 
OVERFLOW TABLESPACE tabsp_overflow 
PARTITION BY RANGE(sales_date)
(PARTITION sales_jan2000 VALUES LESS THAN(TO_DATE('02/01/2000','DD/MM/YYYY'))
 OVERFLOW TABLESPACE p1_overflow, 
 PARTITION sales_feb2000 VALUES LESS THAN(TO_DATE('03/01/2000','DD/MM/YYYY'))
 OVERFLOW TABLESPACE p2_overflow, 
 PARTITION sales_mar2000 VALUES LESS THAN(TO_DATE('04/01/2000','DD/MM/YYYY'))
 OVERFLOW TABLESPACE p3_overflow, 
 PARTITION sales_apr2000 VALUES LESS THAN(TO_DATE('05/01/2000','DD/MM/YYYY'))
 OVERFLOW TABLESPACE p4_overflow);

Miscellaneous Information about Creating Indexes on Partitioned Tables

You can create bitmap indexes on partitioned tables, with the restriction that the bitmap indexes must be local to the partitioned table. They cannot be global indexes.

Global indexes can be unique. Local indexes can only be unique if the partitioning key is a part of the index key.

Using Partitioned Indexes in OLTP Applications

Here are a few guidelines for OLTP applications:

  • Global indexes and unique, local indexes provide better performance than nonunique local indexes because they minimize the number of index partition probes.
  • Local indexes offer better availability when there are partition or subpartition maintenance operations on the table.

Using Partitioned Indexes in Data Warehousing and DSS Applications

Here are a few guidelines for data warehousing and DSS applications:

  • Local indexes are preferable because they are easier to manage during data loads and during partition-maintenance operations.
  • Local indexes can improve performance because many index partitions can be scanned in parallel by range queries on the index key.

Partitioned Indexes on Composite Partitions

Here are a few points to remember when using partitioned indexes on composite partitions:

  • Only range partitioned global indexes are supported.
  • Subpartitioned indexes are always local and stored with the table subpartition by default.
  • Tablespaces can be specified at either index or index subpartition levels.

Partitioning to Improve Performance

Partitioning can help you improve performance and manageability. Some topics to keep in mind when using partitioning for these reasons are:

Partition Pruning

The Oracle server explicitly recognizes partitions and subpartitions. It then optimizes SQL statements to mark the partitions or subpartitions that need to be accessed and eliminates (prunes) unnecessary partitions or subpartitions from access by those SQL statements. In other words, partition pruning is the skipping of unnecessary index and data partitions or subpartitions in a query.

For each SQL statement, depending on the selection criteria specified, unneeded partitions or subpartitions can be eliminated. For example, if a query only involves March sales data, then there is no need to retrieve data for the remaining eleven months. Such intelligent pruning can dramatically reduce the data volume, resulting in substantial improvements in query performance.

If the optimizer determines that the selection criteria used for pruning are satisfied by all the rows in the accessed partition or subpartition, it removes those criteria from the predicate list (WHERE clause) during evaluation in order to improve performance. However, the optimizer cannot prune partitions if the SQL statement applies a function to the partitioning column (with the exception of the TO_DATE function). Similarly, the optimizer cannot use an index if the SQL statement applies a function to the indexed column, unless it is a function-based index.

Pruning can eliminate index partitions even when the underlying table's partitions cannot be eliminated, but only when the index and table are partitioned on different columns. You can often improve the performance of operations on large tables by creating partitioned indexes that reduce the amount of data that your SQL statements need to access or modify.

Equality, range, LIKE, and IN-list predicates are considered for partition pruning with range or list partitioning, and equality and IN-list predicates are considered for partition pruning with hash partitioning.

Partition Pruning Example

We have a partitioned table called orders. The partition key for orders is order_date. Let's assume that orders has six months of data, January to June, with a partition for each month of data. If the following query is run:

SELECT SUM(value) 
FROM orders 
WHERE order_date BETWEEN '28-MAR-98' AND '23-APR-98'

Partition pruning is achieved by:

  • First, partition elimination of January, February, May, and June data partitions. Then either:
    • An index scan of the March and April data partition due to high index selectivity

      or

    • A full scan of the March and April data partition due to low index selectivity

Partition-wise Joins

A partition-wise join is a join optimization that you can use when joining two tables that are both partitioned along the join column(s). With partition-wise joins, the join operation is broken into smaller joins that are performed sequentially or in parallel. Another way of looking at partition-wise joins is that they minimize the amount of data exchanged among parallel slaves during the execution of parallel joins by taking into account data distribution.

See Also:

Oracle9i Data Warehousing Guide for more information about partitioning methods and partition-wise joins

Parallel DML

Parallel execution dramatically reduces response time for data-intensive operations on large databases typically associated with decision support systems and data warehouses. In addition to conventional tables, you can use parallel query and parallel DML with range- and hash-partitioned tables. By doing so, you can enhance scalability and performance for batch operations.

The semantics and restrictions for parallel DML sessions are the same whether you are using index-organized tables or not.

See Also:

Oracle9i Data Warehousing Guide for more information about parallel DML and its use with partitioned tables