Sei sulla pagina 1di 42

BUENAS PRÁCTICAS SQL

Contenido
TEMA 1 .......................................................................................................................................... 3
Módulo 1: Procesamiento y complejidad de sentencias SQL ................................................... 3
Fases en el procesamiento de una sentencia SQL ................................................................ 3
G1. Complejidad de sentencias SQL ...................................................................................... 3
Módulo 2: El optimizador .......................................................................................................... 4
¿Qué es el optimizador?........................................................................................................ 4
¿Qué optimizador usaré? ...................................................................................................... 4
Los histogramas..................................................................................................................... 5
Módulo 3: Plan de ejecución ..................................................................................................... 6
Plan de ejecución: Cómo consultarlo e interpretarlo ........................................................... 6
Tipos de Join ........................................................................................................................ 12
¿Qué join usar? ................................................................................................................... 14
Tema 2 ......................................................................................................................................... 16
Módulo 1: En el SELECT ........................................................................................................... 16
COUNT(*) frente a COUNT(1).............................................................................................. 16
Orden de los campos en la SELECT...................................................................................... 16
S1. Campos necesarios en la select ..................................................................................... 16
Tema 3 ......................................................................................................................................... 17
Módulo 1: En el FROM ............................................................................................................ 17
Número de tablas en el FROM ............................................................................................ 17
Orden de las tablas en el FROM .......................................................................................... 17
Sentencias distribuidas........................................................................................................ 17
Tema 4 ......................................................................................................................................... 19
Módulo 1: En el modelo de datos ........................................................................................... 19
Exceso de índices................................................................................................................. 19
Índices redundantes ............................................................................................................ 19
Indices y Foreign Keys ......................................................................................................... 19
Uso de índices compuestos ................................................................................................. 20
Índices efectivos .................................................................................................................. 20
Tipos de datos obsoletos..................................................................................................... 21
Mi índice no se usa .............................................................................................................. 21
Bloqueos en índices bitmap ................................................................................................ 23
Tema 5 ......................................................................................................................................... 24
Módulo 1: En el WHERE .......................................................................................................... 24
Uso de Bind Variables.......................................................................................................... 24
Grandes INLIST .................................................................................................................... 26
Operadores sobre campos indexados ................................................................................. 26
Comparadores sobre campos indexados ............................................................................ 27
Conversiones implícitas ....................................................................................................... 27
Having y Where ................................................................................................................... 28
ROWNUM ............................................................................................................................ 28
Antijoin ................................................................................................................................ 30
Tema 6 ......................................................................................................................................... 32
Módulo 1: Ordenaciones/Agrupaciones ................................................................................. 32
General sobre la ordenación ............................................................................................... 32
ORDER BY ............................................................................................................................ 32
DISTINCT y GROUP BY ......................................................................................................... 32
UNION ALL frente a UNION ................................................................................................. 32
Tema 7 ......................................................................................................................................... 33
Módulo 1: En el INSERT ........................................................................................................... 33
Especificar Campos en el INSERT ........................................................................................ 33
TEMA 1

Módulo 1: Procesamiento y complejidad de sentencias SQL

Fases en el procesamiento de una sentencia SQL

Cada vez que se solicita a Oracle el ejecutar una sentencia SQL, internamente se realizan una serie de
acciones, agrupadas principalmente en 3 bloques: Análisis (parse), Ejecución, Recogida de datos (fetch).

Se chequea si la sentencia SQL existe en la


Existencia de la sentencia en la cache, revisión cache, se revisa sintácticamente (bien escrita)
sintáctica y semántica. y semánticamente (estructura, campos y
permisos correctos)
Algunas queries (p.e. las que incluyen vistas,
subqueries) tienen que ser transformadas a la
Transformación nueva sentencia más adecuada. Algunas
sentencias pueden ser cambiadas por otras
más óptimas o simples.
PARSE
CBO Cálculo del coste Se calculan los costes de acceso a los objetos,
RBO del objeto y y su cardinalidad (estimación número de filas
(Optimizador cardinalidad. involucradas)
(Optimizador por Costes). Costes con se evalúan distintos planes de ejecución
por Reglas). Decide el plan diferentes cambiando el orden de join entre las tablas (2
Decidir plan respecto a las órdenes de join a 2), eligiendo el orden menos costoso.
conforme a estadísticas Creación de
reglas de los objetos estructuras para se crean las estructuras necesarias para la
involucrados. ejecución ejecución (cursores, etc.)

se toma la memoria necesaria para las bind


EXECUTE variables, se asignan y se ejecutan conforme
al plan seleccionado durante el "parse".
se traen los bloques de datos consultados, se
FETCH
realizan ordenaciones si procede.

G1. Complejidad de sentencias SQL

No se considera adecuado el realizar sentencias SQL muy muy largas y complejas, ya que son más costosas
de mantener y de entender por el resto. Además, estas sentencias complejas suelen provocar planes de
ejecución inadecuados.
Se recomienda el descomponer estas sentencias en varias llamadas SQL, aunque provoquen algo más de
código en nuestras aplicaciones. A la larga se mantienen mejor y dan mejores tiempos de respuesta.
Módulo 2: El optimizador

¿Qué es el optimizador?

Cada vez que se ejecuta una sentencia contra la base de datos, entre otras cosas Oracle debe decidir cuál
es el plan de ejecución óptimo (saber cómo acceder a cada objeto, y en qué orden se realizan lo joins a las
distintas tablas implicadas)
El optimizador es el que se encarga de realizarlo. Actualmente existen dos tipos de optimizadores para
Oracle:

 Por Reglas: Los criterios para decidir el plan se realizan en base a una serie de reglas fijas,
independientemente del volumen o distribución de datos (por ejemplo, la existencia de índices, y
tipos de índices). El optimizador por reglas dejo de ser mantenido desde la versión 8 de Oracle, y
dejará de estar soportado a partir de la versión 10.
 Por costes: Oracle tiene en cuenta el volumen y distribución de datos conforme a estadísticas
recogidas anteriormente. Dependiendo del volumen decide los órdenes de join o el uso o no de
índices, por ejemplo.

Resulta claro que, para el optimizador por costes, resulta imprescindible basarse en estadísticas fiables,
con lo que deben actualizarse periódicamente.

¿Qué optimizador usaré?

De los dos optimizadores existentes (reglas y costes), se aconseja siempre utilizar el optimizador por costes.
(El optimizador por reglas dejo de ser mantenido desde la versión 8 de Oracle, y dejará de estar soportado
a partir de la versión 10).

Proponer el uso de un tipo de optimizador

ALTER SESSION SET OPTIMIZER_GOAL=


Sesión A partir de Oracle 9i:
ALTER SESSION SET OPTIMIZER_MODE=
B.Datos optimizer_mode=
Utilizando Hints
Sentencia
/*+ RULE */, /*+ FIRST_ROWS */, /*+ ALL_ROWS */

Valores posibles:
RULE Por reglas.
Por costes, optimizando devolución de las primeras
filas.
FIRST_ROWS
A partir de Oracle 9i: FIRST_ROWS,
FIRST_ROWS_[1|10|100|1000]
ALL_ROWS Por costes, optimizando devolución de todas las filas.
Oracle elige, si hay estadísticas en los objetos, intenta
CHOOSE
por costes (ALL_ROWS)
La utilización de las siguientes características de Oracle, forzará a utilizar el optimizador por costes frente
a reglas:
Tabla con PARALLEL fijado Domain Indexes (Inter Media)
Particionamiento Parallel Create Table As Select
IOT (Index Organized Tables) Índices basados en función
Índices inversos Query Rewrite activado
Cualquier hint, distinto de RULE

Los histogramas

Los histogramas son unas estadísticas más completas, que en determinadas ocasiones deben ser utilizadas
para proporcionar más información al optimizador por costes.
Las estadísticas normales que se ejecutan con ANALYZE, presuponen una distribución uniforme de los
datos. Por ejemplo, en una tabla tenemos un campo llamado estado. El analyze obtendrá el número total
de registros de la tabla (por ejemplo 2.000.000) y el número de valores distintos que contiene el campo
estado (por ejemplo 2 valores: PENDIENTE y TERMINADO). De esta forma, con las estadísticas de Oracle,
cuando hagamos una select a esa tabla preguntando sólo por el campo estado, entenderá que en total
buscaremos 1.000.000 de registros (total de registros / # valores distintos).
Esta suposición de distribución uniforme que realiza Oracle puede causar muchos problemas, si no se
cumple la distribución uniforme. Si resulta que, en mi tabla, el 90% de los campos tienen el valor
"TERMINADO", sería bueno que Oracle lo supiera, y saber que cuando se pregunte por el estado
"PENDIENTE", me devolverán muchos menos registros.
De esta forma, para aquellos campos en que exista una importante desviación de la distribución uniforme
de datos, se aconseja la creación de los histogramas, que en definitiva se encargan de conocer la cantidad
de registros existentes, para los distintos valores de los campos.
Hay que tener en cuenta que los histogramas, también son estadísticas de Oracle, con lo que deben
actualizarse periódicamente. La creación de histogramas, ralentiza la creación de estadísticas, y deben
utilizarse con moderación, en los casos que realmente sea necesario (distribución no uniforme).
Módulo 3: Plan de ejecución

Plan de ejecución: Cómo consultarlo e interpretarlo

Para la optimización de sentencias SQL, es básico conocer el plan de acceso (plan de ejecución) que
Oracle utiliza en la ejecución de la sentencia.

Existen varios métodos para conocer el plan de ejecución:


1.- Desde SQL*Plus ejecutando EXPLAIN PLAN FOR.

2.- Desde SQL*Plus ejecutando SET AUTOTRACE.

3.- Sacando traza de la sesión, y utilizando tkprof.

Plan de ejecución con EXPLAIN PLAN

Es necesario que en el usuario exista una tabla llamada PLAN_TABLE, donde se almacena el plan de
ejecución. Si no existe esa tabla en el usuario que está ejecutando la sentencia, y dado que la estructura de
la PLAN_TABLE varía entre distintas versiones de Oracle, solicita al equipo de Administración de Base de
Datos, que te genere la tabla en tu usuario.
Desde SQL*Plus, ejecuta por ejemplo:

En la tabla PLAN_TABLE quedará almacenado el plan de ejecución.

Todas las versiones de Oracle:


Realizando una select puedes consultarlo. Puedes utilizar la siguiente select, para interpretar visualmente
un poco mejor el plan de ejecución:

El resultado, muestra el plan de ejecución:


A partir de Oracle 9i:
Se puede obtener el plan de ejecución, utilizando esta sentencia:

El resultado, muestra el plan de ejecución:

Plan de ejecución con SET AUTOTRACE

Es necesario que en el usuario exista una tabla llamada PLAN_TABLE, donde se almacena el plan de
ejecución. Si no existe esa tabla en el usuario que está ejecutando la sentencia, y dado que la estructura de
la PLAN_TABLE varia entre distintas versiones de Oracle, solicita al equipo de Administración de Base de
Datos, que te generen la tabla en tu usuario.
Desde SQL*Plus, ejecuta por ejemplo:

Directamente obtendrá el plan de ejecución en el siguiente formato:


Plan de ejecución con TKPROF

Oracle permite registrar en un fichero de traza todas las sentencias que se ejecutan en una sesión, y a partir
de este fichero, utilizando tkprof, puedes consultar los planes de ejecución. A continuación te mostramos
un ejemplo. Para averiguar en qué directorio, para la base de datos que utilizas, se crean los ficheros de
traza, ponte en contacto con el equipo de Administración de Base de Datos.

Con la sentencia ALTER SESSION SET SQL_TRACE=, activamos o desactivamos el volcado en el fichero de
traza. Localizamos el fichero de traza que hemos generado (tec01_ora_6857.trc):

Obtenemos los planes de ejecución en el fichero tec01_ora_6857.tkp, utilizando tkprof:


El formato del comando tkprof es:
tkprof <fichero de entrada> <fichero de salida> sys=no explain=<usuario>/<password>
donde el usuario y la password corresponden al usuario que ejecutó las sentencias SQL de las que
queremos obtener el plan de ejecución.

El fichero de salida tec01_ora_6857.tkp contiene la información que necesitábamos:


Ejemplo de interpretación de un plan para interpretar el plan de ejecución que hemos obtenido por
cualquiera de estos tres métodos, debemos hacer una lectura de dentro hacía fuera para conseguir
entender cómo se consigue el acceso a los datos. A partir de este plan de ejecución, descubrimos que:

Se ejecuta el paso 3 (FULL de emp), devolviendo registro a registro a 2 (NESTED LOOP).


Para cada registro devuelto por 3, se ejecutan los pasos 5 y 4 (búsqueda en índice PK_DEPT (unique scan)
y búsqueda de ese registro en la tabla DEPT accediendo por rowid).
Cada una de las filas recogidas por 3, y 4-5, se recogen en el NESTED LOOP en un único registro (JOIN)
siendo el paso 2.
Después se ejecuta el paso 6 (FULL de la tabla SALGRADE).
A continuación, se ejecuta el paso 1 (FILTER), que en verdad implementa el NOT EXISTS de la select. De esta
forma, para cada registro que devuelva 2, y no esté en 6, se incluirá en el resultado buscado.
Gráficamente podría representarse de la siguiente forma:

La lectura en el orden de ejecución de este árbol se realiza de abajo a arriba y de izquierda a derecha. Si
tienes dudas a la hora de interpretar los planes de ejecución puedes consultar al equipo de Administración
de Base de Datos.

Tipos de Join

Cuando en una sentencia SELECT, en la cláusula FROM indicamos más de una tabla, Oracle al obtener los
datos debe ir "juntando" los datos de cada tabla, en función de las condiciones del WHERE. A esta unión es
a lo que llamamos el join.
Oracle, independientemente del número de tablas en el FROM, siempre realiza los joins de dos en dos
tablas. aplicando los resultados de unir dos tablas, a la siguiente tabla, y así sucesivamente hasta pasar por
todas las tablas del FROM.
Actualmente existen tres tipos de join distintos:
1.- Nested Loop

2.- Sort Merge Join

3.- Hash join

Puede cambiarse el comportamiento del optimizador, para que le elija otro método de join, utilizando los
hints USE_NL, USE_MERGE, USE_HASH.

Join Nested Loop

El optimizador escoge una tabla como directora o "outer". Por cada registro de la "outer", Oracle busca en
la "inner" todos los registros que enlazan con los registros del "outer". En el siguiente ejemplo la tabla dept
es la directora, y por cada registro que se encuentra en dept, se buscan coincidentes en emp.

Sort-Merge Join

Sólo se puede realizar un sort-merge join con equijoin (=). Oracle ordena las dos fuentes de datos (si nos
estan ya ordenadas) por las columnas del equijoin. Oracle une las dos fuentes de datos, por cada par de
filas, y devuelve las que coinciden los campos de join.
En el siguiente ejemplo, Oracle se recorre primero toda la tabla DEPT utilizando el índice PK_DEPT. Como
los datos ya vienen ordenados por el índice, no es necesario ordenar los datos de DEPT. A continuación, se
recorre toda la tabla EMP, y los ordena por el campo deptno (SORT por el campo del equijoin).
Una vez ordenados los dos grupos de datos, Oracle une uno con otro (MERGE JOIN).

Hash Join

Sólo se puede utilizarse con equijoin (=), y utilizando el optimizador por costes. Suponiendo dos grupos de
datos siguientes:
S={1,1,1,3,3,4,4,4,4,5,8,8,8,8,10}

B={0,0,1,1,1,1,2,2,2,2,2,2,3,8,9,9,9,10,10,11}

El proceso es el siguiente:

1.- Se selecciona la tabla de origen más pequeña (S), que se leera entera para formar una tabla hash. Si
esta tabla hash no cabe en memoria, se realiza por particiones o trozos (aplicando una función hash
interna de Oracle), que son almacenados en disco (fan-out). Además de crearse una tabla hash (con el
máximo número de particiones existentes y que caben en memoria), se crea un vector bitmap con los
valores existentes en el campo de join.

Vector Bitmap de S: {1,3,4,5,8,10}

2.- Comienza a leerse la otra tabla (B),

2.1.- Bit-Vector Filtering: Se comprueba si el valor del campo de join existe en el vector bitmap creado. Si
no está en ese vector, se desprecia esa fila directamente.

Las siguiente filas de B son descartadas directamente: {0,0,2,2,2,2,2,2,9,9,9, 11}

2.2.- Se aplica la función hash al campo de join de B, si esa partición hash está en memoria, se devuelve
directamente la fila unida.

2.3.- Si esa partición hash no está en memoria, se escribe en un segmento temporal en forma de
partición o trozo como se hizo con la tabla S. De esta forma, se obtendrán conjuntos de particiones, que
tienen los registros a unir.

Se repite este proceso hasta leer toda la fuente de datos de B.

3.- Comparación de particiones. A partir de ahora se irán cogiendo particiones dos a dos una de cada
origen de datos (S y B). Se crea en memoria la tabla Hash de la partición más pequeña y se compara en
memoria con la otra partición, devolviendo las filas que cumplen la join.

Se repite el paso 3 hasta que se hayan procesado todas las particiones creadas.

¿Qué join usar?

Si las filas a devolver por el join no son muchas (por debajo de 10.000 filas aprox.) :
Oracle tiende a utilizar "Nested Loop"
Si las filas a devolver por el join son muchas (por encima de 10.000 aprox.):
Se aconseja utilizar el "Hash Join". (Siempre y cuando se utilice el optimizador por costes. Si no fuera así,
no hay más remedio que utilizar el "sort-merge join").

El "sort-merge join" se desaconseja (para muchas filas), por los costes de ordenación, con lo que se prefiere
utilizar "hash join" con optimizador por costes.

En verdad, la elección que realiza Oracle es mucho más compleja, y no depende exclusivamente del número
de registros, teniendo en cuenta los costes de las operaciones de E/S, las áreas de memoria de ordenación
y hash, las estadísticas existentes, el modelo de datos, los índices de acceso, etc. Aquí únicamente se
expresan a grandes rasgos como puede decidirse un plan u otro, aunque es el optimizador quien tiene más
información para decidir el plan de join óptimo (y ni aun así no siempre consigue la mejor elección).
Tema 2

Módulo 1: En el SELECT

COUNT(*) frente a COUNT(1)

En versiones anteriores a la 8i, la utilización de count(*), count(1) o count(c1) podía tener distintos tiempos
de respuesta al utilizar o no índices.
A partir de Oracle 8, la utilización de count(*) o count(1) es indiferente ya que puede utilizar accesos por
índice (fast index scan), obteniéndose los mismos tiempos de respuesta.

Orden de los campos en la SELECT

El orden de los campos en la cláusula SELECT, no afecta en absoluto al rendimiento de nuestras sentencias.

S1. Campos necesarios en la select

Se aconseja evitar el uso de SELECT *, restringiendo la búsqueda de campos en la "select" a los campos que
realmente son necesarios. De esta forma se disminuye el volumen de información accedido y transportado
desde el servidor al cliente. También se facilita el acceso rápido a índices, frente al acceso a índice->tabla
(cuando exista un índice que lo permita).

En el caso de necesitar utilizar todos los campos de la tabla, el no utilizar el SELECT * también puede
beneficiar para dar mayor claridad al leer nuestro código SQL, y evitar errores en los cursores al asignar
valores a variables.
Tema 3

Módulo 1: En el FROM

Número de tablas en el FROM

El optimizador por COSTES, debe elegir el orden en que debe realizarse las join entre las tablas (siempre
los joins se realizan de dos en dos tablas). Esta elección se realiza durante el PARSE de la sentencia,
probando todas combinaciones posibles de join, eligiendo la de menor coste.
Sucede que cuando el número de tablas en el FROM (nº de joins) es muy alto, el optimizador decide no
probar todas las combinaciones posibles ya que tardaría mucho tiempo, probando únicamente algunas de
las combinaciones, con lo que hay mayor probabilidad de que no se elija el mejor plan de ejecución.
Cuando el total de tablas en el FROM es mayor de ocho, Oracle puede que ya no realice todas las
comprobaciones necesarias y se elijan planes no óptimos.
Se aconseja intentar evitar poner más de 8 tablas en el FROM, realizar la consulta en varias SELECTs
separadas. Si es completamente imposible, y como última medida, se podría utilizar el hint "ORDERED", y
ordenar las tablas en el FROM según el orden más adecuado para realizar las JOINs.

Orden de las tablas en el FROM

Para el optimizador por COSTES (que es el que debemos utilizar), el orden de las tablas en el FROM no afecta
a los planes de ejecución ni al rendimiento de la sentencia.
La única excepción es cuando se utiliza el hint "ORDERED".

Sentencias distribuidas

Oracle permite de forma transparente al usuario, realizar consultas sobre objetos que se encuentra en
bases de datos remotas. Sin embargo, la transparencia no se mantiene en los planes de ejecución, y en el
rendimiento, con lo que debe de tenerse especial cuidado con sentencias sobre bases de datos distribuidas.

Cómo se procesan las sentencias distribuidas

1.- SQL Remota: Cuando todas las tablas en el FROM pertenecen a tablas remotas en una misma base de
datos, se envía tal cual la sentencia a la base de datos remota, y como si estuviera en local, se obtiene el
plan de ejecución y se ejecuta. Los datos resultantes deben viajar de la base de datos remota a la base de
datos local.

2.- SQL Distribuida: cuando en el FROM aparecen tablas locales y remotas, o remotas en varias bases de
datos. Oracle debe desmembrar la sentencia, para mandar ejecutar la parte que corresponda a cada base
de datos, y ejecutar también la parte local en la base de datos local.

La base de datos local "se convierte en la directora" (si no se especifica lo contrario con un Hint) con lo que
es la base de datos que recibe todos los datos, y se encarga de hacer las "joins", agrupaciones y
ordenaciones.

Recomendaciones para sentencias distribuidas


 Utilizar siempre el optimizador por costes.
 Aunque el acceso distribuido sea transparente para la aplicación, es importante saber que los
planes de ejecución se ven fuertemente modificados, y es conveniente analizar cómo se realiza la
división de la query en las distintas bases de datos.
 No es cierto que al pasar de una base de datos no distribuida a una distribuida no debamos
preocuparnos. Más bien es todo lo contrario, deben revisarse todos los planes mixtos
(local/remoto).
 Siempre debe tenerse especial cuidado con el volumen de datos que vamos a "mover" por la red,
ya que todos los datos son traídos al servidor "director".
 Este volumen de datos no es el volumen final que devuelve la query, sino el volumen necesario
para realizar las joins. (Aunque mi select devuelva un solo registro, puede provocar mover miles
de registros para hacer las joins).
 Tener en cuenta la topología de la red, conociendo su ancho de banda, y su disponibilidad. En
función de estos parámetros valorar el movimiento de datos, y las caídas de red.
 En algunos casos, la utilización de vistas en las bases de datos remotas, ayuda a mejorar los
tiempos de respuesta, por la simplificación del proceso de "desmembrar" las sentencias,
obteniendo en algunos casos mejores planes.

Hints en tablas remotas

Los únicos hints que funcionan sobre tablas remotas son los hints de definición del orden de join, y el tipo
de join a realizar. Los hints de método de acceso, paralelismo, etc. no funcionan sobre las tablas remotas.
Tema 4

Módulo 1: En el modelo de datos

Exceso de índices

Los índices nos pueden permitir mejorar los tiempos de acceso a la información almacenada en las tablas.
Sin embargo, el exceso de índices empeora las operaciones de INSERT, UPDATE y DELETE sobre la tabla, ya
que además de almacenar la información en la tabla, debe actualizar el índice.
Este empeoramiento en los tiempos depende mucho de la degradación del índice, del volumen de datos,
de la cantidad y frecuencia de las modificaciones en la tabla, del tamaño de los campos a indexar, etc. Ni
aún así, recomendamos preocuparse de aquellas tablas que tienen más de seis índices, replanteandose si
es verdaderamente necesario.

Índices redundantes

Consideramos que un índice es redundante y debería eliminarse cuando ya existe otro índice que contiene
las mismas columnas a la izquierda. Los siguientes índices serían redundantes:

Este índice es redundante


por estar contenido en el Índice que debería quedarse
de la derecha
c1 c1, c2
c1, c2 c1, c2, c3

Indices y Foreign Keys

Si en la tabla "padre" se van a realizar operaciones de update o delete, debe definirse en la tabla "hija" un
índice por los mismos campos de la foreign key, por dos motivos:

1. Rendimiento, mejorando al hacer update o delete en la "padre" la búsqueda en la hija que asegure
que se puede hacer el update o delete.
2. Por bloqueos entre la tabla "padre" e "hija":

Operación
Sin índice en campos FK de la tabla Con índice en campos FK de
realizada
"hija" la tabla "hija"
sobre tabla
"padre" Bloqueos "tabla" hija Bloqueos "tabla" hija
Hasta versión Oracle 8i: Versiones 7, 8, 8i y 9i:
Toda la tabla, para evitar cambios Los registros afectados.
DELETE inconsistentes. No se puede hacer
UPDATE ningún insert, update o delete en la

tabla hija.
A partir de version Oracle 9i:
Solo se bloquean los registros
afectados por los cambio de la tabla
padre, no permitiendo cambiar los

campos de la foreign key.

Uso de índices compuestos

Un índice compuesto es un índice que está formado por varios campos. Es importante recordar que para
que el índice compuesto se utilice deben aparecer en la WHERE al menos el primer campo del índice.
Por ejemplo, tengo un índice formado por los campos c1, c2 y c3.

En el WHERE pregunto ¿Podría usar el


por: índice?
c1, c2, c3

c1, c2

c1

c2

c3

c2, c3

c1, c3

Excepciones:

1. Index Fast Full Scan: Cuando existe un índice que tiene todos los campos especificados en la
SELECT, con lo que en lugar de hacer un Full Table Scan, realiza un escaneo completo del índice
(Index Fast Full Scan).
2. Se solicita un ORDER BY y los campos pertenecen a un índice que además no permite nulo.
3. A partir de Oracle 9i, aparece la posibilidad de utilizar el índice a traves de un "INDEX SKIP SCAN",
que en algunos casos puede mejorar el rendimiento respecto a "FULL TABLE SCAN". En cualquier
caso, se considera poco efectivo el uso de "INDEX SKIP SCAN", siendo recomendable seguir
definiendo el orden adecuado en los campos del índice (Novedad Oracle 9i).

Índices efectivos

Los índices son importantes para agilizar las búsquedas de datos. Es importante intentar crear siempre
índices lo más efectivos posible, conforme a las siguientes normas:

1. Deben crearse en función de los campos utilizados en el WHERE.


2. Serán útiles cuando se busquen un conjunto reducido de datos, dentro del volumen total de la
tabla. (Si se buscan muchos datos, puede ser más efectivo un "Full Scan" de la tabla).
3. No debemos abusar del número de índice sobre nuestras tablas. ("Exceso de índices").
4. No debe ser redundante respecto a algún otro índice existente. ("índices Redundantes").
5. Para los índices compuestos debe intentar utilizarse como primeros campos del índice, los campos
con mayor selectividad. La selectividad de un campo (y por tanto del índice) se mide en función
de la repetición o no de ese campo dentro de la tabla. Por ejemplo, en una tabla de empleados,
la selectividad de los campos sexo, municipio y dni será de la siguiente forma:

Campo Selectividad

dni Muy
selectivo

municipio Menos selectivo

sexo Poco selectivo

Tener en cuenta "Uso de índices compuestos".

Tipos de datos obsoletos

Los siguientes tipos de datos son obsoletos para Oracle (actualizado para versión 9.0.2):

Debe cambiarse Paso a


Tipo de datos
por el tipo de obsoleto en Soporte del tipo de datos obsoleto
obsoleto
datos versión
LONG (char CLOB (char hasta 9.0.2. Soportado por compatibilidad. En
8.0.
hasta 2Gb) 4Gb) futuras versiones podría desaparecer.
RAW (binario
BLOB (binario 9.0.2. Soportado por compatibilidad. En
hasta 2000 8.0.
hasta 4Gb) futuras versiones podría desaparecer.
bytes)
LONG
BLOB (binario 9.0.2. Soportado por compatibilidad. En
RAW(binario 8.0.
hasta 4Gb) futuras versiones podría desaparecer.
hasta 2Gb)

Para aquellos tipos de datos que todavía están soportados por compatibilidad, es muy importante su
migración a los nuevos tipos de datos, para asegurar el funcionamiento de nuestras aplicaciones en
versiones futuras de Oracle, y para poder utilizar las mejoras que aportan los nuevos tipos de datos.

Mi índice no se usa

A continuación repasamos algunas causas por lo que mi índice no se utilice.


1. ¿Existe el índice?
2. ¿Están las estadísticas actualizadas?
3. Al menos el primer campo cabecera del índice debe aparecer en el WHERE. (Regla "M4. Uso de
índices compuestos") Excepciones:
- Index Fast Full Scan: Cuando existe un índice que tiene todos los campos especificados
en la SELECT, con lo que en lugar de hacer un Full Table Scan, realiza un escaneo completo
del índice (Index Fast Full Scan).
- Se solicita un ORDER BY y los campos pertenecen a un índice que además no permite
nulos.
- A partir de Oracle 9i, aparece la posibilidad de utilizar el índice a traves de un "INDEX SKIP
SCAN", que en algunos casos puede mejorar el rendimiento respecto a "FULL TABLE
SCAN". En cualquier caso, se considera poco efectivo el uso de "INDEX SKIP SCAN", siendo
recomendable seguir definiendo el orden adecuado en los campos del índice (Novedad
Oracle 9i).

4. ¿Los campos del índice se utilizan para la join? Si es así el uso del índice depende de:

- El tipo de JOIN que se realiza. (NESTED LOOP es la única que permite el uso de un índice).
- El orden en que se realizan las JOIN. En concreto el NESTED LOOP dependiendo de que
tabla sea la entrante y cual la saliente puede que se use o no el índice.

5. ¿Se está aplicando alguna función sobre el campo indexado? (Regla "W4. Comparadores e
índices")
Una función del tipo SUBSTR(campo1,1,2) provoca que el índice no se use, a no ser que se utilice
un índice basado en función (el índice se crea aplicando la función SUBSTR).
6. ¿Se está realizando alguna conversión implícita de tipos? (Regla "W5. Conversiones implícitas")
Las conversiones implícitas pueden inhabilitar los índices. No utilice conversiones implícitas.
7. Usa el índice, pero no el que yo quiero.
El uso de un índice frente a otro es elegido por el optimizador por costes en función "del coste"
menor que implica usar uno frente a otro. Es muy muy difícil que dos índices tengan el mismo
coste (mismo tamaño, mismo número de hojas, profundidad, bloques, etc.). En el caso de que
esto sucediera, el optimizador por costes los elige en orden alfabético. Siempre queda la
posibilidad de los Hints, para asegurar el uso del índice que deseemos. (Regla "H1.
Consideraciones sobre los Hints")
8. Mi índice no es bueno.
Puede ser que mi índice no sea muy bueno (poco restrictivo). (Regla "M5. Índices efectivos").
Puede ser que los datos no están uniformemente distribuidos, y al no tener histogramas el
optimizador saque conclusiones equivocadas. (Véase "Generales sobre SQL-> El optimizador-> Los
histogramas").
9. ¿Preguntas por valores nulos?
Los valores nulos no son almacenados en el índice (para índices compuestos si todos los valores
son nulos, no se almacenan en el índice). No se puede usar un índice al preguntarse por valores
nulos (Regla "W4. Comparadores sobre campos indexados.")
10. Se está utilizando una tabla remota.
La utilización de tablas remotas (querys distribuidas) tiene su complejidad para el optimizador.
Más adelante publicaremos las recomendaciones para querys distribuidas.
11. ¿Está utilizando Parallel Query?
Asegurese que no está utilizando Parallel Query, y si sus tablas tienen activadas algún grado de
paralelismo. La utilización de parallel query provoca tendencia a realizar "Full Table Scan en
paralelo" frente a acceso por índices.
12. ¿Está usando bind variables?
En algunas circunstancias el uso de bind variables provoca el no usar índices ya que el optimizador
no conoce el valor del dato utilizado. y realiza precálculos fijos que puede ser erróneos.
Bloqueos en índices bitmap

Los índices Bitmap son un tipo de índices especiales de Oracle, que pueden resultar más beneficiosos que
los B*Tree, cuando el número de valores distintos que puede tomar el campo es muy reducido.
Estos índices, para cada uno de los valores distintos, almacenarán internamente un 1 ó un 0 por registro,
en función de si ese registro tiene o no ese valor. Pueden llegar a ocupar mucho más espacio, pero pueden
ser bastante más rápidos.
Sin embargo, tienen una desventaja muy importante, para entornos en los que se realicen modificaciones
o inserciones de forma concurrente por varios usuarios.
Cualquier operación DML (Data Manipulation Language) como son insert, update o delete, provocará un
bloqueo exclusivo en "parte" del índice, lo que puede impedir operaciones de modificación sobre esa tabla,
siempre y cuando afecte a los campos que forman parte del índice bitmap.
Normalmente se suelen utilizar de forma muy beneficiosa en entornos de toma de decisión (data
warehouse) y muy poco en entornos transaccionales online (OLTP).
Tema 5

Módulo 1: En el WHERE

Uso de Bind Variables

La ejecución de una sentencia SQL requiere de unos pasos previos de "compilación" (parse) en los que se
analiza su sintaxis, se comprueba la existencia de los objetos, se verifican los permisos, y se decide su plan
de ejecución.
Desde versión 7 de Oracle, se incluyó una cache de sentencias SQL, albergada dentro de la Shared Pool que
se encarga de almacenar todas las sentencias SQL más recientes que se han ejecutado en la base de datos.
El objeto de esta cache es mejorar los tiempos de respuesta a la hora de ejecutar una misma sentencia
SQL. Si esa sentencia se encuentra en la cache, puede evitarse realizar los pasos previos de chequeo y plan
de ejecución, pasando directamente a ejecutar la sentencia SQL a partir de la información almacenada en
la cache.

Hard Parse
Cuando una sentencia no se encuentra en la cache de la Shared Pool, debe realizar un "hard parse" o un
parse completo, que implica obtener memoria para esa sentencia, chequear la sintaxis, los objetos, los
tipos de datos, y obtener su plan de ejecución. Este proceso es un proceso complejo, que requiere un uso
importante de CPU.

Soft Parse
Si esa sentencia ya se encuentra en la Shared Pool, "y es compartible", Oracle solo debe realizar un "soft
parse", mucho menos costoso.

Sentencias compartibles en la Shared Pool.


Dos sentencias se consideran compartibles en la Shared Pool, permitiendo así agilizar la ejecución
utilizando la información de la cache, cuando se cumplen las siguientes condiciones:

- Están escritas a nivel ASCII exactamente igual. (incluyendo letras, espacios en blanco. mayúsculas,
etc.)
- Los objetos referenciados en ambas sentencias son los mismos (mismo usuario).
- En caso de utilizarse bind variables, son del mismo tipo y tamaño para ambas sentencias.
- El optimizador fijado para las dos sesiones que ejecutan las dos sentencias, debe ser exactamente
el mismo (rule, first_rows, all_rows).
- La configuración del NLS (National Language Support) debe ser la misma, para las dos sesiones
que ejecutan las dos sentencias.

Qué son las bind variables


Consiste en substituir los valores utilizados en las búsquedas de las condiciones WHERE, por unas variables.
Por ejemplo las siguientes sentencias:

SELECT name, surname, address FROM employees WHERE dept_id = 20;


SELECT name, surname, address FROM employees WHERE dept_id = 30;

Expresan dentro de la condición WHERE los literales 20 y 30 como identificadores de departamento


(dept_id). Estas dos sentencias a nivel ASCII no son iguales, lo que significa que no son "compartibles". A la
hora de ejecutarlos será obligatorio realizar un "hard parse" si anteriormente no estaban ya en la cache.
Sin embargo, si está sentencia la cambiamos por:
SELECT name, surname, address FROM employees WHERE dept_id = :bindA;

Cada vez que esta sentencia se ejecute, podrá ser compartida y aumenta la probabilidad de realizar "soft
parse", independientemente del valor que se le asigne a la variable ":bindA".

Ventajas de la bind variables y las sentencias compartibles


Favorecen el uso de la cache de sentencias dentro de la Shared Pool, disminuyendo el consumo de memoria
y CPU en la base de datos. Especialmente útil en sentencias muy utilizadas.

Desventajas de la bind variables


Sucede que en algunos casos a la hora de realizar el parse de la sentencia y obtener el plan de ejecución,
el desconocer el valor por el que estamos preguntando (la variable), provoca que los planes de ejecución
sean peores. Estos casos son:

- Utilizando histogramas. El histograma ayuda al optimizador a conocer la distribución de datos para


un valor en concreto. El utilizar bind variables ocultando el valor buscado, impide el uso de
histogramas.
- Particionamiento. En tablas o índices particionados, los valores de búsqueda nos definen las
particiones a utilizar (pudiendo recorrer todas las particiones, o solamente las necesarias). Al usar
bind variables, el optimizador desconoce los valores que busco, con lo que no podrá restringir el
número de particiones a utilizar, tendiendo por lo tanto a realizar procesos que recorren todas las
particiones, con la consiguiente degradación de las búsquedas.
- Sentencias pseudo-dinámicas. Es un ejemplo típico de sentencias utilizadas en pantallas de
búsqueda libre, cada vez más usadas en nuestras aplicaciones. De esta forma, en estas pantallas
permitimos al usuario que busque a su libre antojo, por un número importante de campos
distintos. Sin embargo, la sentencia SQL que genera nuestra aplicación es fija, y no se adapta a lo
que realmente el usuario esta buscando en cada momento. Esta práctica es completamente
desaconsejable por la complejidad de las sentencias que ejecutamos, por la cantidad de
condiciones LIKE que generamos, y por la imposibilidad de optimizar las búsquedas conforme a
reglas fijas. Por nuestra parte aconsejamos:
- No utilizar estas sentencias fijas, y hacerlas dinámicamente dependiendo de los criterios de
búsqueda.
- Además de ser dinámicas, intentar dividir la búsqueda en varias pantallas agrupando y acotando
las condiciones de búsqueda (por ejemplo, si buscas por nombre, obligar a poner el primer
apellido; si buscas por dirección obligar a poner ciudad o código postal, etc...) forzando el uso de
campos obligatorios en la búsqueda. (Estos campos obligatorios serán candidatos a incluirse en
los índices de la base de datos).
- En último lugar, como menos recomendado, el uso de las sentencias pseudo-dinámicas. Estas
sentencias suelen utilizar un campo binario que sirve como "flag" para saber si estoy preguntando
o no por un campo determinado. Veamos el siguiente ejemplo:

En esta sentencia se utilizan las bind variables: "vsurname1", "vsurname2" y "vname" de forma
correcta para identificar los valores de búsqueda por apellidos y nombre.
Las bind variables "flagsurname2" y "flagname" se utilizan para determinar si estamos buscando
o no por segundo apellido y por nombre. Si estos flags valen 0 quiere decir que no buscan por ese
campo, y gracias al OR impiden la búsqueda en el resto de condición (surname2, customer_name).
Si valen 1, si buscan por la condición siguiente al OR.
Estos flags consiguen en parte mejorar el rendimiento de estas sentencias, siempre y cuando en
estos flags no se utilicen con bind variables. Si buscamos por nombre del cliente, por ejemplo, la
sentencia debería ejecutarse de la siguiente forma:

- Consultas por rangos. En algunas consultas donde se pregunta por rangos (>, <, between), o
cuando se utiliza un LIKE por ejemplo, el uso de bind variables puede provocar planes peores. Esto
es debido a que el optimizador no conoce el valor exacto de la variable, y no puede calcular
"cuanto" de grande es el rango a consultar. El optimizador asume reglas estimadas fijas, y puede
provocar planes peores.

Como norma general se aconseja el uso de las bind variables por el importante impacto que tiene en el
rendimiento de la base de datos. En los casos de utilizar particionamiento, histogramas, o sentencias
"pseudo-dinámicas", utilizarlas en los campos no afectados por el particionamiento, histrograma o "flag"
respectivamente. Ante cualquier duda puedes consultar con el equipo de Administración de Base de Datos.

Grandes INLIST

Consideramos que grandes INLIST son cláusulas IN en las que se detallan una gran lista de valores. Es
aconsejable substituir los grandes INLIST por tablas de búsqueda (realizando una join con una tabla que
contiene valores a buscar).

Operadores sobre campos indexados

La utilización en el WHERE de operadores sobre campos indexados (operadores a la izquierda del


comparador), puede imposibilitar el uso de índices. A continuación os mostramos ejemplos de malas y
buenas prácticas:

Mala práctica en
Buena práctica en WHERE
WHERE
WHERE importe +
WHERE importe= anticipo - 5000
5000 = anticipo
WHERE
TRUNC(fecha) = WHERE fecha BETWEEN TRUNC(sysdate) AND TRUNC(sysdate)+ 0.99999
TRUNC(sysdate)
WHERE
account_name =
WHERE account_name LIKE NVL( :acc_name,'%')
NVL( :acc_name,
account_name)

Comparadores sobre campos indexados

La utilización en el WHERE de algunos comparadores, puede imposibilitar el uso de índices. A continuación


os mostramos algunos ejemplos:
Comparador ¿Usa el índice?

WHERE campo = valor

WHERE campo <> valor

WHERE campo >valor

WHERE campo < valor


WHERE campo BETWEEN valor1 AND
valor2

WHERE campo LIKE 'va%'

WHERE campo LIKE '%lor'

WHERE campo LIKE '%alo%'

WHERE campo NOT LIKE 'val%'

WHERE campo IS NULL *

WHERE SUBSTR(campo,1,2) = 'MA' **


* Valores nulos no almacenados en el índice.
** Si no se utiliza un índice basado en la misma función (SUBSTR(campo,1,2)), no se utilizará el índice.

Conversiones implícitas

Cuando en una cláusula WHERE no coincide el tipo de datos de la columna, con el tipo de datos del valor
por el que se pregunta, Oracle puede no generar error, y realizar internamente una conversión
(conversiones implícitas).
Estas conversiones implícitas suelen provocar en muchos casos la no utilización de los índices.
Se considera una mala práctica permitir a Oracle que realice la conversión implícita. Debemos intentar
que los tipos de campo de las variables y los campos coincidan, o realizar expresamente la conversión.
Por ejemplo, en la tabla DEPT tenemos un índice con el campo "descripción" (varchar2). Esto es lo que
sucede:
Condición ¿Usa el índice?

WHERE descripción = '1'

WHERE descripción = TO_CHAR(1)

WHERE descripción = 1
Having y Where

Es importante distinguir y utilizar correctamente el HAVING y el WHERE.


WHERE se utiliza para filtrar los registros a devolver.
HAVING, se aplica después que el WHERE, con funciones de agrupación (GROUP BY), y sirve para filtrar
grupos.
Es adecuado filtrar siempre con el WHERE el mayor número de registros, para que las funciones de
agrupación manejen menos registros, en vez de no filtrar con WHERE sobrecargando la agrupación.

Mala práctica con HAVING Buena práctica con HAVING

ROWNUM

La cláusula ROWNUM en un WHERE, se utiliza para devolver únicamente un número de registros del total
de registros que cumplen el resto de la condición WHERE.
La cláusula ROWNUM únicamente se puede utilizar con el operador "<" o "<=".
Respecto al uso conveniente o no de ROWNUM, en nuestra opinión se debe utilizar ya que es la única
manera que tenemos de "cortar" la devolución de registros a un total dado, pero debemos tener muy claro
que es lo que realmente sucede internamente en Oracle, ya que aunque se utilice el ROWNUM, Oracle puede
ser que utilice muchos más registros.
Primer ejemplo
Veamos el siguiente ejemplo, en este caso el optimizador consigue arrastrar el número de registros del
ROWNUM en casi todo el plan de ejecución, con lo que no parece que sea muy perjudicial.

Este es su plan de ejecución con su estimación de coste y de registros:

Y este es la ejecución real (de registros), obtenida con tkprof:


Estudiemos el plan de ejecución obtenido. Tal y como se explica en el tema Generales SQL -> Plan de
ejecución -> Interpretando el plan", Oracle ejecuta las sentencias realizando operaciones de dos en dos,
con lo que:

1. Oracle realiza "TABLE ACCESS FULL" a toda la tabla VUELOS, recorriéndose en total 57.711
registros (no limitado por ROWNUM). A continuación se recorre a través de la primary key de la
tabla PLAZAS, llamada PLA_PK, pero en este caso si limita al número de registros de ROWNUM (9).
Se realiza un Hash Join, de estos dos bloques de datos, devolviéndo en total 9 registros.

2. Se realiza un "TABLE ACCESS FULL" de toda la tabla COMPANIAS, sin limitarse por ROWNUM,
encontrándose en total 8 registros. A continuación se realiza un Hash Join, de los 8 registros de
COMPANIAS y los 9 registros devueltos en el paso 1. El hash join devuelve 9 registros.

3. Se realiza el "COUNT STOPKEY" que es realmente contar hasta los 9 registros que se deben
devolver para respetar el ROWNUM<10 que aparece en la cláusula WHERE. En verdad este COUNT
en esta ejecución no filtra, ya que en el paso anterior ya se devolvían únicamente los 9 registros
deseados.

Tal y como se observa en este plan, las operaciones de "FULL TABLE SCAN" no se han limitado por el
ROWNUM, mientras que la operación "INDEX (FAST FULL SCAN)" sí que ha limitado los registros analizados,
y devolviendo 9 registros, ya ha conseguido devolver lo solicitado en el ROWNUM.
Segundo ejemplo
Veamos la misma sentencia, pero en este caso hemos puesto un par de Hints, para conseguir un plan de
ejecución mucho peor, pero que si nos permite mostrar cómo no se limita en todo el plan, por la condición
ROWNUM.
Este es su plan de ejecución con su estimación de coste y de registros:

Y este es la ejecución real (de registros), obtenida con tkprof:

En este plan de ejecución se observa que para devolver los nueve registros solicitados por ROWNUM<10,
se han recorrido en total (en diversas tablas) un total de 3.313.493 registros.

Antijoin

General Anti-Join
Un antijoin es una consulta que devuelve registros de una tabla que no se corresponden con registros de
otra tabla. De alguna forma, es la operación contraria a un "join".
En Oracle existen las siguientes formas de realizar un Anti-Join:

 Con NOT IN
 Con NOT EXISTS
 Con MINUS
 Con OUTER-JOIN

Uso de NOT EXISTS frente a NOT IN


Utilizar NOT EXISTS en lugar de NOT IN, cuando existe un índice en el campo de join de la tabla de la
subselect.
Tenemos la siguiente sentencia con NOT IN, y con su plan de ejecución correspondiente:

Oracle realiza primero la subselect (SELECT deptno FROM emp), con lo que como no tiene ninguna cláusula
WHERE se recorre toda la tabla emp. A continuación se recorre la tabla dept, buscando los departamentos
que no tienen ningún empleado asociado.
Sin embargo, reconstruyendo la sentencia con NOT EXISTS, obtenemos el siguiente resultado:

Oracle ahora realiza un NESTED LOOP, con lo que por cada departamento (DEPT) realiza una búsqueda POR
íNDICE en la tabla empleados (EMP). Así hemos evitado realizar el FULL SCAN de emp, accediendo ahora
por el índice del campo de join.
Tener en cuenta que asumimos que el volumen de la tabla EMP es suficientemente grande como para que
merezca la pena usar un índice frente a un full scan de toda la tabla.
Tema 6

Módulo 1: Ordenaciones/Agrupaciones

General sobre la ordenación

Las ordenaciones son operaciones costosas para la base de datos, y pueden ser uno de los motivos
importantes por los que nuestras sentencias no sean tan rápidas como deseamos.
Como norma general deben evitarse siempre que no sean imprescindibles.
Las operaciones, que obligan a una ordenación son:

 Creación de un índice
 Utilización de las cláusulas de agrupación GROUP BY, DISTINCT
 Utilización de la cláusula ORDER BY
 Join utilizando SORT-MERGE
 Utilización de los operadores de conjunto UNION, INTERSECT y MINUS

ORDER BY

Utilizar la claúsula ORDER BY únicamente cuando sea importante para nuestra aplicación el obtener los
datos ordenados. Es frecuente utilizar ORDER BY sin ser necesario la ordenación.

DISTINCT y GROUP BY

Utilizar la claúsula DISTINCT cuando estemos seguros que se pueden devolver registros duplicados y nos
interese eliminarlos. Muy frecuente utilizar distinct sin ser necesario, forzando a una ordenación.
Lo mismo sucede con la cláusula GROUP BY, utilizándalo sin funciones de agrupación de forma innecesaria.

UNION ALL frente a UNION

Los operadores de conjunto UNION, agregan los resultados de varias SELECTs. UNION frente a UNION ALL
realiza una ordenación adicional para eliminar registros repetidos.
Utilizar UNION ALL frente a UNION cuando no nos importe el recibir registros repetidos, o estemos seguros
de no recibir ningún repetido.
Tema 7

Módulo 1: En el INSERT

Especificar Campos en el INSERT

Se considera una muy mala práctica, el realizar INSERT indicando directamente los valores (VALUES)
presuponiendo el orden de los campos en la tabla. Cambios en el modelo de datos (eliminar columnas, o
cambios de orden) pueden provocar que nuestros programas fallen, o lo que es peor, que no fallen y se
inserten valores en columnas equivocadas.

INSERT INTO empleados


VALUES ('García','Aranda', 'Oscar','979123456');
INSERT INTO empleados (apellido1, apellido2,
nombre, telefono1)
VALUES ('García','Aranda', 'Oscar','979123456');
Tema 8
Módulo 1: Hints
Consideraciones generales sobre el uso de Hints
El optimizador no siempre obtiene los mejores planes de ejecución. En algunos casos el conocimiento del
negocio y de la información almacenada puede ayudar a elegir planes de ejecución mejores.
Los hints son propuestas que se realizan al Optimizador de Oracle para proponer como realizar los planes
de ejecución. El optimizador los considera como propuestas, y en algunos casos puede no ser tenido en
cuenta por el optimizador.
La utilización de Hints conlleva los siguientes riesgos:
 Su uso se propone con un volumen actual, en el que se comprueba que aporta
beneficios. Sin embargo, el cambio del volumen por la evolución propia del sistema puede
provocar que los Hints puestos en un momento dado, no sean adecuados en unos meses en
función de los cambios de volumen.
 La evolución del software de Oracle, modificando los hints, o ampliando funcionalidad
puede provocar cambios importantes en el optimizador. Puede suceder que en nuevas versiones
de Oracle, determinados Hints ya no deban ser utilizados, con lo que debemos revisar las
sentencias que los utilizan.
En los casos en que se decida el uso de hints no debe olvidarse nunca que:
 Los hints deben utilizarse con moderación, en los casos realmente imprescindibles, tras
descartar otras posibilidades.
 Obligan a mantener una documentación clara de dónde se usan, ya que periódicamente
deben revisarse las sentencias que los utilizan, para comprobar si siguen siendo adecuados (por
cambios de versión de Oracle, o por la evolución del volumen y los datos).
¿Por qué no funciona mi Hint?
Los hints son propuestas que se hacen al optimizador, y puede que en algunos casos no sean tenidos en
cuenta por el optimizador.
En cualquier caso, si tu Hint no funciona no olvides comprobar que estás respetando las siguientes reglas:
 Deben estar escritos obligatoriamente de la forma /*+ HINT HINT */
 El uso de cualquier Hint (excepto RULES) implica la utilización del optimizador por
costes. Recuerda que las estadísticas deben estar ejecutadas y actualizadas.
 El uso del Hint RULES (optimizador por reglas) se anula si se usa alguna funcionalidad
que requiere optimizador por costes.
 Los hints no deben hacer referencia al nombre del esquema. Se puede solucionar
utilizando un alias en la tabla. El siguiente ejemplo es incorrecto:
SELECT /*+ index(scott.emp emp1) */ ...
 En caso de usar alias, en el hint debe usarse el alias y no el nombre de la tabla. Por
ejemplo:
SELECT /*+ FULL ( myalias ) */ empno FROM emp myalias WHERE empno > 10;
 Justo después del "+" del hint, debe haber un espacio en blanco, en los bloques PL/SQL.
 No utilizar hints que no tengan sentido en la sentencia. Por ejemplo, utilizar el hint
"FIRST_ROWS" en una sentencia que tiene un ORDER BY.
 Para el hint INDEX:
o Su formato es: SELECT /*+ index(TABLE_NAME INDEX_NAME) */ col1...
o Es obligatorio utilizar el nombre de la tabla (el alias si se utilizan)
o El nombre del índice es opcional. Si no se especifica, el optimizador elige el
índice. Si se especifica, debe ser el nombre correcto, ya que sino se invalida el hint.
 Para querys distribuidas, en tablas remotas, los únicos hints que funcionan son los de
orden de join, y tipo de join.
Hints para el uso del optimizador
Los hints relacionados con el uso del optimizador son:
 ALL_ROWS
 FIRST_ROWS
 CHOOSE
 RULE
ALL_ROWS
Especifica que se utilice el optimizador por costes, intentando conseguir el mejor "throughput". (consumo
mínimo de recursos, penalizando quizás el tiempo de respuesta).
Formato:

FIRST_ROWS
Especifica que se utilice el optimizador por costes, intentando conseguir el mejor tiempo de respuesta,
penalizando quizás el consumo de recursos.
El optimizador en modo FIRST_ROWS tiene mayor tendencia al uso de índices, aunque implique aumento
de accesos a disco. (no siempre aconsejable).
Formato

CHOOSE
Especifica que se utilice el optimizador por costes si alguna tabla posee estadísticas. Si no, se utilizará el
optimizador por reglas.
Formato

RULE
Especifica que se utilice el optimizador por reglas.
Formato

Hints para el método de acceso


Los hints relacionados con el método de acceso a las tablas son:
 FULL
 ROWID
 CLUSTER
 HASH
 INDEX
 INDEX_ASC
 INDEX_COMBINE
 INDEX_JOIN
 INDEX_DESC
 INDEX_FFS
 NO_INDEX
 AND_EQUAL
 USE_CONCAT
 NO_EXPAND
 REWRITE
 NOREWRITE
FULL
Especifica que se realice un acceso recorriendo toda la tabla entera (Full table scan)
Formato:

ROWID
Especifica que se realice un acceso a través del RowId
Formato

INDEX
Especifica que se utilice un índice determinado.
Formato

Hints para el orden de realización del JOIN


Los hints relacionados con el orden de realización del JOIN son:
 ORDERED
 STAR
Hints para las operaciones de JOIN
Los hints relacionados con las operaciones de JOIN son:
 USE_NL
 USE_MERGE
 USE_HASH
 DRIVING_SITE
 LEADING
 HASH_AJ
 MERGE_AJ
 HASH_SJ
 MERGE_SJ
Hints para ejecuciones paralelas
Los hints relacionados con las ejecuciones paralelas son:
 PARALLEL
 NOPARALLEL
 PQ_DISTRIBUTE
 APPEND
 NOAPPEND
 PARALLEL_INDEX
 NOPARALLEL_INDEX
Otros hints
A continuación se muestran otros hints, de uso variado:
 CACHE
 NOCACHE
 MERGE
 NO_MERGE
 UNNEST
 NO_UNNEST
 PUSH_PRED
 NO_PUSH_PRED
 PUSH_SUBQ
 STAR_TRANSFORMATION
 ORDERED_PREDICATES
Tema 9
Módulo 1: Programación y SQL
Commits en bucle
La operación de commit, es una operación compleja para la base de datos, no necesariamente lenta. El
incluir dentro de un bucle la ejecución de un commit ("en cada vuelta") no resulta adecuada por
sobrecargar a la base de datos, que provoca que los rendimientos puedan empeorar.
Solo debería hacerse, cuando por el propio negocio, necesite asegurarse que en cada vuelta del bucle los
cambios se han realizado y son visibles al resto de usuarios. Si suele utilizarse, pero no para cada vuelta, la
realización de commits tras un volumen importante de cambios.
WHILE (i< num_elementos) LOOP
..... SELECT ...
...... INSERT .....
COMMIT
END LOOP;
WHILE (i< num_elementos) LOOP
..... SELECT ...
...... INSERT .....
END LOOP;
COMMIT

Commits y cierre de cursores


Algunos lenguajes de programación, como por ejemplo en Pro*C, el realizar un commit en nuestro
programa puede implicar un cierre de los cursores que están abiertos.
Es aconsejable que no se produzca este cierre de los cursores, porque el proceso de reabrir el cursor
puede ser costoso y provocar esperas innecesarias.
¿Cómo se podría solucionar?
No hacer commit durante todo el proceso, y en el commit final se
cierran los cursores.
Cuando el cursor procesa muchos registros, consume mucho
segmento de rollback, lo cual no es adecuado.
Parametrizando nuestro lenguaje de programación, para que no se
cierren los cursores.
Parametrizando nuestro lenguaje de programación:
Pro*C (parámetros de compilación)
MODE= oracle
CLOSE_ON_COMMIT=no
(Tened cuidado que al cambiar de modo ANSI a ORACLE, cambia el valor de la
variable SQLCODE al no haber más datos en un cursor. Para ANSI es 100 y
para ORACLE es 1403.
Tema 10
Módulo 1: PL/SQL
Uso de "Cursor FOR" en lugar de bucle CURSOR.
Hasta versión 8 de Oracle, el tratamiento de cursores consistia en un OPEN del cursor, un bucle que
procesa de los datos y un CLOSE que cierra el cursor.
Desde versión 8i de Oracle, existe otra manera más simple y con mejor rendimiento. Es una versión
especial del bucle FOR pensada para cursores. El propio FOR realiza OPEN, FETCH y CLOSE de los
cursores.

Uso de %TYPE y %ROWTYPE.


A la hora de definir nuestras variables en PL/SQL, para almacenar datos de nuestras tablas, podemos o
bien indicar en nuestro código el tipo de dato, o bien hacer que sea dinámico y en tiempo de ejecución
tome el tipo de datos del modelo de datos.
Esta segunda opción usando %TYPE y %ROWTYPE nos permite asegurar que un cambio en el modelo de
datos, no implica tener que retocar todos nuestros programas PL/SQL.
Uso innecesario del DUAL
Utilizar innecesariamente DUAL para obtener resultados de funciones, provoca accesos a la base de datos
no deseados. Estos problemas se suelen agravar cuando este uso de hace dentro de bucles.

Uso inadecuado de SELECT COUNT


Algunos programadores están acostumbrados a realizar un SELECT COUNT para revisar si un cursor
devolverá datos. Si devuelve datos, se procede a abrir el cursor con los datos. Esta forma de programar,
obliga a ejecutar dos veces el cursor.
Es preferible abrir el cursor la primera vez, y comprobar si en el primer FETCH hay datos.
Cursores Explícitos frente a Cursores Implícitos
Los cursores implícitos son aquellos que no se declaran expresamente como CURSOR en el código.
Internamente Oracle se encarga de realizar todos los pasos de abrir, fetch y cerrar. Se pueden usar en
PL/SQL sólo cuando devuelven un único registro.
Los cursores explícitos son los que declaramos expresamente como CURSOR.
Un cursor que devuelve un solo registro podría utilizarse en PL/SQL como explícito o implícito. Se
recomienda utilizar cursores de forma explícita, por tener mejor rendimiento aunque su sintaxis sea un
poco más compleja. Los cursores implícitos obligan a ejecutar siempre dos fetch (traer registro). El
primero para traerse el primer registro, y el segundo fetch para asegurar que no hay más valores (ya que
solo deben devolver un registro).
Por contra, en los explícitos traigo el primer registro y cierro el cursor. No realizo dos fetch ("asumo" que
estoy seguro que el cursor no devuelve nunca más de un registro, y me evito un nuevo fetch).
Otras ventajas de los cursores explícitos son:
 Se pueden cerrar por código, liberando antes recursos, sin tener que esperar a que
termine todo el bloque PL/SQL.
 Si el curso posee Bind Variables, se pueden volver a reabrir, tomando los nuevos
valores. Es más rápido por aprovechar estructuras internas de Oracle creadas en la definición del
cursor.
Tema 11
Módulo 1: Trucos SQL
Consultas Ranking
A partir de Oracle 8i, aparecen ampliaciones al SQL, con la inclusión de funciones analíticas.
Entre estas nuevas funciones aparecen RANK y DENSE_RANK que nos permiten realizar un ranking de
ordenación de los registros obtenidos. Veamos un ejemplo:
Queremos obtener un ranking de sueldos de los empleados de una empresa:

La diferencia entre RANK y DENSE_RANK es que con DENSE_RANK no se permiten "huecos" en los valores
de ranking, cuando se repiten los valores por los que se hace el ranking.

Potrebbero piacerti anche