<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" href="oracle_faq.xsl"?>

<bd_faq actualizado="15 de Enero de 2003" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="oracle_faq.xsd">
	<faq	codigo="1"
			titulo="¿Como puedo usar el optimizador basado en costes para optimizar una consulta?" 
			categoria="Optimización" 
			nivel="3" 
			autor="JM" 
			mail="jose.man_ARROBA_iespana.es">
		<parrafo>
			<linea>El optimizador basado en costes (Cost Based Optimizer, CBO) se introdujo en Oracle 7 y se basa en calcular el coste de distintos planes de ejecución y eligir para la ejecución el plan de ejecución con menor coste. En su contra, está el optimizador basado en reglas (Rules Based Optimizer, RBO), que se basa en una reglas prefijadas para decidir el plan de ejecución de una consulta.</linea>
		</parrafo>			
		<parrafo>
			<linea>Para calcular el coste de una consulta, el optimizador se basa en las estadísticas almacenadas en el catálogo de Oracle, a través de la instrucción:</linea>
		</parrafo>			
		<parrafo tipo="PRE">
			<linea>  ANALYZE [TABLE,INDEX] &lt;object_name&gt; [COMPUTE, ESTIMATE] STATISTICS;</linea>
		</parrafo>
		<parrafo>
			<linea>Si no existen datos estadísticos para un objeto (por ejemplo, porque se acaba de crear), se utilizarán valores por defecto. Además, si los datos estadísticos está anticuados, se corre el riesgo de calcular costes basados en estadísticas incorrectas, pudiendo ejecutarse planes de ejecución que a priori pueden parecer mejores.</linea>
       <linea>Por esto, si se utiliza el optimizador basado en costes, es muy importante analizar los objetos periodicamente (como parte del mantenimiento de la base de datos). Como las estadísticas van evolucionando en el tiempo (ya que los objetos crecen o decrecen), el plan de ejecución se va modificando para  optimizarlo mejor a la situación actual de la base de datos. El optimizador basado en reglas hacía lo contrario: ejecutar siempre el mismo plan, independientemente del tamaño de los objetos involucrados en la consulta.</linea>
		</parrafo>			
		<parrafo>
			<linea>Dentro de la optimización por costes, existen dos modos de optimización, configurables desde el parámetro OPTIMIZER_MODE:</linea>
		</parrafo>			
		<parrafo>
       <linea>- FIRST_ROWS: utiliza sólo un número determinado de filas para calcular los planes de ejecución. Este método es más rápido pero puede dar resultados imprecisos.</linea>
		</parrafo>			
		<parrafo>
       <linea>- ALL_ROWS: utiliza todas las filas de la tabla a la hora de calcular los posibles planes de ejecución. Este método es más lento, pero asegura un plan de ejecución muy preciso. Si no se indica lo contrario, este es el método por defecto.</linea>
		</parrafo>			

		<parrafo>
       <linea>El optimizador basado en costes se utilizará en alguna de las siguientes situaciones:</linea>
		</parrafo>			
		<parrafo>
       <linea>1.- Si el parámetro OPTIMIZER_MODE del archivo INIT.ORA está definido como ALL_ROWS o FIRST_ROWS.</linea>
       <linea>2.- Si existen estadísticas para alguna de las tablas involucradas en la consulta y el parámetro OPTIMIZER_MODE es distinto a RULE.</linea>
       <linea>3.- Si se incluye un "hint" distinto a +RULE.</linea>
       <linea>4.- Si se ha establecido la variable OPTIMIZER_GOAL a ALL_ROWS o FIRST_ROWS a nivel de sesión.</linea>
       <linea>5.- En Oracle 7.3, si alguna tabla tiene un grado de paralelismo distinto de 0.</linea>
		</parrafo>			
	</faq>

	<faq codigo="2"
			titulo="¿Cómo puedo saber el optimizador que está siendo usado?" 
			categoria="Optimización" 
			autor="JM" 
			nivel="2" 
			mail="jose.man_ARROBA_iespana.es">
		<parrafo>
			<linea>Hay que generar un plan de ejecución de la consulta a través del comando EXPLAIN PLAN. Una vez generado, se debe comprobar la columna POSITION de PLAN_TABLE para aquellas filas que ID=0. Si el valor de POSITION es NULL, se está usando el optimizador basado en reglas, en el  resto de los casos se utiliza el basado en costes.</linea>
		</parrafo>			
		<parrafo>
			<linea>El siguiente SELECT nos puede servir para ver el tipo de optimizador utilizado en los distintos planes:</linea>
		</parrafo>			
		<parrafo tipo="PRE">
			<linea>    SELECT DECODE( NVL(position,-1),-1, 'Reglas','Costes')</linea>
			<linea>    FROM   plan_table</linea>
			<linea>    WHERE  id = 0;</linea>
		</parrafo>
	</faq>

	<faq codigo="3"
			titulo="¿Como puedo averiguar el plan de ejecución que utiliza una consulta?" 
			categoria="Optimización" 
			autor="JM" 
			nivel="3" 
			mail="jose.man_ARROBA_iespana.es">
		<parrafo>
			<linea>Esto se puede hacer con distintos métodos:</linea>
		</parrafo>
		<parrafo>
			<linea>1.- Activando la traza de SQL a nivel de sesión.</linea>
			<linea>2.- Utilizando la instrucción EXPLAIN PLAN.</linea>
			<linea>3.- Utilizando el comando SET AUTOTRACE de SQL*Plus.</linea>
		</parrafo>
		<parrafo>
			<linea></linea>
			<linea>1.- Activar la traza de SQL a nivel de sessión:</linea>
		</parrafo>
		<parrafo>
       <linea> Consultar el FAQ sobre trazas a nivel de sesión.</linea>
		</parrafo>
		<parrafo>
			<linea></linea>
			<linea>2.-Utilizar la instruccion EXPLAIN PLAN:</linea>
		</parrafo>
		<parrafo>
       <linea>La instrucción EXPLAIN PLAN nos permite llenar la tabla PLAN_TABLE con datos sobre el plan de ejecución. La tabla PLAN_TABLE debe crearse, y para ello podemos ir al fichero UTLXPLAN.SQL del directorio $ORACLE_HOME/rdbms/admin. Una vez creada la tabla, ejecutaremos la instrucción:</linea>
		</parrafo>
		<parrafo tipo="PRE">
			<linea>    EXPLAIN PLAN</linea>
			<linea>    [SET STATEMENT ID = 'identificador']</linea>
			<linea>    INTO esquema.PLAN_TABLE</linea>
			<linea>    FOR &lt;instruccion SQL&gt;;</linea>
		</parrafo>
		<parrafo>
			<linea>Con esto llenaremos la tabla PLAN_TABLE con un número de registros indeterminado. Cada registro, tendrá en la columna STATEMENT_ID el valor 'identificador'. Para ver el plan de ejecución, podremos mostrar las distintas filas formateadas con la siguiente consulta:</linea>
		</parrafo>
		<parrafo tipo="PRE">
			<linea>    SELECT LPAD(’ ’, 2 * (level-1)) || operation operation,</linea>
			<linea>           options,</linea>
			<linea>           object_name,</linea>
			<linea>    FROM   output</linea>
			<linea>    START  WITH id = 0 AND </linea>
			<linea>           statement_id = 'identificador'</linea>
			<linea>    CONNECT BY PRIOR id = parent_id AND</linea>
			<linea>           statement_id = 'identificador';</linea>
		</parrafo>
		<parrafo>
			<linea></linea>
			<linea>3.- Utilizar el comando AUTOTRACE (a partir de Oracle 7.3)</linea>
		</parrafo>
		<parrafo>
			<linea>El programa SQL*Plus nos permite mostrar el plan de ejecución de todas las instrucciones que vamos lanzando. Para ello basta con activar la auto-traza a través del comando:</linea>
		</parrafo>
		<parrafo tipo="PRE">
			<linea>    SET AUTOTRACE [ON,OFF] [EXPLAIN] [STATISTICS]</linea>
		</parrafo>
		<parrafo>
			<linea>Si indicamos la opción EXPLAIN, nos mostrará el plan de ejecución, y si indicamos la opción STATISTICS, nos presentará una tabla de estardísticas tales como número de ordenaciones, lecturas a disco, bloques recuperados, etc. Para poder ejecutar este comando, es necesario tener el permisos sobre V$SESSION. La mejor manera de conseguir esto es concediendo el rol completo PLUSTRACE, y estar creada la tabla PLAN_TABLE dentro del esquema actual (ver el punto 2 para consultar cómo crear esta tabla).</linea>
		</parrafo>
	</faq>
	
	<faq codigo="4"
			titulo="¿Cómo puedo obtener una traza de todas las sentencias ejecutadas durante una sesión?" 
			categoria="Optimización" 
			autor="JM" 
			nivel="4" 
			mail="jose.man_ARROBA_iespana.es">
		<parrafo>
			<linea>Oracle nos permite obtener una traza de todas las instrucciones que se lancen en una sesión determinada. Puede ser útil para ver el plan de ejecución de cada una de ellas, para averiguar las instrucciones SQL que son lanzadas contra una base de datos (cuando no disponemos de los fuentes de un programa concreto), o incluso para detectar qué instrucción ralentiza un proceso complejo.</linea>
		</parrafo>
		<parrafo>
			<linea>Los pasos a dar son:</linea>
		</parrafo>
		<parrafo>
			<linea>a) Obtener el 'sid' y 'serial#' del proceso sobre el que queremos hacer la traza. Para ello debemos hacer la siguiente consulta sobre V$SESSION:</linea>
		</parrafo>
		<parrafo tipo="PRE">
			<linea>    SELECT username, program, sid, serial#</linea>
			<linea>    FROM   v$session</linea>
			<linea>    ORDER BY 1, 2;</linea>
		</parrafo>			
		<parrafo>
			<linea>Dentro de la tabla resultado, buscaremos nuestro proceso, por ejemplo:</linea>
		</parrafo>			
		<parrafo tipo="PRE">
			<linea>USERNAME        PROGRAM                    SID   SERIAL#</linea>
			<linea>--------------- -------------------- --------- ---------</linea>
			<linea>SCOTT           DELPHI32.EXE                 9        30</linea>
			<linea> </linea>
		</parrafo>			
		<parrafo>
			<linea>b) Ejecutar el siguiente bloque de código PL/SQL:</linea>
		</parrafo>			
		<parrafo tipo="PRE">
			<linea>BEGIN</linea>
			<linea>    SYS.DBMS_SYSTEM.set_sql_trace_in_session(9, 30, TRUE);</linea>
			<linea>END;</linea>
		</parrafo>			
		<parrafo>
			<linea>Esto nos activa la traza para una sesión identificada por sid y serial#</linea>
		</parrafo>			
		<parrafo>
			<linea>c) Ejecutar todas las instrucciones sobre las que queremos hacer la traza. Esto puede ser una instrucción concreta, un proceso completo, o incluso una jornada entera de trabajo con un programa.</linea>
		</parrafo>			
		<parrafo>
			<linea>d) Ejecutar de nuevo el bloque de código para desactivar la traza:</linea>
		</parrafo>			
		<parrafo tipo="PRE">
			<linea>BEGIN</linea>
			<linea>    SYS.DBMS_SYSTEM.set_sql_trace_in_session(9, 30, FALSE);</linea>
			<linea>END;</linea>
		</parrafo>			
		<parrafo>
			<linea>e) En el directorio configurado para la salida aparecerá nuestro archivo de traza ORAxxx.trc. Este directorio se configura desde la opción USER_DUMP_DEST del archivo INIT.ORA. Por defecto tiene el valor ORACLE_HOME/rdbms/trace.</linea>
		</parrafo>			
		<parrafo>
			<linea>f) Formatear el archivo de traza a través de la utilidad TKPROF, situada en el directorio BIN de la instalación de Oracle. Una llamada básica podría ser la siguiente:</linea>
		</parrafo>			
		<parrafo tipo="PRE">
			<linea>    TKPROF &lt;fichero_traza&gt; &lt;fichero_salida&gt; [explain=usuario/password] [sys=no]</linea>
		</parrafo>			
		<parrafo>
			<linea>Con el parámetro "explain=usuario/password" indicamos que nos muestre el plan de ejecución de todas las instrucciones, conectándose para ello al usuario/password indicados.</linea>
			<linea>Con el parámetro "sys=no" indicamos que no nos muestre las instrucciones realizadas por el usuario SYS.</linea>
		</parrafo>			
		<parrafo>
			<linea>Además de las instrucciones y su plan de ejecución, el archivo de traza nos proporciona información sobre las sentencias erroneas, los tiempos de ejecución, el optimizador utilizado, etc.</linea>
		</parrafo>			
	</faq>

	<faq codigo="5"
			titulo="¿Porqué una conversión en el WHERE desactiva mis índices?" 
			categoria="Optimización" 
			autor="JM" 
			nivel="2" 
			mail="jose.man_ARROBA_iespana.es">
		<parrafo>
			<linea>Cuando se hace una consulta y la condición de filtro incluya una función de conversión (TO_NUMBER, TO_DATE, etc.), esta se debe aplicar siempre que se pueda sobre un valor constante en vez de sobre una columna.</linea>
			<linea>Por ejemplo, la siguiente consulta no utilizará ningún indice sobre &lt;COLUMNA_VARCHAR&gt;</linea>
		</parrafo>
		<parrafo tipo="PRE">
			<linea>    SELECT &lt;COLUMNAS&gt;</linea>
			<linea>    FROM   &lt;TABLA&gt;</linea>
			<linea>    WHERE  TO_NUMBER(&lt;COLUMNA_VARCHAR&gt;) = 2;</linea>
		</parrafo>			
		<parrafo>
			<linea>Sin embargo, la siguiente consulta sí que utiliza los índices sobre la &lt;COLUMNA_VARCHAR&gt;</linea>
		</parrafo>
		<parrafo tipo="PRE">
			<linea>    SELECT &lt;COLUMNAS&gt;</linea>
			<linea>    FROM   &lt;TABLA&gt;</linea>
			<linea>    WHERE  &lt;COLUMNA_VARCHAR&gt; = TO_CHAR(2);</linea>
		</parrafo>			
		<parrafo>
			<linea>Las conversiones implícitas siguen unas normas parecidas, pero teniendo en cuenta que Oracle aplicará la función de conversión sobre el valor alfanumérico:</linea>
			<linea>Por ejemplo, esta consulta:</linea>
		</parrafo>
		<parrafo tipo="PRE">
			<linea>    SELECT &lt;COLUMNAS&gt;</linea>
			<linea>    FROM   &lt;TABLA&gt;</linea>
			<linea>    WHERE  &lt;COLUMNA_VARCHAR&gt; = 2;</linea>
		</parrafo>			
		<parrafo>
			<linea>será interpretada del siguiente modo:</linea>
		</parrafo>
		<parrafo tipo="PRE">
			<linea>    SELECT &lt;COLUMNAS&gt;</linea>
			<linea>    FROM   &lt;TABLA&gt;</linea>
			<linea>    WHERE  TO_NUMBER(&lt;COLUMNA_VARCHAR&gt;) = 2;</linea>
		</parrafo>			
		<parrafo>
			<linea>Por lo tanto, desactivará los índices. Sin embargo, esta otra consulta:</linea>
		</parrafo>
		<parrafo tipo="PRE">
			<linea>    SELECT &lt;COLUMNAS&gt;</linea>
			<linea>    FROM   &lt;TABLA&gt;</linea>
			<linea>    WHERE  &lt;COLUMNA_NUMBER&gt; = '2';</linea>
		</parrafo>			
		<parrafo>
			<linea>será interpretada del siguiente modo:</linea>
		</parrafo>
		<parrafo tipo="PRE">
			<linea>    SELECT &lt;COLUMNAS&gt;</linea>
			<linea>    FROM   &lt;TABLA&gt;</linea>
			<linea>    WHERE  &lt;COLUMNA_VARCHAR&gt; = TO_NUMBER('2');</linea>
		</parrafo>			
		<parrafo>
			<linea>Por lo que no desactivará los índices, ya que la función se aplica sobre el valor constante y no sobre la columna.</linea>
		</parrafo>

	</faq>


</bd_faq>
