martes, 26 de junio de 2012

trigger con variables


create or replace
TRIGGER LIQUIDACIONPROCESADA
after insert on liquidacion
for each row
declare
  vRut empleado.rut%type;
  vIdEmpleado empleado.idempleado%type;
begin
select idempleado,rut into vIdEmpleado,vRut from empleado where idempleado = :new.idempleado;
insert into funcionariosprocesados(rut,glosa,idempleado) values(vRut,'Funcionario procesado',vIdEmpleado);
end;

miércoles, 20 de junio de 2012

Codigo Control, package con Funcion - Rodrigo Rojas

create or replace package pkDreamHome is
function CtaPropiedad (pNumEmpleado char) return number;
end pkDreamHome;
create or replace package body pkDreamHome is
 FUNCTION CtaPropiedad (pNumEmpleado char)
 RETURN NUMBER is
total char;
BEGIN
  select count(*) into total
  from Propiedad
  where numempleado = pNumEmpleado;
  RETURN total;
END CtaPropiedad;
end pkDreamHome;
select pkDreamHome.CtaPropiedad('SL21') from dual;

Funcion De Control - Propiedad por Empleado - Rodrigo Rojas

create or replace FUNCTION CtaPropiedad (pNumEmpleado char)
return integer is
total integer :=0;
begin
select count (numPropiedad)into total
from  Propiedad
where numEmpleado = pNumEmpleado;
return total;
end CtaPropiedad;
select CtaPropiedad('SL21')from dual;

Triggers y Sequence


create sequence seqLiquidacion
start with 1
increment by 1
nomaxvalue;

create trigger liquidacionIncr
before insert on liquidacion
for each row
begin
select seqLiquidacion.nextval into :new.idliquidacion from dual;
end;

miércoles, 6 de junio de 2012

Package Luis en clases


create or replace package pkgLuis is
  function cantidadEmpleados(pDepto IN employees.department_id%type) return number;
end pkgLuis;

create or replace
package body pkgLuis is
  FUNCTION cantidadEmpleados(
 pDepto IN employees.department_id%type
)
  RETURN NUMBER AS
 
  totalEmpleados integer;
  BEGIN
    select count(*) into totalEmpleados
    from Employees
    where department_id = 90;
    RETURN totalEmpleados;
  END cantidadEmpleados;
end pkgLuis;

select * from employees where department_id = 90;

set serveroutput on
begin
  dbms_output.put_line('Cantidad :'||pkgLuis.cantidadEmpleados(90));
end;
set serveroutput on
declare
  cantidad number;
begin
  select pkgLuis.cantidadEmpleados(90) into cantidad from dual;
  dbms_output.put_line('Cantidad :'||cantidad);
end;

select pkgLuis.cantidadEmpleados(90) from dual;

package - 06-06-2012


create or replace package pkg_uno is
function CUENTAEMPLEADOS (pDepto IN employees.department_id%type) return number;
end pkg_uno;

create or replace package body pkg_uno is
 FUNCTION CUENTAEMPLEADOS (pDepto IN employees.department_id%type)

 RETURN NUMBER AS
totalEmpleados integer;
BEGIN
  select count(*) into totalEmpleados
  from Employees
  where department_id = pDepto;
  RETURN totalEmpleados;
END CUENTAEMPLEADOS;
end pkg_uno;



    select pkg_uno.CUENTAEMPLEADOS(90) from dual;

Clase Trigger


create table Autor(
idAutor int,
nombre varchar(30),
constraint Pk_idAutor primary key (idAutor)  
);

begin
insert into Autor values(7);
insert into Autor VALUES(1);
insert into Autor VALUES(2);
insert into Autor VALUES(3);
insert into Autor VALUES(4);
insert into Autor VALUES(5);
end;

create table Libro(
IdLibro integer,
IdAutor integer,
constraint Pk_idLibro primary key (IdLibro),
constraint Fk_IdAutor foreign key (IdAutor) references Autor
);

begin
insert into Libro values(1, 1);
insert into Libro values(2, 1);
insert into Libro values(3, 2);
insert into Libro values(4, 3);
end;

create sequence seqAutor
start with 1 increment by 10
nocache
nocycle;

create or replace trigger MiPrimerTrigger
after delete on Autor
for each row
begin
  DELETE FROM Libro
  WHERE IdAutor = :old.IdAutor;
end;

begin
delete from Autor
where idAutor = 1;
end;

select * from Libro;

sábado, 2 de junio de 2012

Trigger


create or replace trigger IngresoDetalleCompra
after insert on DetalleCompra
for each row
begin
  update Producto set StockActual = StockActual + :new.Cantidad where idProducto = :new.idProducto;
end;

sábado, 19 de mayo de 2012

Actualizar departamento empleado con procedure


Procedimiento almacenado que alctualiza la tabla empleados

create or replace procedure actualizarDepartamento(pempleadoId in char,pdepartamentoId in char)
is
total integer;
begin
  total := 0;
  select count(*) into total from empleado;
  if total > 0 then
    update empleado set numoficina = pdepartamentoId where numempleado = pempleadoId;
  end if;
end actualizarDepartamento;


begin
  actualizarDepartamento('SL21','B006');
  dbms_output.put_line('Actualizado');
end;

Repaso funciones


Funcion para contar cuantos empleados por departamento hay.


create or replace function empleadosPorDepartamento(departamento number)
return number
is
resultado number;
begin
  select count(*) as contador into resultado  from employees where department_id = departamento;
  return resultado;
end empleadosPorDepartamento;


set serveroutput on
declare
  resultado number;
begin
  resultado := empleadosPorDepartamento(50);
  dbms_output.put_line('El resultado es: '||resultado);
end;

Forma para mostrar los datos y la cantidad de empleados llamando a la funcion creada anteriormente.

select employee_id,department_id,empleadosPorDepartamento(50) from employees

miércoles, 16 de mayo de 2012

Estructura de funciones

Estructura de funciones

create or replace function miFuncion (pnumero in number)
return number
is
  resultado number;
begin
  resultado := pnumero*2;
  return(resultado);
end;

set serveroutput on
declare
  numero number := 1;
begin
  numero := miFuncion(numero);
  dbms_output.put_line('Numero: '||numero);
end;


Funcion par o impar


create or replace function miFuncion (pnumero number)
return varchar2
is
  msg varchar2(20);
begin
  if mod(pnumero,2) = 0  then
    msg := 'Par';
  else
    msg := 'Impar';
  end if;
  return(msg);
end;
show err function miFuncion;
set serveroutput on
declare
  numero number := 1;
  mensaje varchar2(20);
begin
  mensaje := miFuncion(numero);
  dbms_output.put_line('Numero: '||mensaje);
end;

Procedimientos almacenados Estructura


Estructura del procedimiento con out
create or replace procedure miProcedimiento(ptexto out varchar2) is
begin
  ptexto := 'Este es mi mensaje del procedimiento';
end miProcedimiento;

Llamando al procedimiento


declare
  vtexto varchar2(50) := 'Hola a todos';
begin
  miProcedimiento(vtexto);
  dbms_output.put_line('Mi mensaje : '||vtexto);
end;

Procedimiento con in y out


create or replace procedure miProcedimiento(ptexto in out varchar2) is
begin
  dbms_output.put_line('Mi mensaje : '||ptexto);
  ptexto := 'Este es mi mensaje del procedimiento';
end miProcedimiento;

set serveroutput on
declare
  vtexto varchar2(50) := 'Hola a todos';
begin
  miProcedimiento(vtexto);
  dbms_output.put_line('Mi mensaje : '||vtexto);
end;

Procedimiento con solo salida


create or replace procedure miProcedimiento(ptexto out varchar2) is
begin
  dbms_output.put_line('Mi mensaje : '||ptexto);
  ptexto := 'Este es mi mensaje del procedimiento';
end miProcedimiento;

set serveroutput on
declare
  vtexto varchar2(50) := 'Hola a todos';
begin
  miProcedimiento(vtexto);
  dbms_output.put_line('Mi mensaje : '||vtexto);
end;

Procedimiento concatenado


create or replace procedure Concatenar(ptexto1 varchar2,ptexto2 varchar2) is
begin
  dbms_output.put_line(ptexto1||' '||ptexto2);
end Concatenar;

set serveroutput on
declare
  vtexto varchar2(50) := 'Hola a todos';
begin
  Concatenar('Buenos','Dias');
  Concatenar(ptexto2=>'Buenos',ptexto1=>'Dias');
end;




miércoles, 9 de mayo de 2012

Solucion Control Cursores



set serveroutput on

Problema 1

declare
  cursor Uno is select first_name,last_name,salary from employees order by last_name;
  recEmpleado Uno%rowtype;
  contador integer := 0;
begin
  open Uno;  
      loop
        fetch Uno into recEmpleado;
        exit when Uno%NOTFOUND;
        contador := contador + 1;
        dbms_output.put_line('Nombre: '||recEmpleado.first_name||' Apellido: '||recEmpleado.last_name||' Salario: '||recEmpleado.salary);
      end loop;
  close Uno;
  dbms_output.put_line('Total de empleados: '||contador);
  exception
    when others then
    dbms_output.put_line('Error');
end;

Problema 2






set serveroutput on
declare
  cursor Uno is select first_name,last_name,salary,hire_date from employees order by last_name;
  recEmpleado Uno%rowtype;
  contador integer := 0;
  edad integer;
  msg varchar2(10);
begin
  open Uno;
  fetch Uno into recEmpleado;
      while Uno%FOUND loop      
        contador := contador + 1;
        edad := round((sysdate - recEmpleado.hire_date)/365);
        if edad <= 25 then
          msg := 'Junior';
        elsif edad > 25 and edad < 40 then
          msg := 'Senior';
        else
          msg := 'Master';
        end if;
        fetch Uno into recEmpleado;
        dbms_output.put_line('Nombre: '||recEmpleado.first_name||' Apellido: '||recEmpleado.last_name||' Salario: '||recEmpleado.salary||' Mensaje : '||msg);
      end loop;
  close Uno;
  dbms_output.put_line('Total empleados '||contador);
  exception
    when others then
    dbms_output.put_line('Error');
end;





desc departments
set serveroutput on
declare
  cursor Uno is
  select employees.first_name,employees.last_name,employees.salary,departments.DEPARTMENT_NAME
  from employees, departments
  where departments.department_id = employees.department_id
  order by DEPARTMENT_NAME;
  recEmpleado Uno%rowtype;
  contador integer := 0;
  edad integer;
  msg varchar2(10);
begin
  for recEmp in Uno loop
   dbms_output.put_line('Nombre:  '||recEmp.first_name);
  end loop;

  exception
    when others then
    dbms_output.put_line('Error');
end;




viernes, 4 de mayo de 2012

Tarea 3 - Cursores Explicitos en PL/SQL


Declaración de cursores explícitos.

1.    Declaración del cursor: lo tenemos que declarar en la zona de declaraciones, con el siguiente formato: CURSOR <nombrecursor> IS <sentencia SELECT>;
2.    Apertura del cursor: Deberá colocarse en la zona de instrucciones, con el siguiente formato: OPEN <nombrecursor>; 
Al hacerlo se ejecuta automáticamente la sentencia select y sus resultados se almacenan en las estructuras internas de memoria manejadas por el cursor.
3.    Recogida de información: Para recuperar la información anteriormente guardada en las estructuras de memoria interna tenemos que usar el siguiente formato: 
FETCH <nombrecursor> INTO {<variable> | <listavariables>}; 
Si tenemos una única variable que recoge los datos de todas las columnas, el formato de la variable seria el siguiente:
  <variable> <nombrecursor>%ROWTYPE; 
Si tenemos una lista de variables, cada una recogerá la columna correspondiente de la cláusula select, por lo que serán del mismo tipo que las columnas.
4.    - Cierre del cursor:  CLOSE <nombrecursor>;
OPEN nombre_cursor;
    LOOP
        FETCH nombre_cursor INTO lista_variables;
        EXIT WHEN nombre_cursor%NOTFOUND;
        /* Procesamiento de los registros recuperados */
    END LOOP;
CLOSE nombre_cursor;

Atributos del cursor


Detalles de la situación del cursor tenemos 4 atributos:
  • %FOUND: devuelve verdadero di el último FETCH ha recuperado algún valor; en caso contrario devuelve falso; si el cursor no está abierto nos devuelve error.
  • %NOTFOUND: hace justamente lo contrario al anterior.
  • %ROWCOUNT: nos devuelve el número de filas recuperadas hasta el momento.
  • %ISOPEN: devuelve verdadero si el cursor está abierto.

Manejo del cursor 

Bucle LOOP con una sentencia EXIT condicionada

DECLARE

  cursor CursorLoop IS SELECT employee_id,first_name,email FROM employees;

  vid employees.employee_id%TYPE;

  vfirst_name employees.first_name%TYPE;

  vemail employees.email%TYPE;

BEGIN

  OPEN CursorLoop;

    LOOP

      FETCH CursorLoop INTO vid,vfirst_name,vemail;

      EXIT WHEN CursorLoop %NOTFOUND;

      dbms_output.put_line('Id: '||vid||' |Nombre: '||vfirst_name||' |Email:'||vemail);

    END LOOP;

  CLOSE CursorLoop;

  EXCEPTION

    WHEN OTHERS THEN

      dbms_output.put_line('Error: '||SQLERRM);

END;

Bucle WHILE LOOP 

DECLARE
  cursor CursorWhile IS SELECT employee_id,first_name,email FROM employees;
  vid employees.employee_id%TYPE;
  vfirst_name employees.first_name%TYPE;
  vemail employees.email%TYPE;
BEGIN
  OPEN CursorWhile;
    FETCH CursorWhile INTO vid,vfirst_name,vemail;
    WHILE CursorWhile%FOUND
      LOOP       
        dbms_output.put_line('Id: '||vid||' |Nombre: '||vfirst_name||' |Email:'||vemail);
        FETCH CursorWhile INTO vid,vfirst_name,vemail;
      END LOOP;
  CLOSE CursorWhile;
  EXCEPTION
    WHEN OTHERS THEN
      dbms_output.put_line('Error: '||SQLERRM);
END;

Bucle FOR LOOP 

DECLARE
  cursor CursorFor IS SELECT employee_id,first_name,email FROM employees;
BEGIN
  FOR employee_rec IN CursorFor
    LOOP
      dbms_output.put_line('Id: '||employee_rec.employee_id||' |Nombre: '||employee_rec.first_name||' |Email: '||employee_rec.email);
    END LOOP;
  EXCEPTION
    WHEN OTHERS THEN
      dbms_output.put_line('Error: '||SQLERRM);
END;

miércoles, 2 de mayo de 2012

Cursor Par - Impar 2


set serveroutput on
declare
  cursor LuisUno is select employee_id,first_name,last_name,email from employees;
  vid employees.employee_id%TYPE;
  vnombre employees.first_name%TYPE;
  vapellido employees.last_name%TYPE;
  vemail employees.email%TYPE;
  vtipo varchar2(5);
begin
  open LuisUno;
    loop
      fetch LuisUno into vid,vnombre,vapellido,vemail;
      exit when LuisUno%NOTFOUND;
        if mod(vid,2)=0 then
          vtipo := 'Par';
        else
          vtipo := 'Impar';
        end if;
        update employees set tipoid = vtipo where employee_id = vid;
        --dbms_output.put_line('Id: '||vid||' | Nombre: '||vnombre||' |Apellido: '||vapellido||' |Email: '||vemail);
    end loop;
  close LuisUno;
  exception
    when others then
     dbms_output.put_line('Error: '||SQLERRM);
end;

martes, 1 de mayo de 2012

Cursores y procedure


set serveroutput on
DECLARE
 vPropietario all_tables.owner%TYPE;
 vNombreTabla varchar2(40);
    vNombreColumna varchar2(100);

    /* Primer cursor */
    cursor obtieneTablas(pPropietario all_tables.owner%TYPE) is
    select distinct t.owner, t.table_name
    from all_tables t
    where t.owner = pPropietario;

    /* Segundo cursor */
    cursor obtieneColumnas is
    select distinct c.column_name
    from all_tab_columns c
    where c.owner = vPropietario
    and c.table_name = vNombreTabla;
begin
   open obtieneTablas('SYSTEM');
dbms_output.put_line('Abriendo Cursor - obtieneTablas');
loop fetch obtieneTablas into vPropietario, vNombreTabla;
   exit when obtieneTablas%NOTFOUND;
   dbms_output.put_line('Tabla : '||vPropietario||'.'||vNombreTabla);
       open obtieneColumnas;
       loop fetch obtieneColumnas into vNombreColumna;
     exit when obtieneColumnas%NOTFOUND;
         dbms_output.put_line('='||vNombreTabla||'.'||vNombreColumna);
       end loop;
       close obtieneColumnas;
   end loop;
close obtieneTablas;
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20001,'Se ha detectado un error - '||SQLCODE||' -ERROR- '||SQLERRM);
end;

CREATE OR REPLACE PROCEDURE CATUSER(pOwner all_tables.owner%type) AS

    vPropietario all_tables.owner%TYPE;
 vNombreTabla varchar2(40);
    vNombreColumna varchar2(100);

    /* Primer cursor */
    cursor obtieneTablas(pPropietario all_tables.owner%TYPE) is
    select distinct t.owner, t.table_name
    from all_tables t
    where t.owner = pPropietario;

    /* Segundo cursor */
    cursor obtieneColumnas is
    select distinct c.column_name
    from all_tab_columns c
    where c.owner = vPropietario
    and c.table_name = vNombreTabla;
begin
   open obtieneTablas(pOwner);
dbms_output.put_line('Abriendo Cursor - obtieneTablas');
loop fetch obtieneTablas into vPropietario, vNombreTabla;
   exit when obtieneTablas%NOTFOUND;
   dbms_output.put_line('Tabla : '||vPropietario||'.'||vNombreTabla);
       open obtieneColumnas;
       loop fetch obtieneColumnas into vNombreColumna;
     exit when obtieneColumnas%NOTFOUND;
         dbms_output.put_line('='||vNombreTabla||'.'||vNombreColumna);
       end loop;
       close obtieneColumnas;
   end loop;
close obtieneTablas;
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20001,'Se ha detectado un error - '||SQLCODE||' -ERROR- '||SQLERRM);
END CATUSER;

set serveroutput on
begin
  CATUSER('SYSTEM');
end catuser;

Cursor Par - Impar

set serveroutput on
declare
  cursor ParImpar is select employees.employee_id,employees.tipoid from employees;
  vid employees.employee_id%type;
  vtipo employees.tipoid%type;
begin
  open ParImpar;
    loop
      fetch ParImpar into vid,vtipo;
      exit when ParImpar%NOTFOUND;      
      if mod(vid,2) = 0 then
        dbms_output.put_line('Id: '||vid||' Par');
        update employees SET tipoid = 'Par' where employee_id=vid;
      else
        dbms_output.put_line('Id: '||vid||' Impar');
        update employees SET tipoid = 'Impar' where employee_id=vid;
      end if;
    end loop;
  close ParImpar;
end;

jueves, 19 de abril de 2012

Cursores


set serveroutput on;
declare
  primo char(2) := 'si';
  num integer;
begin
  num := to_number('&IngreseNumero');
  dbms_output.put_line('El umero ingresado es: '||num);
  for  i in 2..num - 1
    loop
    if mod(num,i)=0 then
      primo := 'no';
    end if;
  end loop;
  if primo = 'si' then
    dbms_output.put_line('El numero es primo');
  else
    dbms_output.put_line('El numero no es primo');
  end if;
  exception
    when VALUE_ERROR then
    dbms_output.put_line('Error el dato ingresado tiene que ser un numero');
end;


set serveroutput on
declare
  cursor mirPrimerCursor is select employee_id,first_name, from employees;
  vid employees.employee_id%TYPE;
  vnombre employees.first_name%TYPE;
begin
  open  mirPrimerCursor;
  loop
    fetch mirPrimerCursor into vid,vnombre;
    exit when mirPrimerCursor%NOTFOUND;
    dbms_output.put_line('ID: '||vid||' Nombre: '||vnombre);
  end loop;
  close mirPrimerCursor;
end;

set serveroutput on
declare
  cursor mirPrimerCursor is select employee_id,first_name,hire_date from employees ORDER BY hire_date DESC;
  vid employees.employee_id%TYPE;
  vnombre employees.first_name%TYPE;
  vFechaContrato employees.hire_date%TYPE;
  antiguedad integer;
begin
  open  mirPrimerCursor;
  loop
    fetch mirPrimerCursor into vid,vnombre,vFechaContrato;
    exit when mirPrimerCursor%NOTFOUND;
    antiguedad := trunc((sysdate - vFechaContrato)/365);
    if antiguedad= 12 then
      dbms_output.put_line('ID: '||vid||' | Nombre: '||vnombre||' | Antiguedad: '||antiguedad);
    end if;
  end loop;
  close mirPrimerCursor;
end;


set serveroutput on
declare
  cursor miSegundoCursorTabla is select table_name from all_tables where owner = 'HR';
  cursor miSegundoCursorColumnas is select owner,table_name,column_name from all_tab_columns where owner = 'HR';
  vNombreTabla all_tables.table_name%TYPE;
  vOwner all_tab_columns.owner%TYPE;
  vTablaNombre all_tab_columns.table_name%TYPE;
  vNombreColumna all_tab_columns.column_name%TYPE;
begin
  open  miSegundoCursorTabla;
  loop
    fetch miSegundoCursorTabla into vNombreTabla;
    exit when miSegundoCursorTabla%NOTFOUND;
      open  miSegundoCursorColumnas;
      loop
        fetch miSegundoCursorColumnas into vOwner,vTablaNombre,vNombreColumna;
        exit when miSegundoCursorColumnas%NOTFOUND;
        dbms_output.put_line('Tabla: '||vNombreTabla||' Columna: '||vNombreColumna);
      end loop;
      close miSegundoCursorColumnas;
  end loop;
  close miSegundoCursorTabla;
end;

miércoles, 18 de abril de 2012

Clase Viernes 14 de abril


create table EMPLEADO (
NUMEMPLEADO CHAR(4) not null,
NOMBRE CHAR(30),
APELLIDO CHAR(30),
CARGO CHAR(35),
SEXO CHAR(1),
FECHNAC DATE,
SALARIO FLOAT,
NUMOFICINA CHAR(4),
constraint PK_EMPLEADO primary key (NUMEMPLEADO)
);

insert into empleado values('SL21','Jhon','White','Gerente','M','01/10/45',300000,'B005');
insert into empleado values('SG37','Peter','Denver','Asistente','M','10/11/60',120000,'B006');
insert into empleado values('SG14','David','Ford','Supervisor','M','09/09/58',180000,'B003');
insert into empleado values('SA9','Mary','Lee','Asistente','F','17/09/59',90000,'B007');
insert into empleado values('SG5','Susan','Sarandon','Gerente','F','21/03/60',240000,'B003');
insert into empleado values('SL41','Julie','Roberts','Asistente','F','13/06/63',90000,'B005');
insert into empleado values('SL22','Juan','Blanco','Gerente','M','01/10/44',300000,'B005');
insert into empleado values('SG36','Luis','Jara','Asistente','M','10/11/61',120000,'B003');
insert into empleado values('SG13','David','Gates','Supervisor','M','09/09/58',180000,'B003');
insert into empleado values('SA8','Maria','Bombal','Asistente','F','17/09/59',90000,'B007');
insert into empleado values('SG4','Susana','Sarandons','Gerente','F','21/03/60',240000,'B003');
insert into empleado values('SL40','James','Bond','Asistente','F','13/06/63',90000,'B005');
insert into empleado values('SL50','Juan','Perez','Vendedor','M','13/06/63',151000,'B015');
insert into empleado values('SL60','Jaime','Soto','Vendedor','M','14/06/83',350000,'B115');
insert into empleado values('SL70','Julia','Berne','Vendedor','F','23/01/53',200000,'B215');
insert into empleado values('SL55','Jorge','Fernandez','Vendedor','M','13/06/63',151000,'B015');
insert into empleado values('SL65','Jose','Isla','Vendedor','M','14/06/83',350000,'B115');

select *
from empleado

select nombre, fechnac, (sysdate -fechnac) / 365 as edad
from empleado
where numempleado = 'SL40'

select trim(nombre), fechnac, trunc((sysdate -fechnac) / 365) as edad
from empleado
where upper(numempleado) = upper('SL40')

declare
  vnumEmpleadoTmp char(4);
  vnumEmpleado char(4);
  vnombre varchar(30);
  vfecha date;
  vedad integer(2);


set serveroutput on
declare
  vnumEmpleado empleado.numempleado%TYPE;
  vnombre empleado.nombre%TYPE;
  vfecha empleado.fechnac%TYPE;
  vedad integer;
begin
  vnumEmpleado := '&IngreseNumeroEmpleado';
  select trim(nombre), fechnac, trunc((sysdate -fechnac) / 365) as edad into vnombre,vfecha,vedad
  from empleado
  where upper(numempleado) = upper(vnumEmpleado);
  dbms_output.put_line('Nombre: '||trim(vnombre)||' Fecha Nacimiento: '||vfecha||' Edad: '||vedad);
end;

begin
  vnumEmpleado := '&vnumEmpleadoTmp';
  select trim(nombre), fechnac, trunc((sysdate -fechnac) / 365) as edad into vnombre,vfecha,vedad
  from empleado
  where upper(numempleado) = upper(vnumEmpleado);
  dbms_output.put_line('Nombre: '||vnombre||' Fecha Nacimiento: '||vfecha||' Edad: '||vedad);
end;

set serveroutput on
declare
  vnumEmpleado empleado.numempleado%TYPE;
  vnombre empleado.nombre%TYPE;
  vfecha empleado.fechnac%TYPE;
  vedad integer(2);
begin
  vnumEmpleado := '&IngreseNumeroEmpleado';
  select trim(nombre), fechnac, trunc((sysdate -fechnac) / 365) as edad into vnombre,vfecha,vedad
  from empleado
  where upper(numempleado) = upper(vnumEmpleado);
  dbms_output.put_line('Nombre: '||vnombre||' Fecha Nacimiento: '||vfecha||' Edad: '||vedad);
end;


set serveroutput on
declare
  vnumEmpleado empleado.numempleado%TYPE;
  vnombre empleado.nombre%TYPE;
  vfecha empleado.fechnac%TYPE;
  vedad integer;
begin
  vnumEmpleado := '&IngreseNumeroEmpleado';
  select trim(nombre), fechnac, trunc((sysdate -fechnac) / 365) as edad into vnombre,vfecha,vedad
  from empleado
  where upper(numempleado) = upper(vnumEmpleado);
  dbms_output.put_line('Nombre: '||trim(vnombre)||' Fecha Nacimiento: '||vfecha||' Edad: '||vedad);
end;


set serveroutput on
declare
  vnumEmpleado empleado.numempleado%TYPE;
  vnombre empleado.nombre%TYPE;
  vfecha empleado.fechnac%TYPE;
  vedad integer;
begin
  vnumEmpleado := '&IngreseNumeroEmpleado';
  select trim(nombre), fechnac, trunc((sysdate -fechnac) / 365) as edad into vnombre,vfecha,vedad
  from empleado
  where upper(numempleado) = upper(vnumEmpleado);
  dbms_output.put_line('Nombre: '||trim(vnombre)||' Fecha Nacimiento: '||vfecha||' Edad: '||vedad);
end;

miércoles, 11 de abril de 2012

Tarea 2 - Tipos de datos

1) Tipos de datos de PL/SQL versión 10g, 


VARCHAR2 : Cadena de caracteres de longitud variable con un máximo de size bytes de longitud. Debe especificar el tamaño


NVARCHAR2 : De longitud variable de cadena nacional, el conjunto de caracteres que tiene un máximo tamaño de bytes de longitud. 
Debe especificar el tamaño

CHAR : Los datos de caracteres de longitud fija de bytes de tamaño de longitud. Esto se debe utilizar para los datos de longitud fija. 

Nchar : De caracteres de longitud fija nacional de conjunto de datos de tamaño bytes de longitud.Esto se debe utilizar para los datos de longitud fija.

NUMBER : Número de tener la precisión p y escala s.

PLS_INTEGER : enteros con signo 
Los valores PLS_INTEGER menos espacio de almacenamiento y ofrecer un mejor rendimiento que los valores numéricos.
Así que utilice PLS_INTEGER donde se puede


BINARY_INTEGER : enteros con signo (mayor versión más lenta de PLS_INTEGER)

DATE Intervalo de fechas válido

TIMESTAMP : el número de dígitos en la parte fraccionaria del campo de fecha y hora SEGUNDO.

INTERVAL YEAR : Tiempo en años y meses, donde year_precision es el número de dígitos en el campo de fecha y hora AÑO.

INTERVAL DAY: El tiempo en días, horas, minutos y segundos. 
day_precision es el número máximo de dígitos en el 'DIA'  fractional_seconds_precisiones el número máximo de decimales en el segundo campo.

RAW : Datos binarios de bytes de tamaño de longitud.
Debe especificar el tamaño de un valor RAW.

ROWID Cadena hexadecimal que representa la dirección única de una fila en su tabla.
(Sobre todo para los valores devueltos por el ROWID pseudocolumna.)

UROWID : Cadena Hex en representación de la dirección lógica de una fila de una tabla organizada por índices

MLSLABEL : Formato binario de un sistema de tipo de datos de funcionamiento label

CLOB : Carácter y objeto grande

NCLOB : Character Large Object Nacional

BLOB: Binary Large Object

BFILE: puntero a un archivo binario en el disco

XMLType: De datos XML



3)  Funciones de conversión entre tipos de datos. 


Aqui algunos ejemplos para adicionar y sustraer días y meses y encontrar la diferencia entre
fechas en Oracle. Estos ejemplos toman el resultado de la tabla "dual. La tabla Dual es una
tabla virtual que existe en todas las Bases de datos Oracle. Muchas veces hemos usado la
consulta SELECT sysdate FROM dual; la cual simplemente nos retorna la fecha y hora
actual.


Ajustar Dias, Semanas, Horas y minutos


Para adicionar y sustraer días a una fecha, simplemente usamos los signos + o -
respectivamente. Algunos ejemplos:


SELECT sysdate + 7 FROM dual;


salida:
SYSDATE+7
18/04/12
------------------------------------------------------------------------
SELECT sysdate - 30 FROM dual;


salida
SYSDATE-30
12/03/12
--------------------------------------------------------------------------------
SQL> SELECT to_char(sysdate - 14, 'MM/DD/YYYY HH:MI AM') FROM dual;


salida
TO_CHAR(SYSDATE-14,'MM/DD/YYYYHH:MIAM')
03/28/2012 12:44 AM


--------------------------------------------------------------------------------
En el primer ejemplo, vemos que la consulta retorna la fecha siete días apartir de hoy. La segunda
retorna la fecha de hace 30 días. En la tercera, se ha usado la función de conversión to_char,
para ver las horas y minutos. Esto nos muestra que mientras la fecha, ha cambiado, el tiempo no
cambia.
Los ejemplos primero y tercero ademas muestran como el adicionar días, puede ser útil para
computar semanas. Si ud desea simplificar el computo de semanas, podías usar querys de este
tipo:
SQL> SELECT sysdate + (7 * 3) FROM dual;


salida
SYSDATE+(7*3) 02/05/12
--------------------------------------------------------------------------------


Para trabajar con meses y años (cada uno de los cuales puede variar en número de días)
Oracle ofrece la función numtoyminterval. Esta trabaja en forma similar a la función
numtodsinterval tomando un numero y una cadena string. Las opciones válidas para esta
función son: ‘YEAR’ or ‘MONTH’.


SELECT to_char(sysdate + numtoyminterval(5, 'MONTH'), 'MM/DD/YYYY') FROM dual;


salida
TO_CHAR(SYSDATE+NUMTOYMINTERVAL(5,'MONTH'),'MM/DD/YYYY')
09/11/2012
------------------------------------------------------------------------------------------


SELECT to_char(sysdate + numtoyminterval(2, 'YEAR'), 'MM/DD/YYYY') FROM dual;


salida
TO_CHAR(SYSDATE+NUMTOYMINTERVAL(2,'YEAR'),'MM/DD/YYYY')
04/11/2014
------------------------------------------------------------------------------------------


Comparar fechas


Suponga que deseamos comparar algunas fechas y encontrar el número de días entre ellas. Para
ver este resultado en días, simplemente basta con usar el operador -, de la siguiente manera:


SELECT TO_DATE('11/04/2006', 'MM/DD/YYYY') - sysdate FROM dual;


salida
TO_DATE('11/04/2006','MM/DD/YYYY')-SYSDATE
-1985,0386111111111111
------------------------------------------------------------------------------------------


El resultado es expresado en días, incluyendo horas fracción de horas. Por supuesto si se desea
semanas, se podría dividir el resultado entre 7. De manera similar, si se desea obtener horas, se
debería multiplicar por 24, pero si se desea meses, entonces se podría usar la función
months_between.


SELECT months_between(sysdate, to_date('01/01/2006', 'MM/DD/YYYY')) FROM dual;


salida
MONTHS_BETWEEN(SYSDATE,TO_DATE('01/01/2006','MM/DD/YYYY'))
75,323867607526881
------------------------------------------------------------------------------------------


Retornar la mayor y menor fecha dentro de un conjunto


Las funciones greatest y least pueden ser usadas en fechas para retornar la menor o la mayor
fecha.


SELECT greatest(sysdate,sysdate+1, to_date('09/11/2005','MM/DD/YYYY'), to_date('12/25/
2005','MM/DD/YYYY'))FROM dual;


salida


GREATEST(SYSDATE,SYSDATE+1,TO_DATE('09/11/2005','MM/DD/YYYY'),TO_DATE('12/25/2005','MM/DD/YYYY'))


12/04/12


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


SELECT least(sysdate,sysdate+1,to_date('09/11/2005','MM/DD/YYYY'), to_date('12/25/
2005','MM/DD/YYYY'))FROM dual;


salida


LEAST(SYSDATE,SYSDATE+1,TO_DATE('09/11/2005','MM/DD/YYYY'),TO_DATE('12/25/2005','MM/DD/YYYY'))


11/09/05
------------------------------------------------------------------------------------------


Cual es el último día de este mes?


La función last_day retorna el último día del mes pasado como parámetro.


select last_day(sysdate) from dual;


salida
LAST_DAY(SYSDATE) 30/04/12


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


Siguiente día de la semana despues de una fecha


La función next_day retorna la fecha de la próxima ocurrencia de un día de la
semana(’Monday’, ‘Tuesday’, etc.) después de una fecha dada. ejemplo: consultar la fecha del
próximo domingo:


select next_day(sysdate,'Domingo') from dual;


salida
NEXT_DAY(SYSDATE,'DOMINGO') 15/04/12


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


3)  Funciones para manipular fechas



TO_CHAR

Con esta función es posible convertir números a caracteres, otra versión de la misma función nos permite hacer lo mismo con los tipos de dato asociados con fechas y tiempo. De igual manera que con los números, TO_CHAR ofrecen un gran número de posibilidades para formatear fechas y que estas aparezcan tal y como queramos.

Ejemplo:

Convertir números:

Begin
Dbms_output.put_line (to_char (sysdate));

Dbms_output.put_line (to_char (systimestamp));
End;

Resultado:

11/04/12
11-ABR-12 01.10.57,711000000 AM -03:00



Devuelve dia de la "de"semana y el nombre del mes

Begin
dbms_output.put_line(to_char (sysdate,'day,dd "de" Month "de" YYYY' ));
end;

Resultado
miércoles,11 de Abril      de 2012

Statement processed.

sábado, 31 de marzo de 2012

Clase 30-03-12 pl/sql - exception


http://www.devjoker.com/contenidos/Tutorial-PLSQL/48/Excepciones-en-PLSQL.aspx

set serveroutput on

declare
  nombre varchar2(25);
begin
  dbms_output.put_line('Hola ' ||'&nombre');
end;

--

set serveroutput on

declare
  diaSemana integer;
  fechaIngresada char(2);
begin
  diasemana := to_number('&fechaIngresada');
  dbms_output.put_line('El numero ingresado es: ' || diaSemana);
EXCEPTION
  when VALUE_ERROR then
  dbms_output.put_line('Error el numero ingresado tiene que ser un numero');
end;

--

set serveroutput on

declare
  diaSemana integer;
  IngreseValorNumerico char(2);
begin
  diasemana := to_number('&IngreseValorNumerico');
  dbms_output.put_line('El numero ingresado es: ' || diaSemana);
EXCEPTION
  when VALUE_ERROR then
  dbms_output.put_line('Error el numero ingresado tiene que ser un numero');
  when others then
    rollback;
    raise;
end;

--

set serveroutput on

declare
  diaSemana integer;
  IngreseValorNumerico char(2);
begin
  diasemana := to_number('&IngreseValorNumerico');
  if diasemana > 0 and diasemana < 8 then
    dbms_output.put_line('El numero ingresado es: ' || diaSemana);
  else
    dbms_output.put_line('Error: numero ingresado fuera de rango.');
  end if;
EXCEPTION
  when VALUE_ERROR then
  dbms_output.put_line('Error: el numero ingresado tiene que ser un numero');
  when others then
    rollback;
    raise;
end;

--
set serveroutput on

declare
  numero integer;
  suma integer := 0;
  IngreseValorNumerico char(2);
begin
  numero := to_number('&IngreseValorNumerico');
  for i IN 1..numero
  loop
    suma := suma + i;
  end loop;
  dbms_output.put_line('Valor ingresado: '||numero||' la suma es:' || suma);
EXCEPTION
  when VALUE_ERROR then
  dbms_output.put_line('Error: el numero ingresado tiene que ser un numero');
  when others then
    rollback;
    raise;
end;

--

set serveroutput on

declare
  numero integer;
  suma integer := 0;
  i integer :=0;
  IngreseValorNumerico char(2);
begin
  numero := to_number('&IngreseValorNumerico');
  while i <= numero loop
    suma := suma + 1;
    i++;
    end loop;
  dbms_output.put_line('Valor ingresado: '||numero||' la suma es:' || suma);
EXCEPTION
  when VALUE_ERROR then
  dbms_output.put_line('Error: el numero ingresado tiene que ser un numero');
  when others then
    rollback;
    raise;

Buenas practicas PL / SQL


Pauta para variables y estructuras de datos


Declarar, definir el tipo, inicializar y asignar valor por defecto a las estructura de datos antes de trabajar con ellas
PL/SQL es un lenguaje fuertemente tipificado. Esto significa que antes de trabajar con cualquier tipo de estructura de datos, es necesario declarar las mismas, definir el tipo y opcionalmente inicializarlas o asignarles un valor por defecto. Las declaraciones deben realizarse en la sección de declaración de variables de un bloque anónimo, procedimiento, función o paquete.

En las declaraciones que se relacionan a tablas y columnas utilizar la cláusula
%TYPE y %ROWTYPE.

Esta forma permite al código adaptarse a cambios de estructuras de datos yautodocumentarse, ya que al leer el código se puede saber a qué tipo de dato hace referencia. Para el tipo VARCHAR2, Oracle separa la memoria necesaria según la longitud definida. Es muy común ‘Hardcodear’ la longitud del mismo a su máxima tamaño para evitar problemas, a costo de utilizar más memoria de la necesaria. Aquí nuevamente se hace conveniente el uso de%TYPE o %SUBTYPE para un mejor aprovechamiento de los recursos de memoria.

En los datos numéricos, definir la precisión.

Oracle soporta hasta 38 dígitos de precisión en los tipos NUMBER .
Si no se define en forma correcta se está derrochando memoria.

Siempre que sea posible, utilizar el tipo de dato RECORD para manejar estructuras.

La declaración de variables individuales o dispersión de datos, muchas veces, complica  la lectura del código. La agrupación de los mismos bajo estructuras, facilita la administración y el mantenimiento del código.

Los errores de un código solo pueden ser capturados en la sección ejecutable de un bloque. Si la inicialización de una variable en la sección de declaración falla, el error no se puede manejado. Para ello hay que asegurar que la inicialización lógica no falle y esto se asegura haciendo las inicializaciones al comienzo de la sección de ejecución. Si se produce un error, se puede capturar el error y decidir su tratamiento.

Reemplazar expresiones complejas con variables booleanas y funciones.

Las expresiones booleanas se pueden evaluar con tres valores: TRUE, FALSE o NULL
.Se pueden usar variables de este tipo para ocultar expresiones complejas. Como consecuencia se puede leer más fácilmente el código y es más simple su mantenimiento.
Tener cuidado con las conversiones implícitas de tipos de datos.

Si bien es sabido que PL/SQL maneja las conversiones implícitas, existen al menos dos grandes problemas con esto. Las conversiones no son intuitivas, a veces se realizan de formas no esperadas y ocurren problemas, especialmente dentro de sentencias SQL. Las reglas de conversión no están bajo el control de desarrollador. Pueden cambiar con el upgrade a una versión de Oracle o con el cambio de parámetros como NLS_DATE_FORMAT.


  
Se puede convertir con el uso explícito de funciones como son:
TO_DATE, TO_CHAR,TO_NUMBER CAST.

Pauta para declaración y uso de variables de paquetes.


Agrupar los tipos de datos, evitar su exposición y controlar su manipulación.

Los paquetes requieren ciertos tipos de recaudos en la forma de declaración y uso de variables. Agrupar los tipos de datos, evitar su exposición y controlar su manipulación permiten obtener las ventajas del uso de los mismos.
Definir las constantes que son referenciadas por toda la aplicación en un paquete único.
Definir las constantes que corresponden a un área específica dentro de un paquete que encapsula esa funcionalidad nunca colocar literales ‘en duro’, como ‘SI’ o 150 en el código. Es conveniente crear un paquete para mantener estos nombres y valores publicados en reemplazo de los literales.

Este tipo de práctica permite que el código no luzca ‘hard codeado’, lo cual lo hace más legible y mantenible y además evita que los literales sean modificados.

Centralizar las definiciones de Types en las especificaciones del paquete. A medida que se usan los features del lenguaje, se definirán distintos TYPE entre los que podemos incluir:

SUBTYPEs que definen tipos específicos de la aplicación.
Collection TYPEs, como lista de números, fechas y records.

Cursores referenciados

Esto permite tener estandarizados los tipos de datos para que sean usados por múltiples programas. Los desarrolladores pueden escribir más rápidamente y disminuir los bugs. También simplifica el mantenimiento de los types, ya que solo es necesario modificarle/los paquetes donde están declarados.

Disminuir el uso de variables globales en paquetes y en caso de hacerlo, solo en el cuerpo del paquete. Una variable global es una estructura de datos que se puede referenciar fuera del alcance o bloque en donde está declarada. Cuando se declara una variable en un paquete, existe y retiene su valor durante la duración de la sesión. Son peligrosas porque crean dependencias ocultas o efectos laterales. El seguimiento delas mismas es complejo, ya que es necesario ver la implementación para ver sus instancias. Una solución general para esto es pasar estas variables globales como un parámetropara no referenciarlas directamente en el programa.

Exponer las variables globales de los paquetes usando ‘get and set’. Cualquier estructura de datos declarada en la especificación del paquete puede ser referenciada por cualquier programa con autorización de EXECUTE. Esto hace que en forma deliberada se pueda hacer uso de esas estructuras globales. Para evitar eso, es conveniente declarar estos datos en el cuerpo del paquete y proveer al paquete de métodos get y set, declarados en la especificación. Esta forma le permite a los desarrolladores, acceder a los datos a través de estos programas y manipular los datos según las reglas de estos procedimientos.

Pautas para el uso de estructuras de Control


Las estructuras de control pueden convertirse en un punto de complejidad de un código. La normalización del uso de RETURN y el EXIT garantiza rapidez en la comprensión de las lógicas escritas y facilidad en su mantenimiento.

Nunca se sale de una estructura repetitiva con RETURN o con EXITCada estructura repetitiva tiene un punto de control por donde se debe producir su salida.

Un FOR loop itera desde el valor de comienzo hasta el valor de terminación (ciclo N).
Un WHILE loop itera mientras no se cumpla la condición de terminación. (Ciclo0).
Existe un LOOP con control de la condición al final de ciclo (ciclo 1). Es LOOP … EXIT WHEN condición lógica.

Combinando estos ciclos se puede hacer que las estructuras tengan un único punto de control para el ciclo repetitivo. Esto facilita la lectura, comprensión y mantenimiento del programa y permite hacer modificaciones en forma más simple.

Una función debe tener un único RETURN exitoso como última línea de la sección ejecutable. Normalmente, cada manejador de excepciones puede retornar un valor. En los programas largos donde se evalúan muchas condiciones, la existencia de múltiples salidas dificulta el entendimiento y mantenimiento del código.

Nunca declarar el índice de un FOR .. LOOP.

PL/SQL ofrece dos tipos de FOR LOOP: Numéricos y cursores.
Ambos tienen este formato general:

FOR indice_loop IN loop range LOOP
Loop body
END LOOP;

El índice del loop se declara implícitamente durante la ejecución. El ámbito de existencia está restringido al cuerpo del loop. Si se declarase el índice, se estaría generando otra variable completamente separada que en el mejor de los casos nunca será usada, pero si se usa fuera del loop, puede introducir errores.

Pautas para el manejo de excepciones


Aunque se escriba un código perfecto, que no contenga errores y que nunca realice operaciones inapropiadas, el usuario podría usar el programa incorrectamente, produciendo una falla que no estaba contemplada. El uso de excepciones permite capturar y administrar los errores que se pueden producir dentro del código. Su buen uso trae como resultado un código con menos bugsy  más fácil de corregir.

Establecer los lineamientos para el manejo de errores antes de comenzar a codificar. Es impráctico definir secciones de excepciones en el código después de que el programa fue escrito. La mejor forma de implementar un manejo de errores en toda la aplicación es usando paquetes que contengan al menos los siguientes elementos.

Procedimientos que realicen el manejo de tareas de excepciones, como por ejemplo escribir un log de errores.

Un programa que oculte la complejidad de RAISE_APPLICATION_ERROR y los números de aplicación-error.

Una función que retorne el mensaje de error para un código de error.

Utilizar el modelo por defecto de manejo de excepciones para propagar la comunicación de errores. Aprovechar la arquitectura de manejo de errores de PL/SQL y separar las excepciones en bloques.

La sección de código ejecutable debe estar limpia, simple y fácil de seguir. No es necesario controlar el estado después de cada llamada a un programa. Simplemente debe incluirse una sección de excepción, capturar el error y determinar la acción a realizar.

Capturar todas las excepciones y convertir el significado de los códigos de error en los retornos a programas que no son PL/SQL. Cuando los programas PL/SQL son llamados por otros lenguajes de programación (Java, Visual Basic, etc.), al menos es necesario regresar el estado de error (código y mensaje)para que administren los mismos .Una manera sencilla de hacer esto es sobrecargar el programa original con otro del mismo nombre y dos parámetros adicionales.

Los desarrolladores pueden llamar al procedimiento que deseen y chequear los errores de la manera más apropiada para sus programas.

Usar procedimientos propios de RAISE  en lugar de las llamadas explicitas a
RAISE_APPLICATION_ERROR. Cuando se está controlando excepciones de sistemas como
NO_DATA_FOUND, se usa RAISE, pero cuando se quiere controlar un error de aplicación específico, se usa RAISE_APPLICATION_ERROR. Para el último caso, se debe asignar un número de error y mensaje, lo cual termina en un ‘hardcodeo’.Para ello se puede utilizar constantes para especificar un número. Una manera más clara es proveer un procedimiento que automáticamente controle el número de error y determina la forma de ejecutar el error.

Los desarrolladores no tienen que determinar cuál es el número de error que tienen que usar, solo pasan la constante que desean usar y dejan que la rutina determine cuál es el RAISE correspondiente.

No sobrecargar una EXCEPTION con múltiples errores al menos que la pérdidade información sea intencional.
No declarar una excepción genérica como ERROR_GENERAL y luego ejecutar la excepción en diferentes circunstancias. La lectura del código traerá problemas alintentar cual fue la causa de problema. La excepción NO_DATA_FOUND se puede utilizar para indicar que no se encontraron filas en la ejecución de un query o para indicar que se llegó al final de un archivo.

No Manejar excepciones que no pueden evitarse, pero si anticiparse. Si cuando se está escribiendo un programa, se puede predecir que un error va a ocurrir, se debería incluir un manejador para ese caso.

Evitar exponer código en duro para el manejo de errores, reemplazar por procedimientos que administren los mismos. Evitar exponer código en duro para el manejo de errores, reemplazar por procedimientos que administren los mismos. La mejor manera de registrar en forma consistente y tener mantener la calidad del seguimiento de errores por toda la aplicación es ofreciendo un conjunto de procedimientos predefinidos que canalicen el manejo de los mismos. Es fundamental que el equipo de desarrollo use siempre solamente estos procedimientos en sus cláusulas WHEN.

Usar nombres constantes para flexibilizar los números y mensajes de errores de aplicación, Oracle define 1000 números de error, entre –20000 y –20999 para usar para nuestra aplicación. Se recomienda definir todos los errores en una tabla o un archivo del sistema operativo, construir paquetes para manipular esos datos y ejecutar el RAISE usando esos nombres y no códigos en duro.

Usar WHEN OTHERS solo para excepciones desconocidas que deben serejecutadas. No es conveniente usar WHEN OTHERS para cualquier error. Si se puede conocer el tipo de excepción que se quiere ejecutar, es conveniente administrarla en forma específica.

Pautas mínimas para uso de SQL dentro de PL/SQL


La escritura de SQL dentro del código es uno de los aspectos más flexibles de PL/SQL y también puede ser uno de los más peligrosos. El establecimiento de reglas claras para su inclusión permite asegurar beneficios y buenos resultados.

Usar transacciones autónomas para aislar los efectos del ROLLBACK y COMMIT Permite grabar o realizar rollback dentro de un código PL/SQL sin afectar la sesión principal.

No utilizar COMMIT o ROLLBACK dentro de los procedimientos PL/SQL que son utilizados por procesos en otros leguajes. El COMMIT o ROLLBACK debe utilizarse dentro del proceso que llama los procedimientos PL/SQL. Cuando los procedimientos PL/SQL son llamados por otros lenguajes de programación (Java, Visual Basic, etc.) no deben utilizar las sentencias de manejo de transaccionesCOMMIT o ROLLBACK de modo de posibilitar un ordenado control de transacciones.

Usar COUNT solo cuando es necesario saber el número de ocurrencias. No usar
COUNT para saber si hay una o más de una fila para una condición dada. Solo se debe usar
COUNT para saber cuántas filas hay.

Usar cursores implícitos para realizar operaciones sobre muchos registros este constructor realiza la apertura y cierre en forma automática. Es fácil de usar y su lectura es más simple.

Nunca usar cursores implícitos para una fila El FOR LOOP está diseñado para múltiples filas de un cursor. Para un solo registro es más eficiente un SELECT INTO o un cursor explícito.


Parametrizar cursores explícitos. Los cursores retornan información como lo haría una función, por lo cual también pueden aceptar parámetros. Si se define un cursor con parámetros, se puede hacer quesea reutilizable en diferentes circunstancias de un programa. Esto es mejor si se define en un paquete.

Usar RETURNING para obtener información sobre filas modificada

Referenciar atributos del cursor inmediatamente después de ejecutar la operación de SQL. Las sentencias INSERT, UPDATE y DELETE se ejecutan como cursores implícitos en PL/SQL. Es decir, no se puede declarar, abrir y procesar este tipo de operaciones en forma explícita. El motor de Oracle SQL toma el control y administración del cursor. Se puede obtener información acerca de los resultados de estas operaciones implícitas más recientemente utilizada consultando los siguientes atributos de cursor.
SQL%ROWCOUNT: Números de filas afectadas por la instrucción DML.
SQL%ISOPEN: Siempre es falso en los manejos de cursores implícitos.
SQL%FOUND: Retorna TRUE si la operación afecta al menos una fila.
SQL%NOTFOUND: Retorna FALSE si la operación afecta al menos una fila. Hay un solo set de atributos SQL% en una sesión, el cual refleja la última operación implícita realizada. Por lo cual el uso de estos atributos deberá hacerse con el mínimo uso de código entre la operación y la referencia al atributo. De otra manera el valor retornado por el atributo podría no corresponder al SQL deseado.

Usar BULK COLLECT para mejorar la performance de los querys multiregistros. En algunas oportunidades es necesario recuperar un gran número de filas desde la base de datos.
BULK COLLECT permite obtener el set de filas en una sola llamada al motor y recuperar el resultado en un arreglo (COLLECTION).Para usar este método es necesario declarar un arreglo para mantener los datos recuperados y luego anteponer al INTO la cláusula BULK COLLECT.

Esta forma de recuperación de datos mejora (en algunos casos enormemente) la performance del query .Se debe tener cuidado cuando el
SELECT retorna miles de filas ya que si el mismo programa está corriendo con diferentes usuarios en una misma instancia, puede haber problemas de memoria. Se podría restringir usando ROWNUM o LIMIT para cada instancia.


Usar FORALL para mejorar la performance de DML basados en arreglos.


  
Para los casos en que es necesario modificar (INSERT, DELETE o UPDATE) un gran número de filas en una base de datos dentro de un PL/SQL, se recomienda la sentencia FORALL
. La performance mejora notablemente ya que se reduce el número de llamadas entre PL/SQL y el SQL engine.

Reglas generales para performance



Para el manejo de datos dentro de un código PL/SQL es necesario tener en cuenta las siguientes premisas.

Una sentencia SQL es más rápida que un programa. Siempre es preferible utilizar la potencia del SQL antes que emular su funcionamiento con un PL/SQL largo y complejo.

Un programa pequeño es más rápido que uno extenso. El uso de programas largos agrega más tiempo de ejecución. El uso de paquetes pequeños permite al servidor disponer de ellos de manera más rápida y eficiente

Evitar llamadas repetitivas al sql engine Mientras menos solicitudes de servicios se hagan al sql engine, más rápido se ejecutará un programa. Para el caso de operaciones de SQL similares o repetitivas, utilizarFORALL, BULKS, variables BIND, etc.