miércoles, 29 de febrero de 2012

Moviendo tablas e índices en Oracle

Si necesitas mover algunas tablas o índices a otro tablespace para reorganizar el almacenamiento de tu base de datos, o solo quieres reconstruírlos para compactarlos y recuperar espacio, puedes usar sentencias como estas:

SQL> alter table SCOTT.DEPT move tablespace users;

Table altered.

SQL> alter index SCOTT.PK_DEPT rebuild tablespace users;

Index altered.

Puedes mover tablas e índices con la base de datos abierta, pero estos objetos estarán bloqueados mientras sean movidos por lo que esto podría ser un problema con grandes objetos muy usados. Además, después de mover una tabla tienes que reconstruir todos sus índices ya que se vuelven inválidos después del movimiento de la tabla.

También puedes mover tablas e índices particionados pero tienes que mover las particiones (o subparticiones) ya que las tablas e índices particionados no son colocados en ningún tablespace:

SQL> select distinct TABLE_NAME from DBA_TAB_PARTITIONS where TABLE_OWNER='SH';

TABLE_NAME
------------------------------------------------------------------------------------------
COSTS
SALES

SQL> select distinct SUBPARTITION_COUNT from DBA_TAB_PARTITIONS where TABLE_OWNER='SH';

SUBPARTITION_COUNT
------------------
0

SQL> column TABLE_OWNER format a30
SQL> column TABLE_NAME format a30
SQL> select TABLE_OWNER, TABLE_NAME, PARTITION_NAME from DBA_TAB_PARTITIONS where
TABLE_OWNER='SH' and TABLE_NAME='SALES' order by TABLE_OWNER, TABLE_NAME, PARTITION_POSITION;

TABLE_OWNER TABLE_NAME PARTITION_NAME
------------------------------ ------------------------------ ------------------------------
SH SALES SALES_1995
SH SALES SALES_1996
SH SALES SALES_H1_1997
SH SALES SALES_H2_1997

...

SQL> alter table SH.SALES move partition SALES_1995 tablespace example;

Table altered.

SQL> select distinct INDEX_NAME from DBA_IND_PARTITIONS where INDEX_OWNER='SH';

INDEX_NAME
------------------------------------------------------------------------------------------
SALES_CHANNEL_BIX
SALES_CUST_BIX
SALES_PROMO_BIX
SALES_PROD_BIX
COSTS_PROD_BIX
SALES_TIME_BIX
COSTS_TIME_BIX

7 rows selected.

SQL> select distinct SUBPARTITION_COUNT from DBA_IND_PARTITIONS where INDEX_OWNER='SH';

SUBPARTITION_COUNT
------------------
0

SQL> select INDEX_OWNER, INDEX_NAME, PARTITION_NAME from DBA_IND_PARTITIONS where
INDEX_OWNER='SH' and INDEX_NAME like 'SALES%'
order by INDEX_OWNER, INDEX_NAME, PARTITION_POSITION;

INDEX_OWNER INDEX_NAME PARTITION_NAME
------------------------------ ------------------------------ ------------------------------
SH SALES_CHANNEL_BIX SALES_1995
SH SALES_CHANNEL_BIX SALES_1996
SH SALES_CHANNEL_BIX SALES_H1_1997
SH SALES_CHANNEL_BIX SALES_H2_1997

...

SQL> alter index SH.SALES_CHANNEL_BIX rebuild partition SALES_1995 tablespace example;

Index altered.

Dado que no puedes mover un esquema completo (o incluso solo una tabla o índice particionado) con sólo una sentencia SQL, puedes generar las sentencias SQL necesarias para mover objetos de esta manera:

SQL> select 'alter table '||OWNER||'.'||TABLE_NAME||' move tablespace example;'
from dba_tables where TABLESPACE_NAME='USERS' order by OWNER, TABLE_NAME;

'ALTERTABLE'||OWNER||'.'||TABLE_NAME||'MOVETABLESPACEEXAMPLE;'
------------------------------------------------------------------------------------
alter table SCOTT.BONUS move tablespace example;
alter table SCOTT.DEPT move tablespace example;
alter table SCOTT.EMP move tablespace example;
alter table SCOTT.SALGRADE move tablespace example;

4 rows selected.

SQL> select 'alter index '||OWNER||'.'||INDEX_NAME||' rebuild tablespace example;'
from dba_indexes where TABLESPACE_NAME='USERS' order by OWNER, INDEX_NAME;

'ALTERINDEX'||OWNER||'.'||INDEX_NAME||'REBUILDTABLESPACEEXAMPLE;'
------------------------------------------------------------------------------------
alter index SCOTT.PK_DEPT rebuild tablespace example;
alter index SCOTT.PK_EMP rebuild tablespace example;

2 rows selected.

SQL> select 'alter table '||TABLE_OWNER||'.'||TABLE_NAME||' move partition '||PARTITION_NAME||' tablespace example;'
from DBA_TAB_PARTITIONS where TABLE_OWNER='SH' and TABLE_NAME='SALES'
order by TABLE_OWNER, TABLE_NAME, PARTITION_POSITION;

'ALTERTABLE'||TABLE_OWNER||'.'||TABLE_NAME||'MOVEPARTITION'||PARTITION_NAME||'TABLESPACEEXAMPLE;'
------------------------------------------------------------------------------------
alter table SH.SALES move partition SALES_1995 tablespace example;
alter table SH.SALES move partition SALES_1996 tablespace example;
alter table SH.SALES move partition SALES_H1_1997 tablespace example;
alter table SH.SALES move partition SALES_H2_1997 tablespace example;

...

SQL> select 'alter index '||INDEX_OWNER||'.'||INDEX_NAME||' rebuild partition '||PARTITION_NAME||' tablespace example;'
from DBA_IND_PARTITIONS where INDEX_OWNER='SH' and INDEX_NAME like 'SALES%'
order by INDEX_OWNER, INDEX_NAME, PARTITION_POSITION;

'ALTERINDEX'||INDEX_OWNER||'.'||INDEX_NAME||'REBUILDPARTITION'||PARTITION_NAME||'TABLESPACEEXAMPLE;'
-------------------------------------------------------------------------------------------------
alter index SH.SALES_CHANNEL_BIX rebuild partition SALES_1995 tablespace example;
alter index SH.SALES_CHANNEL_BIX rebuild partition SALES_1996 tablespace example;
alter index SH.SALES_CHANNEL_BIX rebuild partition SALES_H1_1997 tablespace example;
alter index SH.SALES_CHANNEL_BIX rebuild partition SALES_H2_1997 tablespace example;

...

Y si necesitas saber información sobre tablespaces, índices y datafiles puedes obtenerla así:

SQL> select i.TABLE_OWNER, t.TABLE_NAME, i.OWNER as INDEX_OWNER, i.INDEX_NAME, i.TABLESPACE_NAME from dba_tables t, dba_indexes i
where t.OWNER=i.TABLE_OWNER and t.TABLE_NAME=i.TABLE_NAME and t.TABLESPACE_NAME in ('USERS')
order by i.TABLE_OWNER, t.TABLE_NAME, i.OWNER, i.INDEX_NAME;

TABLE_OWNER TABLE_NAME INDEX_OWNER INDEX_NAME TABLESPACE_NAME
------------- ------------- -------------- ------------- ------------------
SCOTT DEPT SCOTT PK_DEPT USERS
SCOTT EMP SCOTT PK_EMP USERS

SQL> select i.TABLE_OWNER, t.TABLE_NAME, i.OWNER as INDEX_OWNER, i.INDEX_NAME, i.TABLESPACE_NAME from dba_tables t, dba_indexes i
where t.OWNER=i.TABLE_OWNER and t.TABLE_NAME=i.TABLE_NAME and t.TABLESPACE_NAME in ('USERS') and i.TABLESPACE_NAME not in ('USERS')
order by i.TABLE_OWNER, t.TABLE_NAME, i.OWNER, i.INDEX_NAME;

no rows selected

SQL> select i.OWNER as INDEX_OWNER, i.INDEX_NAME, i.TABLE_OWNER, t.TABLE_NAME, t.TABLESPACE_NAME from dba_tables t, dba_indexes i
where t.OWNER=i.TABLE_OWNER and t.TABLE_NAME=i.TABLE_NAME and i.TABLESPACE_NAME in ('USERS')
order by i.OWNER, i.INDEX_NAME, i.TABLE_OWNER, t.TABLE_NAME;

INDEX_OWNER INDEX_NAME TABLE_OWNER TABLE_NAME TABLESPACE_NAME
------------------------------ ------------------------------ ------------------------------ ------------------------------ ------------------------------
SCOTT PK_DEPT SCOTT DEPT USERS
SCOTT PK_EMP SCOTT EMP USERS

SQL> select i.OWNER as INDEX_OWNER, i.INDEX_NAME, i.TABLE_OWNER, t.TABLE_NAME, t.TABLESPACE_NAME from dba_tables t, dba_indexes i
where t.OWNER=i.TABLE_OWNER and t.TABLE_NAME=i.TABLE_NAME and i.TABLESPACE_NAME in ('USERS') and t.TABLESPACE_NAME not in ('USERS')
order by i.OWNER, i.INDEX_NAME, i.TABLE_OWNER, t.TABLE_NAME;

no rows selected

SQL> select i.TABLE_OWNER, t.TABLE_NAME, pt.TABLESPACE_NAME, i.OWNER as INDEX_OWNER, i.INDEX_NAME, pi.PARTITION_NAME, pi.TABLESPACE_NAME
from dba_tables t, dba_indexes i, DBA_IND_PARTITIONS pi, (select distinct TABLE_OWNER, TABLE_NAME, TABLESPACE_NAME from DBA_TAB_PARTITIONS) pt
where t.OWNER=i.TABLE_OWNER and t.TABLE_NAME=i.TABLE_NAME and i.OWNER=pi.INDEX_OWNER and i.INDEX_NAME=pi.INDEX_NAME and t.OWNER=pt.TABLE_OWNER and t.TABLE_NAME=pt.TABLE_NAME and pt.TABLESPACE_NAME in ('EXAMPLE') and t.OWNER='SH' and t.TABLE_NAME='SALES'
order by i.TABLE_OWNER, t.TABLE_NAME, i.OWNER, i.INDEX_NAME, pi.PARTITION_POSITION;

TABLE_OWNER TABLE_NAME TABLESPACE_NAME INDEX_OWNER INDEX_NAME PARTITION_NAME TABLESPACE_NAME
------------ ------------- ----------------- ------------- -------------------- ----------------- -----------------
SH SALES EXAMPLE SH SALES_CHANNEL_BIX SALES_1995 EXAMPLE
SH SALES EXAMPLE SH SALES_CHANNEL_BIX SALES_1996 EXAMPLE
SH SALES EXAMPLE SH SALES_CHANNEL_BIX SALES_H1_1997 EXAMPLE
SH SALES EXAMPLE SH SALES_CHANNEL_BIX SALES_H2_1997 EXAMPLE

...

SQL> select i.TABLE_OWNER, t.TABLE_NAME, pt.TABLESPACE_NAME, i.OWNER as INDEX_OWNER, i.INDEX_NAME, pi.PARTITION_NAME, pi.TABLESPACE_NAME
from dba_tables t, dba_indexes i, DBA_IND_PARTITIONS pi, (select distinct TABLE_OWNER, TABLE_NAME, TABLESPACE_NAME from DBA_TAB_PARTITIONS) pt
where t.OWNER=i.TABLE_OWNER and t.TABLE_NAME=i.TABLE_NAME and i.OWNER=pi.INDEX_OWNER and i.INDEX_NAME=pi.INDEX_NAME and t.OWNER=pt.TABLE_OWNER and t.TABLE_NAME=pt.TABLE_NAME and pt.TABLESPACE_NAME in ('EXAMPLE') and pi.TABLESPACE_NAME not in ('EXAMPLE') and t.OWNER='SH' and t.TABLE_NAME='SALES'
order by i.TABLE_OWNER, t.TABLE_NAME, i.OWNER, i.INDEX_NAME, pi.PARTITION_POSITION;

no rows selected

SQL> select i.OWNER as INDEX_OWNER, i.INDEX_NAME, pi.TABLESPACE_NAME, i.TABLE_OWNER, t.TABLE_NAME, pt.PARTITION_NAME, pt.TABLESPACE_NAME
from dba_tables t, dba_indexes i, (select distinct INDEX_OWNER, INDEX_NAME, TABLESPACE_NAME from DBA_IND_PARTITIONS) pi, DBA_TAB_PARTITIONS pt
where t.OWNER=i.TABLE_OWNER and t.TABLE_NAME=i.TABLE_NAME and i.OWNER=pi.INDEX_OWNER and i.INDEX_NAME=pi.INDEX_NAME and t.OWNER=pt.TABLE_OWNER and t.TABLE_NAME=pt.TABLE_NAME and pi.TABLESPACE_NAME in ('EXAMPLE') and t.OWNER='SH' and t.TABLE_NAME='SALES'
order by i.OWNER, i.INDEX_NAME, i.TABLE_OWNER, t.TABLE_NAME, pt.PARTITION_POSITION;

INDEX_OWNER INDEX_NAME TABLESPACE_NAME TABLE_OWNER TABLE_NAME PARTITION_NAME TABLESPACE_NAME
------------ -------------------- ----------------- ------------- ------------- ----------------- -----------------
SH SALES_CHANNEL_BIX EXAMPLE SH SALES SALES_1995 EXAMPLE
SH SALES_CHANNEL_BIX EXAMPLE SH SALES SALES_1996 EXAMPLE
SH SALES_CHANNEL_BIX EXAMPLE SH SALES SALES_H1_1997 EXAMPLE
SH SALES_CHANNEL_BIX EXAMPLE SH SALES SALES_H2_1997 EXAMPLE

...

SQL> select i.OWNER as INDEX_OWNER, i.INDEX_NAME, pi.TABLESPACE_NAME, i.TABLE_OWNER, t.TABLE_NAME, pt.PARTITION_NAME, pt.TABLESPACE_NAME
from dba_tables t, dba_indexes i, (select distinct INDEX_OWNER, INDEX_NAME, TABLESPACE_NAME from DBA_IND_PARTITIONS) pi, DBA_TAB_PARTITIONS pt
where t.OWNER=i.TABLE_OWNER and t.TABLE_NAME=i.TABLE_NAME and i.OWNER=pi.INDEX_OWNER and i.INDEX_NAME=pi.INDEX_NAME and t.OWNER=pt.TABLE_OWNER and t.TABLE_NAME=pt.TABLE_NAME and pi.TABLESPACE_NAME in ('EXAMPLE') and pt.TABLESPACE_NAME not in ('EXAMPLE') and t.OWNER='SH' and t.TABLE_NAME='SALES'
order by i.OWNER, i.INDEX_NAME, i.TABLE_OWNER, t.TABLE_NAME, pt.PARTITION_POSITION;

no rows selected

SQL> select d.TABLESPACE_NAME, d.TMBYTES, e.UMBYTES, round(e.UMBYTES/d.TMBYTES*100) as PERCENTAGE from
(select TABLESPACE_NAME, round(sum(BYTES)/1024/1024) as TMBYTES
from dba_data_files group by TABLESPACE_NAME) d, (select TABLESPACE_NAME, round(sum(BYTES)/1024/1024) as UMBYTES from dba_extents group by TABLESPACE_NAME) e
where d.TABLESPACE_NAME=e.TABLESPACE_NAME order by d.TABLESPACE_NAME;

TABLESPACE_NAME TMBYTES UMBYTES PERCENTAGE
------------------------------ ---------- ---------- ----------
EXAMPLE 100 68 68
SYSAUX 240 232 97
SYSTEM 480 473 99
UNDOTBS1 30 3 10
USERS 5 3 60

SQL> select OWNER, TABLESPACE_NAME, round(sum(BYTES)/1024/1024) as MBYTES from dba_segments group by OWNER, TABLESPACE_NAME order by OWNER, TABLESPACE_NAME;

OWNER TABLESPACE_NAME MBYTES
--------------- ------------------------------ ----------
CTXSYS SYSAUX 5
DBSNMP SYSAUX 2
DMSYS SYSAUX 0
EXFSYS SYSAUX 4
HR EXAMPLE 2
IX EXAMPLE 2
MDSYS SYSAUX 32
OE EXAMPLE 6
OE USERS 3
OLAPSYS SYSAUX 16
ORDSYS SYSAUX 1
OUTLN SYSTEM 1
PM EXAMPLE 3
SCOTT USERS 0
SH EXAMPLE 56
SYS SYSAUX 61
SYS SYSTEM 457
SYS UNDOTBS1 3
SYSMAN SYSAUX 50
SYSTEM SYSAUX 7
SYSTEM SYSTEM 15
TSMSYS SYSAUX 0
WMSYS SYSAUX 7
XDB SYSAUX 49

24 rows selected.

martes, 28 de febrero de 2012

Obteniendo el DDL de un tablespace

Si quieres copiar la estructura de un tablespace y no tienes un sapo a la mano, entonces puedes usar el paquete DBMS_METADATA para obtener el DDL que necesitas para recrearlo:

SQL> exec dbms_metadata.set_transform_param(dbms_metadata.session_transform, 'SQLTERMINATOR',true);

PL/SQL procedure successfully completed.

SQL> set linesize 150
SQL> SET PAGESIZE 0
SQL> SET LONG 90000
SQL> SELECT DBMS_METADATA.GET_DDL('TABLESPACE',TABLESPACE_NAME) from dba_tablespaces order by TABLESPACE_NAME;

CREATE TABLESPACE "MYTABLESPACE" DATAFILE
'/mydb/mytablespace.dbf' SIZE 5242880
LOGGING ONLINE PERMANENT BLOCKSIZE 8192
EXTENT MANAGEMENT LOCAL AUTOALLOCATE SEGMENT SPACE MANAGEMENT AUTO;


CREATE TABLESPACE "PERFSTAT" DATAFILE
'/mydb/perfstat.dbf' SIZE 201326592
AUTOEXTEND ON NEXT 16777216 MAXSIZE 402653184
NOLOGGING ONLINE PERMANENT BLOCKSIZE 8192
EXTENT MANAGEMENT LOCAL AUTOALLOCATE SEGMENT SPACE MANAGEMENT AUTO;


CREATE TABLESPACE "SYSAUX" DATAFILE
'/mydb/sysaux.dbf' SIZE 419430400
LOGGING ONLINE PERMANENT BLOCKSIZE 8192
EXTENT MANAGEMENT LOCAL AUTOALLOCATE SEGMENT SPACE MANAGEMENT AUTO;


CREATE TABLESPACE "SYSTEM" DATAFILE
'/mydb/system.dbf' SIZE 419430400
LOGGING ONLINE PERMANENT BLOCKSIZE 8192
EXTENT MANAGEMENT DICTIONARY DEFAULT NOCOMPRESS
STORAGE(INITIAL 16384 NEXT 16384 MINEXTENTS 1 MAXEXTENTS 505
PCTINCREASE 50);


CREATE TEMPORARY TABLESPACE "TEMP" TEMPFILE
'/mydb/temp.dbf' SIZE 10737418240
EXTENT MANAGEMENT LOCAL UNIFORM SIZE 1048576;


CREATE UNDO TABLESPACE "UNDO" DATAFILE
'/mydb/undo.dbf' SIZE 838860800
BLOCKSIZE 8192
EXTENT MANAGEMENT LOCAL AUTOALLOCATE;
ALTER DATABASE DATAFILE
'/mydb/undo.dbf' RESIZE 5368709120;


CREATE TABLESPACE "USERS" DATAFILE
'/mydb/users.dbf' SIZE 536870912
AUTOEXTEND ON NEXT 1048576 MAXSIZE 2048M
LOGGING ONLINE PERMANENT BLOCKSIZE 8192
EXTENT MANAGEMENT LOCAL AUTOALLOCATE SEGMENT SPACE MANAGEMENT AUTO;


7 rows selected.

Como habrás notado con el tablespace UNDO primero aparece la sentencia CREATE TABLESPACE original, y si hubo alguna modificación al tablespace esas sentencias vienen después.

Más información:

How to extract DDL for all Tablespaces

lunes, 27 de febrero de 2012

Verificando el numero máximo de sesiones

Si quieres saber si tu instancia de Oracle puede soportar cierta cantidad de usuarios concurrentes, entonces tienes que verificar los parámetros sessions y processes:

SQL> set linesize 150
SQL> select count(*) from v$session;

COUNT(*)
----------
87

SQL> column name format a30
SQL> column value format a30
SQL> select name, value from v$parameter where NAME in ('sessions','processes');

NAME VALUE
------------------------------ ------------------------------
processes 600
sessions 665

El parámetro sessions es derivado por default del parámetro processes, pero podría ser mejor revisar y configurar ambos parámetros processes y sessions. Si configuraste servidores compartidos para tu instancia, puedes tener muchas sesiones con pocos procesos.

Pero cuántos procesos puedes tener depende de los parámetros de sistema operativo, por lo tanto sería bueno revisar esos parámetros:

myserver> lsattr -El sys0 -a maxuproc
maxuproc 2048 Maximum number of PROCESSES allowed per user True
myserver> id oracle
uid=214(oracle) gid=203(dba) groups=1(staff)
myserver> ps axl | grep " 214" | wc -l
409
myserver> ulimit -a
time(seconds) unlimited
file(blocks) unlimited
data(kbytes) unlimited
stack(kbytes) 32768
memory(kbytes) 32768
coredump(blocks) 5097151
nofiles(descriptors) unlimited

Para este ejemplo revisamos el numero máximo de procesos por usuario en AIX, cuántos procesos estaban corriendo en ese momento, y sólo para estar seguros los límites del usuario oracle. Si tienes más de una instancia por usuario, entonces tienes que tener en cuenta los parámetros máximos de sesiones de todas las demás instancias para estar seguro de que los límites del sistema operativo no afectarán tus instancias.

Más información:

SESSIONS
PROCESSES
HPUX: Kernel Tunable parameters
Getting AIX basic system info

sábado, 25 de febrero de 2012

Aplicando un delta en Progress

Si quieres aplicar un delta a una base de datos Progress, esto es, un archivo que contiene información para actualizar objetos de una base de datos Progress para hacerlos iguales a los objetos de otra base de datos Progress (como de una base de datos de desarrollo a una base de datos de producción), entonces puedes hacerlo con la utilería mpro:

RHPRGPR1:/mnt0/new:> mpro /mydatabase/mydb.db









@@@@@@ @@@@@@ @@@@@@@ @@@@@ @@@@@@ @@@@@@@ @@@@@ @@@@@
@ @ @ @ @ @ @ @ @ @ @ @ @ @ @
@ @ @ @ @ @ @ @ @ @ @ @
@@@@@@ @@@@@@ @ @ @ @@@@ @@@@@@ @@@@@ @@@@@ @@@@@
@ @ @ @ @ @ @ @ @ @ @ @
@ @ @ @ @ @ @ @ @ @ @ @ @ @
@ @ @ @@@@@@@ @@@@@ @ @ @@@@@@@ @@@@@ @@@@@

Progress Software Corporation
14 Oak Park
Bedford, Massachusetts 01730
781-280-4000

PROGRESS is a registered trademark of Progress Software Corporation
Copyright 1984-2004
by Progress Software Corporation
All Rights Reserved

Después de esta pantalla te pedirán un usuario y contraseña; tienes que ingresar con un usuario con suficientes privilegios:

PROGRESS Version 9.1E as of Tue Oct 12 14:40:19 EDT 2004

┌─────────────────── Login ───────────────────┐
│ │
│ Please enter a User Id and Password for │
│ database: mydb │
│ │
│ User Id: DBAUSER │
│ Password: │
│ │
│ (OK) (Cancel) │
└─────────────────────────────────────────────┘












Enter data or press F4 to end.

Entonces en la siguiente pantalla tienes que presionar F3 y seleccionar el menú Tools, opción Data Dictionary:

PROGRESS Version 9.1E as of Tue Oct 12 14:40:19 EDT 2004
File Edit Search Buffer Compile Tools Help
────────────────────────────────┌──────────────────────┐────────────────────────
│ Data Dictionary │
│ OS Shell │
│ Application Compiler │
└──────────────────────┘














─ File: Untitled:1 ─────────────────────────────────────────────────────────────


F1=RUN F3=MENUS F5=OPEN F6=SAVE F8=CLOSE Insert

La siguiente pantalla será la de la herramienta Data Dictionary:

Database Schema Admin DataServer Utilities PRO/SQL Tools





┌──────────── Welcome to the PROGRESS Data Dictionary ─────────────┐
│ │
│ You may use these facilities to set up or alter the structure of │
│ your database(s) or to perform various administrative functions. │
│ │
└──────────────────────────────────────────────────────────────────┘







Main Menu Data Dictionary
Database: mydb (PROGRESS) Table:

En la pantalla de Data Dictionary tienes que seleccionar el menú Admin, submenú Load Data and Definitions, opción Data Definitions (.df file):

Database Schema Admin DataServer Utilities PRO/SQL Tools
┌────────────────────────────────────────┐
│ Dump Data and Definitions -> │
│ Load Data and Definitions -> │
│ ┌─────────────────────────────────┐ │
│ │ Data Definitions (.df file)... │ │
┌─────────│ │ Table Contents (.d file)... │ │y ─────────────┐
│ │ │ SQL Views... │... │ │
│ You may └─│ User Table Contents... │────┘e structure of │
│ your datab│ Sequences Current Values... │strative functions. │
│ │ Reconstruct Bad Load Records... │ │
└───────────└─────────────────────────────────┘────────────────────┘







Main Menu Data Dictionary
Database: mydb (PROGRESS) Table:

Para aplicar un archivo delta la base de datos no debe estar bloqueada por ningún usuario, pero si es así obtendrás un mensaje de error como el siguiente:

Database Schema Admin DataServer Utilities PRO/SQL Tools


















Data Definitions (.df file) Data Dictionary
Database: mydb (PROGRESS) Table:


Database in use by SOMEUSER on w1c3xpr3. Wait or press CTRL-C to stop. (388)

Si tienes que aplicar este delta y puedes de manera segura reiniciar la base de datos o desconectar los usuarios que están bloqueando la base de datos entonces hazlo, de preferencia en otra terminal; si no hay usuarios bloqueando la base de datos entonces obtendrás una pantalla donde tienes que seleccionar el archivo del delta y muchas otras opciones que puedes mantener sin cambios.

Finalmente después de seleccionar OK, si nuevamente no hay usuarios bloqueando la base de datos el delta será aplicado bastante rápido y regresarás al menú Data Dictionary.

miércoles, 22 de febrero de 2012

Obteniendo el DDL de un perfil

Si quieres copiar un perfil y no tienes un sapo a la mano, puedes usar el paquete DBMS_METADATA para obtener el DDL que necesitas para recrearlo:

SQL> exec dbms_metadata.set_transform_param(dbms_metadata.session_transform, 'SQLTERMINATOR',true);

PL/SQL procedure successfully completed.

SQL> SET LINESIZE 150
SQL> SET PAGESIZE 0
SQL> SET LONG 90000
SQL> SELECT DBMS_METADATA.GET_DDL('PROFILE',PROFILE) from dba_profiles
where PROFILE='MYPROFILE' group by PROFILE;

CREATE PROFILE "MYPROFILE"
LIMIT
COMPOSITE_LIMIT DEFAULT
SESSIONS_PER_USER DEFAULT
CPU_PER_SESSION DEFAULT
CPU_PER_CALL DEFAULT
LOGICAL_READS_PER_SESSION DEFAULT
LOGICAL_READS_PER_CALL DEFAULT
IDLE_TIME UNLIMITED
CONNECT_TIME DEFAULT
PRIVATE_SGA DEFAULT
FAILED_LOGIN_ATTEMPTS DEFAULT
PASSWORD_LIFE_TIME UNLIMITED
PASSWORD_REUSE_TIME DEFAULT
PASSWORD_REUSE_MAX DEFAULT
PASSWORD_VERIFY_FUNCTION "VERIFY_FUNCTION"
PASSWORD_LOCK_TIME DEFAULT
PASSWORD_GRACE_TIME DEFAULT ;

Como puedes ver hay una función de verificación con el propósito de revisar el password, por lo que primero tienes que crear la función VERIFY_FUNCTION para crear este perfil. Puedes obtener el código fuente de esta función consultando la vista dba_source:

SQL> select TEXT from dba_source where NAME='VERIFY_FUNCTION' and TYPE='FUNCTION' order by LINE;
FUNCTION verify_function
(username varchar2,
password varchar2,
old_password varchar2)
RETURN boolean IS
n boolean;
m integer;

...

IF differ < 3 THEN
raise_application_error(-20004, 'Password should differ by at \
least 3 characters');
END IF;
END IF;
END IF;
-- Everything is fine; return TRUE ;
RETURN(TRUE);
END;

113 rows selected.

Más información:

57 DBMS_METADATA

martes, 21 de febrero de 2012

Obteniendo el DDL de un rol

Si quieres copiar un rol y no tienes un sapo a la mano, puedes usar el paquete DBMS_METADATA para obtener el DDL que necesitas para recrearlo:

SQL> exec dbms_metadata.set_transform_param(dbms_metadata.session_transform, 'SQLTERMINATOR', true);

PL/SQL procedure successfully completed.

SQL> SET PAGESIZE 0
SQL> SET LONG 90000
SQL> SELECT DBMS_METADATA.GET_DDL('ROLE', role) || ';' FROM dba_roles WHERE ROLE='MYROLE';

CREATE ROLE "MYROLE";
;

SQL> SELECT DBMS_METADATA.GET_GRANTED_DDL('ROLE_GRANT',role) FROM ROLE_ROLE_PRIVS
WHERE ROLE='MYROLE' group by ROLE;

GRANT "CONNECT" TO "MYROLE";

GRANT "EXP_FULL_DATABASE" TO "MYROLE";


SQL> SELECT DBMS_METADATA.GET_GRANTED_DDL('SYSTEM_GRANT',role) FROM ROLE_SYS_PRIVS
WHERE ROLE='MYROLE' group by ROLE;

no rows selected

SQL> SELECT DBMS_METADATA.GET_GRANTED_DDL('OBJECT_GRANT',role) FROM ROLE_TAB_PRIVS
WHERE ROLE='MYROLE' group by ROLE;

no rows selected

Como puedes ver, hay un ; extra después de la sentencia CREATE ROLE que puedes ignorar sin problemas, y para este rol no hay derechos sobre el sistema u objetos.

Más información:

Oracle Roles
57 DBMS_METADATA

lunes, 20 de febrero de 2012

Obteniendo el DDL de un usuario

Si quieres copiar un usuario y no tienes un sapo, entonces puedes usar el paquete DBMS_METADATA para obtener el DDL que necesitas para recrearlo:

SQL> exec dbms_metadata.set_transform_param(dbms_metadata.session_transform, 'SQLTERMINATOR', true);

PL/SQL procedure successfully completed.

SQL> SET PAGESIZE 0
SQL> SET LONG 90000
SQL> SELECT DBMS_METADATA.GET_DDL('USER',username) from dba_users where username='MYUSER';

CREATE USER "MYUSER" IDENTIFIED BY VALUES '123456B563D86FE2'
DEFAULT TABLESPACE "MYTABLESPACE"
TEMPORARY TABLESPACE "TEMP"
PROFILE "MYPROFILE";


SQL> SELECT DBMS_METADATA.GET_GRANTED_DDL('ROLE_GRANT',GRANTEE) FROM DBA_ROLE_PRIVS
WHERE GRANTEE='MYUSER' group by GRANTEE;

GRANT "MYROLE" TO "MYUSER";


SQL> SELECT DBMS_METADATA.GET_GRANTED_DDL('DEFAULT_ROLE','MYUSER') from dual;

ALTER USER "MYUSER" DEFAULT ROLE ALL;


SQL> SELECT DBMS_METADATA.GET_GRANTED_DDL('SYSTEM_GRANT',GRANTEE) FROM DBA_SYS_PRIVS
WHERE GRANTEE='MYUSER' group by GRANTEE;

GRANT CREATE TABLE TO "MYUSER";


SQL> SELECT DBMS_METADATA.GET_GRANTED_DDL('OBJECT_GRANT',GRANTEE) FROM DBA_TAB_PRIVS
WHERE GRANTEE='MXS320A' group by GRANTEE;

no rows selected

SQL> SELECT DBMS_METADATA.GET_GRANTED_DDL('TABLESPACE_QUOTA','MYUSER') from dual;

DECLARE
TEMP_COUNT NUMBER;
SQLSTR VARCHAR2(200);
BEGIN
SQLSTR := 'ALTER USER "MYUSER" QUOTA UNLIMITED ON "MYTABLESPACE"';
EXECUTE IMMEDIATE SQLSTR;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE = -30041 THEN
SQLSTR := 'SELECT COUNT(*) FROM USER_TABLESPACES
WHERE TABLESPACE_NAME = ''MYTABLESPACE'' AND CONTENTS = ''TEMPORARY''';
EXECUTE IMMEDIATE SQLSTR INTO TEMP_COUNT;
IF TEMP_COUNT = 1 THEN RETURN;
ELSE RAISE;
END IF;
ELSE
RAISE;
END IF;
END;
/

DECLARE
TEMP_COUNT NUMBER;
SQLSTR VARCHAR2(200);
BEGIN
SQLSTR := 'ALTER USER "MYUSER" QUOTA 10485760 ON "SYSTEM"';
EXECUTE IMMEDIATE SQLSTR;
EXCEPTION
WHEN OTHERS THEN
IF SQLCODE = -30041 THEN
SQLSTR := 'SELECT COUNT(*) FROM USER_TABLESPACES
WHERE TABLESPACE_NAME = ''SYSTEM'' AND CONTENTS = ''TEMPORARY''';
EXECUTE IMMEDIATE SQLSTR INTO TEMP_COUNT;
IF TEMP_COUNT = 1 THEN RETURN;
ELSE RAISE;
END IF;
ELSE
RAISE;
END IF;
END;
/

Como puedes ver, no obtienes simples sentencias ALTER USER QUOTA con la opción de TABLESPACE_QUOTA por lo que puedes ejecutar el bloque PL/SQL completo o sólo tomar las sentencias ALTER USER QUOTA.

Más información:

Copying Oracle Users
57 DBMS_METADATA

jueves, 16 de febrero de 2012

Elaborando un mapa de uso de tablespace en Oracle


Digamos que necesitas reducir el tamaño de un datafile para recuperar algo de espacio (como en una urgencia), entonces después de revisar el tamaño y uso de un datafile ejecutas un ALTER DATABASE DATAFILE RESIZE sólo para obtener este error:

ORA-03297: file contains used data beyond requested RESIZE value

Quizás te preguntes por qué no puedes reducir el tamaño de ese datafile si hay suficiente espacio libre, y muy probablemente no puedes hacerlo porque algún segmento (tabla, índice, etc.) está ubicado más allá del valor de RESIZE. Tienes que mover ese segmento con técnicas tales como reconstruir índices y mover tablas, pero antes de intentar redimensionar un datafile podrías preferir hacer un mapa del uso del tablespace con este script: Tablespace_map2.sql

Aviso: YO NO ESCRIBI ESTE SCRIPT, sólo lo modifiqué para dividir los mapas por datafile como está escrito en los comentarios al inicio del script. No pude encontrar al autor original por lo que no puedo citarlo apropiadamente.

Después de correr este script obtendrás un archivo HTML con un mapa de uso por datafile del tablespace seleccionado; este script es muy útil si sólo puedes ejecutar scripts en una línea de comandos SQL. Si colocas el apuntador del ratón sobre un bloque obtendrás información sobre el propietario, tipo de segmento y nombre del segmento que corresponde a ese bloque, o si es un bloque libre.

Más información:

ORA-03297 Tips

miércoles, 15 de febrero de 2012

Como eliminar un usuario en Progress

Si necesitas eliminar un usuario conectado a una base de datos Progress (por problemas de bloqueos por ejemplo), puedes obtener una lista y entonces eliminar un usuario con el comando proshut:

myserver:/myprogressdb:> proshut mydb -C list

usr pid time of login user id tty Limbo?

165 20352 Tue Jun 21 08:35:32 2009 SOMEUSER w1c3xpr3 no


myserver:/myprogressdb:> proshut mydb -C disconnect 165

User 165 disconnect initiated. (6796)

También puedes desconectar usuarios y dar de baja bases de datos con el menu promon, seleccionando la opción 8:

myserver:/myprogressdb:> promon mydb.db

PROGRESS MONITOR Version 9

Database: /myprogressdb/mydb

1. User Control
2. Locking and Waiting Statistics
3. Block Access
4. Record Locking Table
5. Activity
6. Shared Resources
7. Database Status
8. Shut Down Database

T. Transactions Control
L. Resolve Limbo Transactions
C. Coordinator Information

M. Modify Defaults
Q. Quit

Enter your selection: 8
usr pid time of login user id tty Limbo?
11 24122 Sat May 21 10:23:29 2009 biw no
12 24247 Sat May 21 10:23:30 2009 wdog no
13 14898 Wed Jun 29 08:25:08 2009 MYUSER0001 /dev/pts/1 no
100 18648 Tue Jun 28 11:30:09 2009 MYUSER0002 w1c3xpr3 no
104 15128 Fri Jun 24 11:51:34 2009 MYUSER0003 w1c3xpr4 no
106 13556 Fri Jun 24 11:52:02 2009 MYUSER0004 w1c3xpr4 no
115 20344 Tue Jun 21 13:30:10 2009 MYUSER0003 w1c3xpr4 no
122 16948 Tue Jun 28 10:18:10 2009 MYUSER0005 w1c3xpr3 no
131 11128 Tue Jun 28 09:09:33 2009 MYUSER0006 w1c3xpr3 no
138 13252 Tue Jun 28 09:45:05 2009 MYUSER0007 w1c3xpr3 no
139 9828 Tue Jun 28 09:00:06 2009 MYUSER0008 w1c3xpr4 no
146 11708 Tue Jun 28 08:51:51 2009 MYUSER0009 w1c3xpr3 no
150 10640 Tue Jun 28 08:48:59 2009 MYUSER0010 w1c3xpr3 no
151 15464 Tue Jun 28 08:48:44 2009 MYUSER0001 w1c3xpr4 no

RETURN - show remaining, Q - quit:
usr pid time of login user id tty Limbo?
190 10708 Wed Jun 29 07:21:35 2009 MYUSER0011 w1c3xpr4 no

1 Disconnect a User
2 Unconditional Shutdown
3 Emergency Shutdown (Kill All)
x Exit

Enter choice>

Si hay muchos usuarios puedes verlos todos presionando RETURN, y después de mostrar todos los usuarios puedes escoger dar de baja la base de datos (opción 2), o eliminar algún usuario con su numero usr (opción 1).

exp e imp de Oracle

Trabajando con exp e imp, prefiero crear archivos de configuración más que escribir todo en la línea de comandos ya que así puedo tener una "plantilla" y modificarla como lo necesite. Por lo cual, esta es mi plantilla para exportar algunos esquemas (no la base de datos completa) con datos, poniendo el archivo de log y el de volcado en /somedir:

BUFFER=1048576
FILE=/somedir/export.dmp
LOG=/somedir/export.log
STATISTICS=NONE
FULL=N
ROWS=Y
OWNER=SOMEUSER,OTHERUSER
DIRECT=Y

La plantilla para exportar una base de datos completa es un poco más corta:

BUFFER=1048576
FILE=/somedir/export.dmp
LOG=/somedir/export.log
STATISTICS=NONE
FULL=Y
ROWS=Y
DIRECT=Y

Por otro lado, para importar algunos esquemas (de un export completo o parcial) uso este archivo de configuración:

BUFFER=1048576
FILE=/somedir/export.dmp
LOG=/somedir/export.log
STATISTICS=NONE
FULL=N
ROWS=Y
feedback=10000
commit=n
IGNORE=Y
fromuser=SOMEUSER,OTHERUSER
touser=SOMEUSER,OTHERUSER
CONSTRAINTS=Y
grants=yes

Y mi plantilla para importar una base de datos completa es la siguiente:

BUFFER=1048576
FILE=/somedir/export.dmp
LOG=/somedir/export.log
STATISTICS=NONE
FULL=Y
ROWS=Y
feedback=10000
commit=n
IGNORE=Y

Y para no olvidar configurar el nombre y el juego de caracteres de la base de datos destino (muy importante), uso este pequeño shell script:

#export ORACLE_SID=mydb
#export NLS_LANG=AMERICAN_AMERICA.WE8ISO8859P1

imp / parfile=/somedir/import.par

Teniendo los parámetros más importantes en tu plantilla puedes cambiarlos fácilmente sin revisar la ayuda o documentación de imp/exp, por eso algunos parámetros son incluídos con sus valores por default.

Más información:

Import Export FAQ
Checking database character set

lunes, 13 de febrero de 2012

Copiando archivos entre servidores con compresión

Podrías tener que copiar archivos muy grandes o sistemas de archivos completos entre servidores de vez en cuando, y mientras más optimices esta copia mas rápido transferirás los archivos con menos uso de recursos computacionales.

Podrías pensar que scp solo podría hacer un buen trabajo, pero sin compresión podrías estar desperdiciando ancho de banda y tiempo (hablando de copiar sistemas de archivos de cientos de gigabytes entre servidores lejanos uno del otro). Pero si tienes mucho espacio en disco disponible, por qué no comprimir el sistema de archivos primero y después transferirlo? Bueno, porque comprimir grandes sistemas de archivos requiere mucho tiempo y la entrada/salida de tu disco es severamente degradada (el servidor lee un archivo e intenta escribirlo comprimido al mismo tiempo); podrías volver tu servidor prácticamente inusable mientras comprimes archivos grandes, por lo menos en AIX.

Pero puedes hacer un respaldo, comprimirlo y enviarlo al otro servidor, descomprimirlo y escribir sólo los archivos que quieres transferir (sin escribir archivos comprimidos en disco), comprimiéndolos al vuelo con tuberías. Para esto, puedes escribir este pequeño script y ponerlo en un directorio apropiado en el servidor origen:

tar -cf - $1 | compress -c

Entonces si nombraste este script mycompressedtar por ejemplo, puedes transferir archivos o directorios a tu servidor destino de esta manera:

otheruser@targetsrv> ssh -l myuser sourcesrv /path/mycompressedtar /some/dir | uncompress -c | tar -xf -

Si lanzas este comando en tu servidor destino, copiarás /some/dir del servidor origen al directorio donde estabas en el momento de la ejecución; por supuesto tienes que tener acceso a /some/dir con la cuenta myuser.

Pero algunas veces tienes pocos archivos pero muy grandes (como datafiles de Oracle), y no puedes transferirlos con tar sin obtener un error en AIX; en ese caso puedes sólo transferir archivo por archivo con este pequeño script:

cat $1 | compress -c

Entonces si nombras este script mycompress por ejemplo, puedes copiar un archivo a tu servidor destino de esta forma:

otheruser@targetsrv> ssh -l myuser sourcesrv /path/mycompress /some/file | uncompress -c > somefile

Si ejecutas este comando en tu servidor destino, copiarás file del servidor origen al directorio donde estés al momento de la ejecución y se llamará somefile; puedes usar el mismo nombre del archivo origen o uno diferente.

Pero estamos hablando acerca de servidores diferentes, y los archivos son tan valiosos que no podemos darnos el lujo de cambiar un solo byte. Cómo podemos estar seguros de que los archivos son exactamente iguales? En AIX, puedes usar el comando cksum:

myuser@sourcesrv> cksum myfile.txt
1656934735 2229 myfile.txt

===================================

otheruser@targetsrv> cksum myfile.txt
1656934735 2229 myfile.txt

De esta manera estarás seguro que ambos archivos son iguasles.

Más información:

cksum Command

miércoles, 8 de febrero de 2012

Creando una base de datos Oracle en espera física

DataGuard es una tecnología de Oracle que te permite replicar una base de datos y mantenerla actualizada de acuerdo a niveles seleccionados de desempeño y protección; puedes tener una o más bases de datos en espera que puedes abrir en modo de sólo lectura con el propósito de generación de reportes (espera física) o en modo de lectura y escritura si no cambias los datos replicados (espera lógica). DataGuard es una tecnología muy útil para recuperación de desastres y más, por lo cual es muy recomendable leer toda la documentación listada al final de este mensaje. En el siguiente ejemplo es descrito el escenario más simple: creación de una base de datos en espera física en un servidor diferente de la base de datos primaria, con los mismos directorios, en modo de desempeño máximo, copiando la base de datos primaria con comandos de sistema operativo (no ASM).

Este ejemplo está escrito de forma que puedas crear rápida y fácilmente una base de datos en espera, pero no te confundas por esto y omitas leer la documentación; mientras más estudies la tecnología DataGuard menos problemas tendrás probando diferentes escenarios y configuraciones y diagnosticando tu base de datos en espera.

La primera parte y la mas difícil de crear una base de datos en espera es configurar la base de datos primaria, por lo tanto empezaremos activando la opción FORCE_LOGGING en la base de datos primaria:

oracle@primarysrv:/opt/oracle/product/10.2.0/db_1/dbs$ sqlplus / as sysdba

SQL*Plus: Release 10.2.0.1.0 - Production on Tue Jun 7 12:49:25 2011

Copyright (c) 1982, 2005, Oracle. All rights reserved.


Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning, OLAP and Data Mining Scoring Engine options

SQL> select FORCE_LOGGING from v$database;

FOR
---
NO

SQL> ALTER DATABASE FORCE LOGGING;

Database altered.

SQL> select FORCE_LOGGING from v$database;

FOR
---
YES

A continuación obtendremos información de los archivos de redo log tal como cantidad de grupos, hilos, el directorio y tamaño de los miembros, y crearemos archivos de log para espera del mismo tamaño que los archivos de log, agregando un grupo extra por cada hilo para los archivos de log para espera:

SQL> select GROUP#, MEMBER from v$logfile order by GROUP#;

GROUP# MEMBER
---------- ----------------------------------------
1 /primarydb/logfile/redo01.rdo
1 /fra/primarydb/logfile/redo01.rdo
2 /primarydb/logfile/redo02.rdo
2 /fra/primarydb/logfile/redo02.rdo
3 /primarydb/logfile/redo03.rdo
3 /fra/primarydb/logfile/redo03.rdo

6 rows selected.

SQL> select GROUP#, THREAD#, BYTES from v$log order by GROUP#;

GROUP# THREAD# BYTES
---------- ---------- ----------
1 1 134217728
2 1 134217728
3 1 134217728

SQL> ALTER DATABASE ADD STANDBY LOGFILE GROUP 4 ('/primarydb/logfile/stdby01.rdo',
'/fra/primarydb/logfile/stdby01.rdo') SIZE 134217728;

Database altered.

SQL> ALTER DATABASE ADD STANDBY LOGFILE GROUP 5 ('/primarydb/logfile/stdby02.rdo',
'/fra/primarydb/logfile/stdby02.rdo') SIZE 134217728;

Database altered.

SQL> ALTER DATABASE ADD STANDBY LOGFILE GROUP 6 ('/primarydb/logfile/stdby03.rdo',
'/fra/primarydb/logfile/stdby03.rdo') SIZE 134217728;

Database altered.

SQL> ALTER DATABASE ADD STANDBY LOGFILE GROUP 7 ('/primarydb/logfile/stdby04.rdo',
'/fra/primarydb/logfile/stdby04.rdo') SIZE 134217728;

Database altered.

SQL> SELECT GROUP#, THREAD#, SEQUENCE#, ARCHIVED, STATUS FROM V$STANDBY_LOG;

GROUP# THREAD# SEQUENCE# ARC STATUS
---------- ---------- ---------- --- ----------
4 0 0 YES UNASSIGNED
5 0 0 YES UNASSIGNED
6 0 0 YES UNASSIGNED
7 0 0 YES UNASSIGNED

Después de crear los grupos de archivos de log para espera revisaremos si la base de datos está en modo de archivado, tiene archivo de contraseñas y el parámetro remote_login_passwordfile está configurado adecuadamente; si no crearemos un archivo de contraseñas y activaremos el modo de archivado:

SQL> show parameter REMOTE_LOGIN_PASSWORDFILE

NAME TYPE VALUE
------------------------------------ -------------------- --------------
remote_login_passwordfile string EXCLUSIVE

SQL> select * from V$PWFILE_USERS;

no rows selected

SQL> select NAME, LOG_MODE from v$database;

NAME LOG_MODE
--------- ------------
primarydb NOARCHIVELOG

SQL> SHUTDOWN IMMEDIATE;
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> exit
Disconnected from Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning, OLAP and Data Mining Scoring Engine options
oracle@primarysrv:~$ cd $ORACLE_HOME/dbs
oracle@primarysrv:/opt/oracle/product/10.2.0/db_1/dbs$ orapwd FILE=orapwprimarydb password="somepassword"
oracle@primarysrv:/opt/oracle/product/10.2.0/db_1/dbs$ sqlplus '/ as sysdba'

SQL*Plus: Release 10.2.0.1.0 - Production on Fri Jun 10 11:34:18 2011

Copyright (c) 1982, 2005, Oracle. All rights reserved.


Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning, OLAP and Data Mining Scoring Engine options

SQL> STARTUP MOUNT;
ORACLE instance started.

Total System Global Area 268435456 bytes
Fixed Size 1218892 bytes
Variable Size 92276404 bytes
Database Buffers 171966464 bytes
Redo Buffers 2973696 bytes
Database mounted.
SQL> ALTER DATABASE ARCHIVELOG;

Database altered.

SQL> ALTER DATABASE OPEN;

Database altered.

SQL> select NAME, LOG_MODE from v$database;

NAME LOG_MODE
--------- ------------
primarydb ARCHIVELOG

SQL> alter user SYS identified by "mypassword";

User altered.

SQL> select * from V$PWFILE_USERS;

USERNAME SYSDB SYSOP
------------------------------ ----- -----
SYS TRUE TRUE

En el paso anterior creamos primero un archivo de contraseñas con una contraseña y después cambiamos la contraseña de SYS; para que funcionen los servicios de transporte de redo las bases de datos primaria y en espera tienen que tener la misma contraseña para SYS y un archivo de contraseñas, por lo tanto si no hay archivo de contraseñas en la base de datos primaria puedes primero crearlo y luego cambiar la contraseña de SYS, crear el archivo de contraseñas con la contraseña apropiada o sólo cambiar la contraseña de SYS si ya existe. Pero dado que no puedes abrir una base de datos en espera física en modo de lectura y escritura sin descomponerla para propósitos de DataGuard, no puedes cambiar la contraseña de SYS con una sentencia ALTER USER por lo que tienes que crear el archivo de contraseñas de la base de datos en espera con la contraseña apropiada.

A continuación configuraremos los parámetros necesarios para una configuración de DataGuard, hay que revisar los 19 parámetros listados a continuación y cambiarlos como sea necesario, además de que podría tenerse que revisar otros parámetros no listados que estén relacionados a direcciones de servidor o directorios de sistemas de archivos.

SQL> column NAME format a30
SQL> column VALUE format a40
SQL> select NAME, VALUE from v$parameter where NAME in ('db_name','db_unique_name',
'log_archive_config','control_files','log_archive_dest','log_archive_dest_1',
'log_archive_dest_2','log_archive_dest_state_1','log_archive_dest_state_2',
'remote_login_passwordfile','log_archive_format','log_archive_max_processes','fal_server',
'fal_client','db_file_name_convert','log_file_name_convert','standby_file_management',
'db_recovery_file_dest','db_recovery_file_dest_size') order by NAME;

NAME VALUE
------------------------------ ----------------------------------------
control_files /primarydb/controlfile/primarydb01.ctl
, /fra/primarydb/controlfile/primarydb02.c
tl

db_file_name_convert
db_name primarydb
db_recovery_file_dest
db_recovery_file_dest_size 0
db_unique_name primarydb
fal_client
fal_server
log_archive_config
log_archive_dest /fra/archfile/
log_archive_dest_1
log_archive_dest_2
log_archive_dest_state_1 enable
log_archive_dest_state_2 enable
log_archive_format primarydb_%t_%s_%r.arc
log_archive_max_processes 2
log_file_name_convert
remote_login_passwordfile EXCLUSIVE
standby_file_management MANUAL

19 rows selected.

SQL> alter system set LOG_ARCHIVE_CONFIG='DG_CONFIG=(primarydb,standbydb)';

System altered.

SQL> alter system set log_archive_dest='';

System altered.

SQL> alter system set LOG_ARCHIVE_DEST_1='LOCATION=/fra/primarydb/archivelog VALID_FOR=(ALL_LOGFILES,ALL_ROLES) DB_UNIQUE_NAME=primarydb';

System altered.

SQL> alter system set LOG_ARCHIVE_DEST_2='SERVICE=standbydb LGWR ASYNC VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=standbydb';

System altered.

SQL> alter system set FAL_SERVER=standbydb;

System altered.

SQL> alter system set FAL_CLIENT=primarydb;

System altered.

SQL> alter system set standby_file_management=AUTO;

System altered.

SQL> select NAME, VALUE from v$parameter where NAME in ('db_name','db_unique_name',
'log_archive_config','control_files','log_archive_dest','log_archive_dest_1',
'log_archive_dest_2','log_archive_dest_state_1','log_archive_dest_state_2',
'remote_login_passwordfile','log_archive_format','log_archive_max_processes','fal_server',
'fal_client','db_file_name_convert','log_file_name_convert','standby_file_management',
'db_recovery_file_dest','db_recovery_file_dest_size') order by NAME;

NAME VALUE
------------------------------ ----------------------------------------
control_files /primarydb/controlfile/primarydb01.ctl
, /fra/primarydb/controlfile/primarydb02.c
tl

db_file_name_convert
db_name primarydb
db_recovery_file_dest
db_recovery_file_dest_size 0
db_unique_name primarydb
fal_client primarydb
fal_server standbydb
log_archive_config DG_CONFIG=(primarydb,standbydb)
log_archive_dest
log_archive_dest_1 LOCATION=/fra/primarydb/archivel
og VALID_FOR=(ALL_LOGFILES,ALL_ROLES) DB
_UNIQUE_NAME=primarydb

log_archive_dest_2 SERVICE=standbydb LGWR ASYNC VALID_FOR=(O
NLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_N
AME=standbydb

log_archive_dest_state_1 enable
log_archive_dest_state_2 enable
log_archive_format primarydb_%t_%s_%r.arc
log_archive_max_processes 2
log_file_name_convert
remote_login_passwordfile EXCLUSIVE
standby_file_management AUTO

19 rows selected.

Tienes que configurar valores como los arriba listados para tus parámetros de base de datos DataGuard. Después de haber hecho esto, estás casi listo para copiar tu base de datos al servidor en espera junto con el archivo pfile y uno especial de control de la base de datos:

SQL> SHUTDOWN IMMEDIATE;
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> exit
Disconnected from Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning, OLAP and Data Mining Scoring Engine options
oracle@primarysrv:~$ sqlplus / as sysdba

SQL*Plus: Release 10.2.0.1.0 - Production on Wed Jun 8 12:18:44 2011

Copyright (c) 1982, 2005, Oracle. All rights reserved.

Connected to an idle instance.

SQL> STARTUP MOUNT;
ORACLE instance started.

Total System Global Area 268435456 bytes
Fixed Size 1218892 bytes
Variable Size 92276404 bytes
Database Buffers 171966464 bytes
Redo Buffers 2973696 bytes
Database mounted.
SQL> ALTER DATABASE CREATE STANDBY CONTROLFILE AS '/primarydb/standbydb.ctl';

Database altered.

SQL> CREATE PFILE='/primarydb/initstandbydb.ora' FROM SPFILE;

File created.

SQL> SHUTDOWN IMMEDIATE;
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> exit
Disconnected from Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning, OLAP and Data Mining Scoring Engine options

En este punto puedes copiar todos los archivos relevantes de la base de datos (datafiles, tempfiles, online redo logs, standby redo logs, standby control file y pfile) con tu método y programas favoritos, como scp. No olvides colocar todos los archivos en el mismo directorio (respecto al origen), este ejemplo asume eso. Si colocas los archivos de la base de datos en un directorio diferente, tienes que configurar parámetros como db_file_name_convert y log_file_name_convert (no cubiertos en este mensaje). Además, tienes que crear directorios necesarios para parámetros como background_dump_dest, core_dump_dest, user_dump_dest y audit_file_dest entre otros.

Después de eso, tienes que copiar tu archivo de control de espera a todos los directorios especificados en el parámetro control_files, usando los mismos nombres de archivo; no puedes usar el archivo de control original (primario) o saldrá un error.

Finalmente, tienes que modificar algunos parámetros en el pfile de espera de la nueva base de datos; podría ser útil tener el pfile primario original y el pfile de espera y compararlos como en el siguiente ejemplo, sólo para estar seguro que modificaste todo apropiadamente. En este pfile de espera no hay parámetros primarydb.__ ya que están relacionados a parámetros manejados automáticamente por la instancia de Oracle pero salvados de todas formas con la sentencia CREATE PFILE, y el parámetro audit_trail debe ser puesto como OS, de otra forma necesitas abrir la base de datos en modo de lectura y escritura para guardar todos los eventos de auditoría en la misma base de datos (valor DB), pero no puedes hacer eso con una base de datos en espera física. Además, hay un parámetro extra db_unique_name con el nombre de la base de datos en espera, que por default es opcional e igual a db_name (en este caso primarydb) pero obligatorio en una base de datos en espera.

oracle@standbysrv:/primarydb$ diff initstandbydb.ora initprimarydb.ora
0a1,5
> primarydb.__db_cache_size=171966464
> primarydb.__java_pool_size=4194304
> primarydb.__large_pool_size=4194304
> primarydb.__shared_pool_size=83886080
> primarydb.__streams_pool_size=0
3,4c8,9
< *.audit_trail='os'
---
> *.audit_trail='db'
14,16c19,20
< *.db_unique_name='standbydb'
< *.fal_client='standbydb'
< *.fal_server='primarydb'
---
> *.fal_client='primarydb'
> *.fal_server='standbydb'
22,23c26,27
< *.log_archive_dest_1='LOCATION=/fra/primarydb/standbydb/archivelog VALID_FOR=(ALL_LOGFILES,ALL_ROLES) DB_UNIQUE_NAME=standbydb'
< *.log_archive_dest_2='SERVICE=primarydb LGWR ASYNC VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=primarydb'
---
> *.log_archive_dest_1='LOCATION=/fra/primarydb/primarydb/archivelog VALID_FOR=(ALL_LOGFILES,ALL_ROLES) DB_UNIQUE_NAME=primarydb'
> *.log_archive_dest_2='SERVICE=standbydb LGWR ASYNC VALID_FOR=(ONLINE_LOGFILES,PRIMARY_ROLE) DB_UNIQUE_NAME=standbydb'

Pero antes de levantar tus bases de datos primaria y en espera, primero tienes que incluir en los archivos tnsnames.ora la información de conexión de las instancias primaria y en espera, en los servidores primario y en espera:

PRIMARYDB =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = primarysrv)(PORT = 1521))
)
(CONNECT_DATA = (SERVICE_NAME = primarydb))
)

STANDBYDB =
(DESCRIPTION =
(ADDRESS_LIST =
(ADDRESS = (PROTOCOL = TCP)(HOST = standbysrv)(PORT = 1521))
)
(CONNECT_DATA = (SERVICE_NAME = standbydb))
)

Además tienes que tener los listeners configurados para las instancias primaria y en espera (cambia los archivos listener.ora como sea necesario):

LISTENER_PRIMARYDB =(ADDRESS_LIST = (ADDRESS= (PROTOCOL=TCP) (HOST = primarysrv) (PORT = 1521)))

SID_LIST_LISTENER_PRIMARYDB =
(SID_LIST =
(SID_DESC =
(SID_NAME = primarydb)
(ORACLE_HOME = /opt/oracle/product/10.2.0/db_1)
(ORACLE_SID = primarydb)
)
)

CONNECT_TIMEOUT_LISTENER_PRIMARYDB=30

Levanta los listeners de las bases de datos primaria y en espera y revisalos con el comando tnsping, en los servidores primario y en espera:

oracle@standbysrv:/primarydb$ tnsping primarydb

TNS Ping Utility for Linux: Version 10.2.0.1.0 - Production on 17-JUN-2011 12:23:42

Copyright (c) 1997, 2005, Oracle. All rights reserved.

Used parameter files:
/opt/oracle/product/10.2.0/db_1/network/admin/sqlnet.ora


Used TNSNAMES adapter to resolve the alias
Attempting to contact (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = primarysrv)(PORT = 1521))
(CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = primarydb)))
OK (10 msec)
oracle@standbysrv:/primarydb$ tnsping standbydb

TNS Ping Utility for Linux: Version 10.2.0.1.0 - Production on 17-JUN-2011 12:23:42

Copyright (c) 1997, 2005, Oracle. All rights reserved.

Used parameter files:
/opt/oracle/product/10.2.0/db_1/network/admin/sqlnet.ora


Used TNSNAMES adapter to resolve the alias
Attempting to contact (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = standbysrv)(PORT = 1521))
(CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = standbydb)))
OK (0 msec)

En este punto podrás crear un archivo de contraseñas y levantar tu base de datos en espera:

oracle@standbysrv:/primarydb$ export ORACLE_SID=standbydb
oracle@standbysrv:/primarydb$ set|grep ORA
ORACLE_BASE=/opt/oracle
ORACLE_HOME=/opt/oracle/product/10.2.0/db_1
ORACLE_SID=standbydb
oracle@standbysrv:/primarydb$ cd $ORACLE_HOME/dbs
oracle@standbysrv:/opt/oracle/product/10.2.0/db_1/dbs$ orapwd FILE=orapwstandbydb password="mypassword"
oracle@standbysrv:/opt/oracle/product/10.2.0/db_1/dbs$ sqlplus / as sysdba

SQL*Plus: Release 10.2.0.1.0 - Production on Wed Jun 8 17:15:59 2011

Copyright (c) 1982, 2005, Oracle. All rights reserved.

Connected to an idle instance.

SQL> create spfile from pfile='/primarydb/initstandbydb.ora';

File created.

SQL> startup mount;
ORACLE instance started.

Total System Global Area 268435456 bytes
Fixed Size 1218892 bytes
Variable Size 88082100 bytes
Database Buffers 176160768 bytes
Redo Buffers 2973696 bytes
Database mounted.

Si todo fue hecho apropiadamente, en el siguiente paso la base de datos en espera contactará la base de datos primaria e iniciará la sincronización de datos, pero primero tienes que levantar tu base de datos primaria como lo haces usualmente (abrirla en modo de lectura y escritura). Entonces, inicias la sincronización de datos en la base de datos en espera:

SQL> alter database recover managed standby database disconnect from session;

Database altered.

SQL> select name, open_mode, database_role from v$database;

NAME OPEN_MODE DATABASE_ROLE
--------- ---------- ----------------
PRIMARYDB MOUNTED PHYSICAL STANDBY

Y además forzas un cambio de log en la base de datos primaria para iniciar la transferencia:

SQL> ALTER SYSTEM SWITCH LOGFILE;

System altered.

SQL> SELECT SEQUENCE#, FIRST_TIME, NEXT_TIME FROM V$ARCHIVED_LOG ORDER BY SEQUENCE#;

SEQUENCE# FIRST_TIME NEXT_TIME
---------- ----------- -----------
6 07-jun-2011 08-jun-2011
7 08-jun-2011 08-jun-2011
8 08-jun-2011 10-jun-2011
9 10-jun-2011 10-jun-2011

Después de esperar un poco debes ver la misma información en la base de datos en espera:

SQL> select sequence#, first_time, next_time from v$archived_log order by sequence#;

SEQUENCE# FIRST_TIME NEXT_TIME
---------- ------------------ ------------------
6 07-JUN-11 08-JUN-11
7 08-JUN-11 08-JUN-11
8 08-JUN-11 10-JUN-11
9 10-JUN-11 10-JUN-11

4 rows selected.

Si puedes verla, felicitaciones! Todo salió bien y ahora tienes una base de datos en espera física funcionando. Como un buen extra, puedes configurar el borrado automático de redo logs aplicados cuando tu sistema de archivos de logs archivados esté casi lleno, y además revisar tu base de datos en espera abriéndola en modo de sólo lectura.

Activemos el borrado automático de redo logs aplicados configurando los siguientes parámetros en la base de datos en espera:

SQL> alter system set DB_RECOVERY_FILE_DEST_SIZE=2G;

System altered.

SQL> alter system set DB_RECOVERY_FILE_DEST='/fra/primarydb';

System altered.

SQL> alter system set LOG_ARCHIVE_DEST_1='LOCATION=USE_DB_RECOVERY_FILE_DEST';

System altered.

SQL> exit
Disconnected from Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning, OLAP and Data Mining options

Configura DB_RECOVERY_FILE_DEST_SIZE un poco abajo del límite del sistema de archivos de DB_RECOVERY_FILE_DEST; recuerda que cualquier archivo no colocado automáticamente por y relacionado con Oracle no cuenta para el límite puesto en el parámetro DB_RECOVERY_FILE_DEST_SIZE. Y para terminar este paso, tienes que usar la sentencia CONFIGURE ARCHIVELOG DELETION POLICY TO APPLIED ON STANDBY en RMAN en los servidores primario y en espera:

oracle@primarysrv:/opt/oracle/product/10.2.0/db_1/dbs$ rman

Recovery Manager: Release 10.2.0.1.0 - Production on Fri Jun 10 12:57:46 2011

Copyright (c) 1982, 2005, Oracle. All rights reserved.

RMAN> connect target /

connected to target database: primarydb (DBID=3023816100)

RMAN> CONFIGURE ARCHIVELOG DELETION POLICY TO APPLIED ON STANDBY;

using target database control file instead of recovery catalog
new RMAN configuration parameters:
CONFIGURE ARCHIVELOG DELETION POLICY TO APPLIED ON STANDBY;
new RMAN configuration parameters are successfully stored

RMAN> show all;

RMAN configuration parameters are:
...
CONFIGURE ARCHIVELOG DELETION POLICY TO APPLIED ON STANDBY;
...

RMAN> exit


Recovery Manager complete.

========================================

oracle@standbysrv:/opt/oracle/product/10.2.0/db_1/dbs$ rman

Recovery Manager: Release 10.2.0.1.0 - Production on Fri Jun 10 12:54:49 2011

Copyright (c) 1982, 2005, Oracle. All rights reserved.

RMAN> connect target /

connected to target database: primarydb (DBID=3023816100, not open)

RMAN> CONFIGURE ARCHIVELOG DELETION POLICY TO APPLIED ON STANDBY;

using target database control file instead of recovery catalog
new RMAN configuration parameters:
CONFIGURE ARCHIVELOG DELETION POLICY TO APPLIED ON STANDBY;
new RMAN configuration parameters are successfully stored

RMAN> SHOW ALL;

RMAN configuration parameters are:
...
CONFIGURE ARCHIVELOG DELETION POLICY TO APPLIED ON STANDBY;
...
RMAN> exit


Recovery Manager complete.

Casi hemos terminado! Ahora revisaremos que la base de datos en espera pueda ser abierta en modo de sólo lectura (por ejempo para generación de reportes) abriéndola y haciendo una simple consulta:

oracle@standbysrv:/opt/oracle/product/10.2.0/db_1/dbs$ sqlplus '/ as sysdba'

SQL*Plus: Release 10.2.0.1.0 - Production on Fri Jun 10 13:16:24 2011

Copyright (c) 1982, 2005, Oracle. All rights reserved.


Connected to:
Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning, OLAP and Data Mining options

SQL> alter database recover managed standby database cancel;

Database altered.

SQL> alter database open;

Database altered.

SQL> select count(*) from dba_objects;

COUNT(*)
----------
9715

SQL> select name, open_mode from v$database;

NAME OPEN_MODE
--------------------------- ------------------------------
primarydb READ ONLY

En este momento puedes consultar tablas relacionadas con tus propios esquemas si es que las conoces.

Finalmente, puedes reiniciar la sincronización en la base de datos en espera:

SQL> shutdown immediate;
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> exit
Disconnected from Oracle Database 10g Enterprise Edition Release 10.2.0.1.0 - Production
With the Partitioning, OLAP and Data Mining options
oracle@standbysrv:/opt/oracle/product/10.2.0/db_1/dbs$ sqlplus '/ as sysdba'

SQL*Plus: Release 10.2.0.1.0 - Production on Fri Jun 10 13:20:46 2011

Copyright (c) 1982, 2005, Oracle. All rights reserved.

Connected to an idle instance.

SQL> startup mount;
ORACLE instance started.

Total System Global Area 268435456 bytes
Fixed Size 1218892 bytes
Variable Size 88082100 bytes
Database Buffers 176160768 bytes
Redo Buffers 2973696 bytes
Database mounted.
SQL> alter database recover managed standby database disconnect from session;

Database altered.

SQL> select sequence#, first_time, next_time from v$archived_log order by sequence#;

SEQUENCE# FIRST_TIME NEXT_TIME
---------- ------------------ ------------------
6 07-JUN-11 08-JUN-11
7 08-JUN-11 08-JUN-11
8 08-JUN-11 10-JUN-11
9 10-JUN-11 10-JUN-11
10 10-JUN-11 10-JUN-11
11 10-JUN-11 10-JUN-11
12 10-JUN-11 10-JUN-11

7 rows selected.

No olvides forzar un cambio de logs en la base de datos primaria para revisar la trasferencia de redo logs.

Y eso es todo! Hemos terminado la creación y revisión de una base de datos Oracle en espera física; no olvides leer la siguiente documentación (o alguna similar para 11g):

Oracle Data Guard Concepts and Administration 10g Release 2 (10.2)
Creating and Maintaining a Password File
Maintenance Of Archivelogs On Standby Databases

martes, 7 de febrero de 2012

Obteniendo información de triggers

Si quieres saber información acerca de triggers propiedad de alguien, triggers ligados a una tabla o el código de un trigger, puedes consultar la tabla dba_triggers:

SQL> set linesize 150
SQL> set wrap on
SQL> select OWNER, TRIGGER_NAME, TRIGGER_TYPE, TABLE_OWNER, TABLE_NAME from dba_triggers
where owner in ('SOMEUSER') order by OWNER, TRIGGER_NAME;

OWNER TRIGGER_NAME TRIGGER_TYPE TABLE_OWNER TABLE_NAME
------------------ -------------------- ---------------- ------------------ ------------------
SOMEUSER SOMETRIGGER AFTER EACH ROW SOMEUSER SOMETABLE
SOMEUSER ATRIGGER AFTER EACH ROW SOMEUSER OTHERTABLE
SOMEUSER OTHERTRIGGER AFTER EACH ROW SOMEUSER ATABLE
SOMEUSER MYTRIGGER AFTER EACH ROW SOMEUSER MYTABLE

4 rows selected.

SQL> select TABLE_OWNER, TABLE_NAME, OWNER, TRIGGER_NAME, TRIGGER_TYPE from dba_triggers
where TABLE_OWNER in ('SOMEUSER') order by TABLE_OWNER, TABLE_NAME;

TABLE_OWNER TABLE_NAME OWNER TRIGGER_NAME TRIGGER_TYPE
------------------ ------------------ ------------------ -------------------- ----------------
SOMEUSER SOMETABLE SOMEUSER TRIGGER_ONE BEFORE STATEMENT
SOMEUSER SOMETABLE SOMEUSER TRIGGER_TWO AFTER STATEMENT
SOMEUSER SOMETABLE SOMEUSER SOMETRIGGER AFTER EACH ROW
SOMEUSER OTHERTABLE SOMEUSER TRIGGER_THREE AFTER STATEMENT

4 rows selected.

SQL> set long 20000
SQL> select TRIGGER_BODY from dba_triggers where OWNER='SOMEUSER' and TRIGGER_NAME='SOMETRIGGER';

TRIGGER_BODY
--------------------------------------------------------------------------------
BEGIN
IF INSERTING THEN
log_id ('', NEW.id);
ELSE
log_id (OLD.id, NEW.id);
END IF;
END;

Exportando esquemas con objetos inválidos

Digamos que quieres copiar un esquema de una base de datos Oracle a otra base de datos Oracle, primero exportas ese esquema con exp sin mensajes de advertencia:

Connected to: Oracle Database 10g Enterprise Edition Release 10.2.0.4.0 - 64bit Production
With the Partitioning, OLAP, Data Mining and Real Application Testing options
Export done in US7ASCII character set and AL16UTF16 NCHAR character set
server uses WE8ISO8859P1 character set (possible charset conversion)

About to export the entire database ...
. exporting tablespace definitions
. exporting profiles
. exporting user definitions
. exporting roles
. exporting resource costs
. exporting rollback segment definitions
. exporting database links

...

. exporting post-schema procedural objects and actions
. exporting user history table
. exporting default and system auditing options
. exporting statistics
Export terminated successfully without warnings.

Entonces lo importas en tu base de datos destino, nuevamente sin mensajes de error:

Connected to: Oracle Database 11g Enterprise Edition Release 11.1.0.7.0 - 64bit Production
With the Partitioning option

Export file created by EXPORT:V10.02.01 via direct path
import done in WE8ISO8859P1 character set and AL16UTF16 NCHAR character set
export client uses US7ASCII character set (possible charset conversion)
. importing SOMEUSER's objects into SOMEUSER
. . importing table "TABLE_ONE"
0 rows imported
. . importing table "TABLE_TWO"
0 rows imported
. . importing table "TABLE_THREE"
22 rows imported

...

. . importing table "TABLE_SIXTY_ONE"
0 rows imported
. . importing table "TABLE_SIXTY_TWO"
1 rows imported
. . importing table "TABLE_SIXTY_THREE"
0 rows imported
Import terminated successfully without warnings.

Sólo para estar seguro, cuentas la cantidad de objetos de ese esquema en ambas bases de datos, solo para encontrar que hay menos objetos en tu base de datos destino:

SQL> select OWNER, count(*) from dba_objects where OWNER in ('SOMEUSER')
group by OWNER order by OWNER;

OWNER COUNT(*)
------------------------------ ----------
SOMEUSER 241

=====================================================

SQL> select OWNER, count(*) from dba_objects where OWNER in ('SOMEUSER')
group by OWNER order by OWNER;

OWNER COUNT(*)
------------------------------ ----------
SOMEUSER 205

Este es un buen momento para revisar y recompilar objetos inválidos, por lo que ejecutas el script utlrp en tu base de datos origen y revisas de nuevo la tabla dba_objects:

@?/rdbms/admin/utlrp.sql;

...

DOC> The following query reports the number of objects that have compiled
DOC> with errors (objects that compile with errors have status set to 3 in
DOC> obj$). If the number is higher than expected, please examine the error
DOC> messages reported with each object (using SHOW ERRORS) to see if they
DOC> point to system misconfiguration or resource constraints that must be
DOC> fixed before attempting to recompile these objects.
DOC>#

OBJECTS WITH ERRORS
-------------------
2

DOC> The following query reports the number of errors caught during
DOC> recompilation. If this number is non-zero, please query the error
DOC> messages in the table UTL_RECOMP_ERRORS to see if any of these errors
DOC> are due to misconfiguration or resource constraints that must be
DOC> fixed before objects can compile successfully.
DOC>#

ERRORS DURING RECOMPILATION
---------------------------
1


PL/SQL procedure successfully completed.

SQL> column OBJECT_NAME format a50
SQL> select OWNER, OBJECT_TYPE, OBJECT_NAME from dba_objects where STATUS='INVALID'
order by OWNER, OBJECT_TYPE, OBJECT_NAME;

OWNER OBJECT_TYPE OBJECT_NAME
------------------------------ ------------------- -----------------------------
SOMEUSER PROCEDURE SOME_PROCEDURE
PERFSTAT PACKAGE BODY STATSPACK
SYS PACKAGE BODY DBMS_SWRF_REPORT_INTERNAL

Ahora sabes por qué hay menos objetos en la base de datos destino, los objetos inválidos no fueron exportados. Podrías querer revisar los errores de procedimientos y arreglarlos antes de intentar nuevamente importar/exportar.

jueves, 2 de febrero de 2012

Cómo activar el particionado en Oracle

Digamos que exportaste algunos datos con exp sin mensajes de error o advertencias de una base de datos, y cuando estás importándolos en otra base de datos sale un error ORA-00439:

Connected to: Oracle Database 11g Enterprise Edition Release 11.1.0.7.0 - 64bit Production

Export file created by EXPORT:V10.02.01 via direct path
import done in WE8ISO8859P1 character set and AL16UTF16 NCHAR character set
export client uses US7ASCII character set (possible charset conversion)
. importing MYUSER's objects into MYUSER
. . importing table "MYTABLE"
0 rows imported
. . importing table "ATABLE"
0 rows imported
. . importing table "ONETABLE"
22 rows imported
IMP-00017: following statement failed with ORACLE error 439:
"CREATE TABLE "BADTABLE" ("LOGDATE" CHAR(8), "SOMEINFO" CHAR(6)
"ENABLE) PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 TABLESPACE "MYTABLES"
"PACE" LOGGING PARTITION BY RANGE ("LOGDATE" ) (PARTITION "ONE""
" VALUES LESS THAN ('20050301') PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 2"
"55 STORAGE(INITIAL 655360 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT"
") TABLESPACE "MYTABLESPACE" LOGGING NOCOMPRESS, PARTITION "TWO" VALUE"
"S LESS THAN ('20040301') PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255 STO"
"RAGE(INITIAL 655360 FREELISTS 1 FREELIST GROUPS 1 BUFFER_POOL DEFAULT) TABL"
"ESPACE "MYTABLESPACE" LOGGING NOCOMPRESS )"
IMP-00003: ORACLE error 439 encountered
ORA-00439: feature not enabled: Partitioning

Este error significa que tienes que activar el particionado en el software de bases de datos de Oracle para usar esta característica, y si no lo hiciste al momento de instalarlo puedes activarlo con estos comandos:

cd $ORACLE_HOME/rdbms/lib
make -f ins_rdbms.mk part_on
make -f ins_rdbms.mk ioracle

Sería mejor dar de baja todas las instancias de Oracle antes de hacer esto; a continuación hay un ejemplo de activar y revisar el particionado en el software de bases de datos Oracle 11g:

$ cd $ORACLE_HOME/rdbms/lib
$ make -f ins_rdbms.mk part_on
/usr/bin/ar d /oracle_11g/product/11.1.0/rdbms/lib/libknlopt.a ksnkkpo.o
/usr/bin/ar cr /oracle_11g/product/11.1.0/rdbms/lib/libknlopt.a /oracle_11g/product/11.1.0/rdbms/lib/kkpoban.o
$ make -f ins_rdbms.mk ioracle
chmod 755 /oracle_11g/product/11.1.0/bin

- Linking Oracle
rm -f /oracle_11g/product/11.1.0/rdbms/lib/oracle
gcc -o /oracle_11g/product/11.1.0/rdbms/lib/oracle -L/oracle_11g/product/11.1.0/rdbms/lib/ -L/oracle_11g/product/11.1.0/lib/ -L/oracle_11g/product/11.1.0/lib/stubs/ -Wl,-E /oracle_11g/product/11.1.0/rdbms/lib/opimai.o /oracle_11g/product/11.1.0/rdbms/lib/ssoraed.o /oracle_11g/product/11.1.0/rdbms/lib/ttcsoi.o -Wl,--whole-archive -lperfsrv11 -Wl,--no-whole-archive /oracle_11g/product/11.1.0/lib/nautab.o /oracle_11g/product/11.1.0/lib/naeet.o /oracle_11g/product/11.1.0/lib/naect.o /oracle_11g/product/11.1.0/lib/naedhs.o /oracle_11g/product/11.1.0/rdbms/lib/config.o -lserver11 -lodm11 -lcell11 -lnnet11 -lskgxp11 -lsnls11 -lnls11 -lcore11 -lsnls11 -lnls11 -lcore11 -lsnls11 -lnls11 -lxml11 -lcore11 -lunls11 -lsnls11 -lnls11 -lcore11 -lnls11 -lclient11 -lvsn11 -lcommon11 -lgeneric11 -lknlopt `if /usr/bin/ar tv /oracle_11g/product/11.1.0/rdbms/lib/libknlopt.a | grep xsyeolap.o > /dev/null 2>&1 ; then echo "-loraolap11" ; fi` -lslax11 -lpls11 -lrt -lplp11 -lserver11 -lclient11 -lvsn11 -lcommon11 -lgeneric11 `if [ -f /oracle_11g/product/11.1.0/lib/libavserver11.a ] ; then echo "-lavserver11" ; else echo "-lavstub11"; fi` `if [ -f /oracle_11g/product/11.1.0/lib/libavclient11.a ] ; then echo "-lavclient11" ; fi` -lknlopt -lslax11 -lpls11 -lrt -lplp11 -ljavavm11 -lserver11 -lwwg `cat /oracle_11g/product/11.1.0/lib/ldflags` -lncrypt11 -lnsgr11 -lnzjs11 -ln11 -lnl11 -lnro11 `cat /oracle_11g/product/11.1.0/lib/ldflags` -lncrypt11 -lnsgr11 -lnzjs11 -ln11 -lnl11 -lnnz11 -lzt11 -lztkg11 -lmm -lsnls11 -lnls11 -lcore11 -lsnls11 -lnls11 -lcore11 -lsnls11 -lnls11 -lxml11 -lcore11 -lunls11 -lsnls11 -lnls11 -lcore11 -lnls11 -lztkg11 `cat /oracle_11g/product/11.1.0/lib/ldflags` -lncrypt11 -lnsgr11 -lnzjs11 -ln11 -lnl11 -lnro11 `cat /oracle_11g/product/11.1.0/lib/ldflags` -lncrypt11 -lnsgr11 -lnzjs11 -ln11 -lnl11 -lnnz11 -lzt11 -lztkg11 -lsnls11 -lnls11 -lcore11 -lsnls11 -lnls11 -lcore11 -lsnls11 -lnls11 -lxml11 -lcore11 -lunls11 -lsnls11 -lnls11 -lcore11 -lnls11 `if /usr/bin/ar tv /oracle_11g/product/11.1.0/rdbms/lib/libknlopt.a | grep "kxmnsd.o" > /dev/null 2>&1 ; then echo " " ; else echo "-lordsdo11"; fi` -L/oracle_11g/product/11.1.0/ctx/lib/ -lctxc11 -lctx11 -lzx11 -lgx11 -lctx11 -lzx11 -lgx11 -lordimt11 -lclsra11 -ldbcfg11 -lhasgen11 -lcore11 -lskgxn2 -locr11 -locrb11 -locrutl11 -lhasgen11 -lcore11 -lskgxn2 -loraz -lsnls11 -lnls11 -lcore11 -lsnls11 -lnls11 -lcore11 -lsnls11 -lnls11 -lxml11 -lcore11 -lunls11 -lsnls11 -lnls11 -lcore11 -lnls11 -lsnls11 -lunls11 -lsnls11 -lnls11 -lcore11 -lsnls11 -lnls11 -lcore11 -lsnls11 -lnls11 -lxml11 -lcore11 -lunls11 -lsnls11 -lnls11 -lcore11 -lnls11 -laio `cat /oracle_11g/product/11.1.0/lib/sysliblist` -Wl,-rpath,/oracle_11g/product/11.1.0/lib -lm `cat /oracle_11g/product/11.1.0/lib/sysliblist` -ldl -lm -L/oracle_11g/product/11.1.0/lib
test ! -f /oracle_11g/product/11.1.0/bin/oracle ||\
mv -f /oracle_11g/product/11.1.0/bin/oracle /oracle_11g/product/11.1.0/bin/oracleO
mv /oracle_11g/product/11.1.0/rdbms/lib/oracle /oracle_11g/product/11.1.0/bin/oracle
chmod 6751 /oracle_11g/product/11.1.0/bin/oracle
$ sqlplus / as sysdba

SQL*Plus: Release 11.1.0.7.0 - Production on Thu Jun 9 17:33:48 2011

Copyright (c) 1982, 2008, Oracle. All rights reserved.


Connected to:
Oracle Database 11g Enterprise Edition Release 11.1.0.7.0 - 64bit Production
With the Partitioning option

SQL> column PARAMETER format a30
SQL> column VALUE format a30
SQL> select * from v$option where parameter = 'Partitioning';

PARAMETER VALUE
------------------------------ ------------------------------
Partitioning TRUE

Más información:

ORA-00439: feature not enabled: Partitioning

miércoles, 1 de febrero de 2012

Instancias de Progress terminando abruptamente y sistemas de archivos

Un día un usuario te dice que una (o muchas) de tus instancias de bases de datos Progress está abajo, por lo que primero revisas si está corriendo con los comandos ps y proutil. La instancia de hecho está abajo, por lo cual revisas el log de la base de datos sólo para no encontrar nada. Intentas levantar la instancia con proserve, y finalmente te das cuenta por qué la instancia murió: el sistema de archivos de la base de datos se cambió a modo de sólo lectura.

Esto podría ser usual si tienes mal configurado un sistema de archivos remoto o tienes una carga pesada, por ejemplo iSCSI, y tiene que ser resuelto por el administrador de sistemas a cargo de ese servidor.

[root@myserver]# ps ax|grep mydb
18187 pts/2 S+ 0:00 grep mydb
[root@myserver]# proutil /progdbs/mydb.db -C holder
PROGRESS Version 9.1E as of Tue Oct 12 14:40:19 EDT 2004

** Cannot find or open file /progdbs/mydb.lk, errno = 17. (43)
[root@myserver]# cd /progdbs
[root@myserver]# tail --lines=10 mydb.lg
14:59:09 SRV 1: Previous message sent on behalf of user 183. (5512)
14:59:10 SRV 1: User is SOMEUSER. (708)
14:59:10 SRV 1: Previous message sent on behalf of user 183. (5512)
14:59:16 SRV 1: Logout usernum 183, userid SOMEUSER, on W1C3XDV1. (739)
14:59:44 SRV 1: Login usernum 183, userid ANUSER, on W1C3XDV1. (742)
14:59:44 SRV 1: User is OTHERUSER. (708)
14:59:44 SRV 1: Previous message sent on behalf of user 183. (5512)
14:59:44 SRV 1: User is OTHERUSER. (708)
14:59:44 SRV 1: Previous message sent on behalf of user 183. (5512)
14:59:44 SRV 1: User is OTHERUSER. (708)
14:59:44 SR
[root@myserver]# proserve /progdbs/mydb -pf /progdbs/mydb.pf
PROGRESS Version 9.1E as of Tue Oct 12 14:40:19 EDT 2004
10:23:38 BROKER : ** Cannot find or open file /progdbs/mydb.lk, errno = 17. (43)
10:23:38 BROKER : ** This process terminated with exit code 2. (8619)
[root@myserver]# ls -la /progdbs/mydb.lk
-r--r--r-- 1 root root 38 Dec 21 11:44 /progdbs/mydb.lk
[root@myserver]# touch /progdbs/hello.txt
touch: cannot touch `/progdbs/hello.txt': Read-only file system