diumenge, 8 / juliol / 2007

Corregint l'absència d'un DBA I: Sobrecarregant Procediments empaquetats

Sovint els entorns de desenvolupament no disposen de la figura del DBA. És una qüestió de costos, a una mala gestió dels mateixos, em refereixo, que no es disposi d'un administrador, que posi ordre al caos que acostuma a esdevenir. Quan la situació és insostenible o s'ha de lliurar una versió amb canvis a nivell de base de dades, llavors es contracta a temps parcial a un, per a que intenti restablir l'ordre que hauria d'haver existit des del principi.

No considererem la qüestió dels costos, però si l'absència de DBA. Quan el disseny d'una base de dades està en mans de desenvolupadors poc formats, ens acostumem a trobar una errada habitual: múltiples definicions de tipus de dades per a un únic Atribut o Columna de BD.

Il·lustrem-ho en un exempel molt freqüent: la columna ID_OBJECTE_APL, en la taula MESTRE_OBJECTES és un Number(12), en la taula OBJECTES_RECURSOS és un Varchar2(15), en la taula RECURSOS_DETALL és un Varchar2(50). Hi ha arguments de negoci -i pot ser emotius- per defendre aquesta posició, però no hi ha cap raó o argument tècnic de pes, que la sostingui...

En la taula mestra la columna ID_OBJECTE_APL, és una clau (identificador únic) de l'Objecte que es genera mitjançant una seqüència numèrica.

En la taula OBJECTES_RECURSOS, la columna ID_OBJECTE_APL és una concatenació d'un literal, per exemple l'àrea de negoci, i de l'identificador únic de la taula mestra.

En la taula RECURSOS_DETALL, la columna ID_OBJECTE_APL, li afegim a la columna de la taula OBJECTES_RECURSOS, una altra cadena que ens informa quin tipus de proveïdor és, i l'identificador del proveïdor.

Aquesta aproximació crea més problemes del que hom intueix a primer cop d'ull. Un dels pilars on es fonamenta un desenvolupament eficient, és la reutilització del codi. En l'escenari que tractem, si tenim una funció - FNC_DESCRIPCIO_OBJECTE -, que ens ha de retornar la descripció associada a l'identificador únic d'objecte... Quin utilitzarem? Haurem de fer una funció per a cadascuna de les taules, o incorporar un tractament en funció de l'origen que volem consultar?

Quan hom se n'adona del problema, acostuma a ser massa tard, i llavors la solució implica haver de modificar el codi de forma acurada i invertir uns recursos que en la majoria dels casos, no s'autoritzaran...

Si analitzem la situació, ens adonem que com a mínim, haurem de crear dues funcions diferents: una que ens accepti un VARCHAR2 i l'altra que ens accepti un NUMBER o un INTEGER. O simplificant al màxim, una única funció que retorni VARCHAR2 i controlar la crida en el codi del subprograma que la fa servir.

En aquesta situació, el desenvolupador ja té la solució i es disposa a crear dues funcions, perque en no tenir definits Paquets PL/SQL -una altra de les errades típiques de desenvolupadors no formats- no té cap altra sortida.

Sobrecarregant Subprogrames en un paquet
L'aproximació del DBA és ben diferent. En arribar, el primer que ha fet és empaquetar les funcions i procediments que penjaven directament de l'esquema, agrupant-los per una lògica concreta. Donat que no podrà modificar les definicions de base de dades per l'impacte que suposa en l'aplicació, decideix solucionar el problema de manera ràpida i senzilla.


..CREATE OR REPLACE PACKAGE pkg_Eines_Negoci AS
....FUNCTION fnc_DESCRIPCIO_OBJECTE (vObjecte Varchar2)
....RETURN Varchar2;

....FUNCTION fnc_DESCRIPCIO_OBJECTE (vObjecte Number)
....RETURN Varchar2;

..END pkg_Eines_Negoci;
../

Fixeu-vos que la funció fnc_DESCRIPCIO_OBJECTE es defineix de dues maneres diferents, una per a acceptar NUMBER i l'altre per a acceptar VARCHAR2, però manté el mateix nom.

NOTA: A nivell d'esquema no podriem fer-ho perque en executar la sentència de creació de la segona funció amb el mateix nom, eliminaríem la primera o l'Oracle ens retornaria un error dient que l'objecte ja existeix.

Hom es pot preguntar, Quin avantatge té? Doncs, bàsicament no haver de modificar gairebé el codi per deslligar el canvi i simplificar el desenvolupament en ho haver-se de preocupar un, de si la variable és un Varchar2 o un Number: crida la funció i recupera la descripció de l'objecte.

De fet un exemple clar del que és una sobrecàrrega de funcions ho tenim en les transformacions que fa l'Oracle entre tipus de dades, com són TO_NUMBER, TO_CHAR, TO_DATE, que sempre es criden de la mateixa manera (potser, caldrà incloure una màscara de format) sense tenir en compte el tipus de dada que li passem per paràmetre. Aquestes funcions es defineixen en el paquet DBMS_SQL.


El problema ara rau en com identificar dels possibles ID_OBJECTE_APL quin és el que necessitem, per informar la descripció, car és possible que les descripcions també siguin diferents, per acabar de liar la troca. El tractament, el veurem en una propera aportació, però el següent llistat aporta la solució més senzilla.


..CREATE OR REPLACE PACKAGE BODY pkg_Eines_Negoci AS
....FUNCTION fnc_DESCRIPCIO_OBJECTE (vObjecte Varchar2)
....RETURN Varchar2 IS
......vTaula...Varchar2(30);
......vSQL.....Varchar2(32767);
......vRetorn..Varchar2(50);
....BEGIN
......If length(vObjecte) <= 12 Then
........vTaula := 'OBJECTES_RECURSOS'
......Else
........vTaula := 'RECURSOS_DETALL';
......End If;

......vSQL := 'select NOM_OBJECTE_APL from '||vTaula||
..............' where ID_OBJECTE_APL = '''||vObjecte||''' ';
......execute immediate vSQL into vRetorn;
......Return vRetorn;
....EXCEPTION
......When Others Then
........vRetorn := 'ERROR';
........Return vRetorn;
....END fnc_DESCRIPCIO_OBJECTE;

....FUNCTION fnc_DESCRIPCIO_OBJECTE (vObjecte Number)
....RETURN Varchar2 IS
......vSQL.....Varchar2(32767);
......vRetorn..Varchar2(50);
....BEGIN
......vSQL := 'select NOM_OBJECTE_APL from MESTRE_OBJECTES'
..............' where ID_OBJECTE_APL = '||vObjecte;
......execute immediate vSQL into vRetorn;
......Return vRetorn;
....EXCEPTION
......When Others Then
........vRetorn := 'ERROR';
........Return vRetorn;
....END fnc_DESCRIPCIO_OBJECTE;
..END pkg_Eines_Negoci;
../

2 comentarios:

Rodrigo ha dit...

Oi, achei teu blog pelo google tá bem interessante gostei desse post. Quando der dá uma passada pelo meu blog, é sobre camisetas personalizadas, mostra passo a passo como criar uma camiseta personalizada bem maneira. Se você quiser linkar meu blog no seu eu ficaria agradecido, até mais e sucesso. (If you speak English can see the version in English of the Camiseta Personalizada. If he will be possible add my blog in your blogroll I thankful, bye friend).

Anònim ha dit...

Amb aquest petit canvi, coneixes quin error Oracle s'ha produït en executar-se la funció.


EXCEPTION
When Others Then
--vRetorn := 'ERROR';
vRetorn := to_char(SQLCode);
Return vRetorn;
END fnc_DESCRIPCIO_OBJECTE;
END pkg_Eines_Negoci;
/

Amb un simple INSTR, posterior de control en tenim prou per tractar el retorn...

...

execucio:=fnc_DESCRIPCIO_OBJECTE
if instr(execucio,'-') > 1 then
MSG_LOG:='fnc_DESCRIPCIO_OBJECTE Error Oracle '||execucio;

raise ErrorProc;

end if;

...