Algunos tips de SQL Servicios web (5.1): poniendo Jdeveloper a punto con Axis(I)
Jul 09

Me he visto obligado a sustituir algunas tablas de un esquema por vistas sobre otro. La idea es hacer una refactorización, por lo que necesito asegurarme de que las nuevas vistas tengan las mismas columnas que las antiguas tablas. Para eso me he hecho un script que se encarga de compararme nombres y tipos de variable. Como utiliza la vista ALL_TAB_COLS sirve para comparar indistintamente vistas y tablas. Por si a alguien le resulta de utilidad, ahí va:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
-- Script para comparar columnas de dos vistas o tablas.
-- http://www.tsoracle.com
--------------
-- Sets varios
--------------
SET SERVEROUTPUT ON 
SET VERIFY OFF
---------------------
-- Valores de entrada
---------------------
ACCEPT PROPIETARIO1 PROMPT "Propietario del esquema 1: "
ACCEPT TABLAOVISTA1 PROMPT "Nombre de la tabla o vista 1: "
ACCEPT PROPIETARIO2 PROMPT "Propietario del esquema 2: "
ACCEPT TABLAOVISTA2 PROMPT "Nombre de la tabla o vista 2: "
DECLARE
 
  --------------------
  -- Tipos de Variable
  --------------------
  -- Tipo para diferencias unitarias
  TYPE tDiferencia IS RECORD (
    vColumna ALL_TAB_COLS.COLUMN_NAME%TYPE,
    vValorA  VARCHAR2(200),
    vValorB  VARCHAR2(200) );
 
  -- Tipo para conjuntos de diferencias
  TYPE tDiferencias IS TABLE OF tDiferencia INDEX BY BINARY_INTEGER;
 
  -------------
  -- Constantes
  -------------
  VOWNER1 CONSTANT ALL_TAB_COLS.OWNER%TYPE      := UPPER( 
                                                     SUBSTR( '&&PROPIETARIO1',
                                                             1, 30 ) );
  VTABLA1 CONSTANT ALL_TAB_COLS.TABLE_NAME%TYPE := UPPER( 
                                                     SUBSTR( '&&TABLAOVISTA1',
                                                             1, 30 ) );
 
  VOWNER2 CONSTANT ALL_TAB_COLS.OWNER%TYPE      := UPPER( 
                                                     SUBSTR( '&&PROPIETARIO2',
                                                             1, 30 ) );
  VTABLA2 CONSTANT ALL_TAB_COLS.TABLE_NAME%TYPE := UPPER( 
                                                     SUBSTR( '&&TABLAOVISTA2',
                                                             1, 30 ) );
  VNULL  CONSTANT VARCHAR2(30) := 'SIN VALOR';
  ------------
  -- Variables
  ------------
  -- Variables para llenar las diferencias entre columnas
  plDistintoTipo     tDiferencias;
  plDistintaLongitud tDiferencias;
  plDistintoNulable  tDiferencias;
 
  -- Variable boleana que me indica si existen las dos tablas/vistas
  bExistenAmbas BOOLEAN;
 
  -----------
  -- Cursores
  -----------
  CURSOR cColumnasDistintoTipo IS
    SELECT A.COLUMN_NAME VCOLUMNA, -- Columna de la discordia
 
           -- Datos de la columna de la tabla 1
           NVL( A.DATA_TYPE, VNULL ) DATA_TYPE1, 
           TO_CHAR( A.DATA_LENGTH )  DATA_LENGTH1,
           NVL( A.NULLABLE,  VNULL ) NULLABLE1,
 
           -- Datos de la columna de la tabla 2
           NVL( B.DATA_TYPE, VNULL ) DATA_TYPE2, 
           TO_CHAR( B.DATA_LENGTH )  DATA_LENGTH2,
           NVL( B.NULLABLE,  VNULL ) NULLABLE2
 
    FROM   ALL_TAB_COLS A, ALL_TAB_COLS B
    WHERE  A.COLUMN_NAME = B.COLUMN_NAME
      AND  A.OWNER       = VOWNER1
      AND  A.TABLE_NAME  = VTABLA1
      AND  B.OWNER       = VOWNER2
      AND  B.TABLE_NAME  = VTABLA2
      AND  ( NVL( A.DATA_TYPE, VNULL )  != NVL( B.DATA_TYPE, VNULL ) OR
             A.DATA_LENGTH              != B.DATA_LENGTH             OR
             NVL( A.NULLABLE,  VNULL )  != NVL( B.NULLABLE,  VNULL ) 
            )
    ORDER BY A.COLUMN_ID;
 
  -----------------------------
  -- Funciones y procedimientos
  -----------------------------
  -- Di -------------------------------------------------------------
  PROCEDURE Di( cadena VARCHAR2 ) IS
    -- Procedimiento que hace un dbms_output.put_line cortando
    -- la cadena de entrada a 255 para evitar que de un error
    -- de línea demasiado larga.
  BEGIN
    DBMS_OUTPUT.PUT_LINE( SUBSTR( cadena, 1, 255 ) );
  END Di;
 
  -- diSeparador ----------------------------------------------------
  PROCEDURE diSeparador IS
    -- Procedimiento que escribe unas líneas que hacen las veces
    -- de separador de sección.
  BEGIN
    Di('.');
    Di('====================================================' );
  END;
 
  -- diDiferencia ---------------------------------------------------
  PROCEDURE diDiferencia( rDif tDiferencia ) IS 
    -- Procedimiento que escribe por pantalla una diferencia
  BEGIN
    Di( rDif.vColumna || '. En ' || VTABLA1 || ': ' || rDif.vValorA || ', ' 
                                 || VTABLA2 || ': ' || rDif.vValorB );
  END diDiferencia;
 
  -- diDiferencias -----------------------------------------------------------
  PROCEDURE diDiferencias( vDescri VARCHAR2, plDif tDiferencias ) IS 
    -- Procedimiento que escribe en pantalla la lista de diferencias
  BEGIN
    diSeparador();
    di( vDescri );
 
    IF plDif.LAST IS NOT NULL THEN 
      FOR nInd IN plDif.FIRST..plDif.LAST LOOP 
        diDiferencia( plDif( nInd ) );
      END LOOP;
 
    ELSE 
      Di( 'No ha diferencias' );
    END IF;
  END diDiferencias;
 
  -- agregaDiferencia -----------------------------------------------
  PROCEDURE agregaDiferencia( rDif tDiferencia, plDif IN OUT tDiferencias ) IS 
    -- Procedimiento que agrega una diferencia a una lista de diferencias.
    ------------
    -- Variables
    ------------
    -- Para el siguiente índice de la tabla
    nIndSiguiente BINARY_INTEGER;
  BEGIN
    IF plDif.LAST IS NULL THEN 
      -- La lista está vacía, me están pidiendo que agrege el primer elemento
      nIndSiguiente := 1;
    ELSE 
      nIndSiguiente := plDif.LAST + 1;
    END IF;
 
    plDif( nIndSiguiente ) := rDif;
  END agregaDiferencia;
 
  -- agregaDifSiExiste ----------------------------------------------
  PROCEDURE agregaDifSiExiste( vColumna        ALL_TAB_COLS.COLUMN_NAME%TYPE,
                               vValorA         VARCHAR2,
                               vValorB         VARCHAR2,
                               plDif    IN OUT tDiferencias
                             )
  IS 
    -- Procedimiento que agrega una diferencia si los valores son distintos
    ------------
    -- Variables
    ------------
    -- Variable de diferencia auxiliar
    rDifAux tDiferencia;
  BEGIN
    IF vValorA != vValorB THEN 
      rDifAux.vColumna := vColumna;
      rDifAux.vValorA  := vValorA;
      rDifAux.vValorB  := vValorB;
 
      agregaDiferencia( rDifAux, plDif );
    END IF;    
 
  END agregaDifSiExiste;
 
  -- diColumnasQueNoEstan -------------------------------------------
  PROCEDURE diColumnasQueNoEstan(
              vOwnerA ALL_TAB_COLS.OWNER%TYPE,
              vTablaA ALL_TAB_COLS.TABLE_NAME%TYPE,
              vOwnerB ALL_TAB_COLS.OWNER%TYPE,
              vTablaB ALL_TAB_COLS.TABLE_NAME%TYPE )
  IS
    -- Procedimiento que saca por pantalla la relación de columnas
    -- de la tabla o vista A que no están en la tabla o vista b
 
    -----------
    -- Cursores
    -----------
    -- Cursor que saca las columnas de la tabla o vista A
    -- que no están en la tabla o vista B
    CURSOR cNoColumnas  IS
      SELECT A.COLUMN_NAME
      FROM   ALL_TAB_COLS A
      WHERE  A.OWNER      = vOwnerA
        AND  A.TABLE_NAME = vTablaA
        AND  NOT EXISTS(
              SELECT 'X'
              FROM   ALL_TAB_COLS B
              WHERE  B.OWNER       = vOwnerB
                AND  B.TABLE_NAME  = vTablaB
                AND  B.COLUMN_NAME = A.COLUMN_NAME )
      ORDER BY A.COLUMN_ID;
  BEGIN
    diSeparador();
 
    Di( 'Columnas de '      || vOwnerA || '.' || vTablaA ||
        ' que no están en ' || vOwnerB || '.' || vTablaB ||
        ':');
 
    FOR rCol IN cNoColumnas LOOP
      Di( rCol.COLUMN_NAME );
    END LOOP;
  END diColumnasQueNoEstan;
 
  -- existeTablaOVista ---------------------------------------------------------
  FUNCTION existeTablaOVista( vPropietario VARCHAR2,
                              vTabla       VARCHAR2 ) 
  RETURN BOOLEAN IS 
    -- Función que comprueba se existe una tabla o vista con el propietario
    -- y nombre que le pasan como parámetro.
    ------------
    -- Variables
    ------------
    vAux VARCHAR2(1);
  BEGIN
    SELECT 'X'
    INTO   vAux
    FROM   ALL_OBJECTS 
    WHERE  OWNER       = vPropietario
      AND  OBJECT_NAME = vTabla 
      AND  OBJECT_TYPE IN ( 'TABLE', 'VIEW' );
 
    -- Si llego hasta aquí es que existe.
    RETURN TRUE;
  EXCEPTION 
    WHEN NO_DATA_FOUND THEN 
      RETURN FALSE;
  END existeTablaOVista;
 
BEGIN
  -- Amplío el buffer del dbms_output para que no se quede pequeño
  DBMS_OUTPUT.DISABLE;
  DBMS_OUTPUT.ENABLE(10000000);
 
  IF existeTablaOVista( VOWNER1, VTABLA1 ) THEN  
    IF existeTablaOVista( VOWNER2, VTABLA2 ) THEN 
      bExistenAmbas := TRUE;
    ELSE 
      diSeparador();
      di( 'No existe la tabla o vista ' || VOWNER2 || '.' || VTABLA2 );
      bExistenAmbas := FALSE;
    END IF;
  ELSE 
    diSeparador();
    di( 'No existe la tabla o vista ' || VOWNER1 || '.' || VTABLA1 );
    bExistenAmbas := FALSE;
  END IF;
 
  -- Sólo continúo si existen ambas tablas:
  IF bExistenAmbas THEN
 
    -- Columnas de la tabla 1 que no están en la tabla 2
    diColumnasQueNoEstan( VOWNER1, VTABLA1, VOWNER2, VTABLA2 );
 
    -- Columnas de la tabla 2 que no están en la tabla 1
    diColumnasQueNoEstan( VOWNER2, VTABLA2, VOWNER1, VTABLA1 );
 
    -- Ahora recopilo las diferencias en las columnas que sí están en las dos
    -- tablas o vistas.
    FOR rDife IN cColumnasDistintoTipo LOOP 
      agregaDifSiExiste( rDife.VCOLUMNA, rDife.DATA_TYPE1, rDife.DATA_TYPE2, 
                         plDistintoTipo );
 
      agregaDifSiExiste( rDife.VCOLUMNA, rDife.DATA_LENGTH1, rDife.DATA_LENGTH2, 
                         plDistintaLongitud );
 
      agregaDifSiExiste( rDife.VCOLUMNA, rDife.NULLABLE1, rDife.NULLABLE2, 
                         plDistintoNulable );
    END LOOP;
 
    -- Pinto la lista de diferencias por cada tipo
    diDiferencias( 'Diferencias de tipo:',     plDistintoTipo );
    diDiferencias( 'Diferencias de longitud:', plDistintaLongitud );
    diDiferencias( 'Diferencias de nulable:',  plDistintoNulable );
  END IF;
END;
/
SET VERIFY ON
This work, unless otherwise expressly stated, is licensed under a Creative Commons Attribution 3.0 License.

Comments are closed.