Entendiendo los índices de expresiones

2 comentarios

Los índices sirven para dos cosas:

  1. Mostrar el resultado de los SELECTs ordenados por el criterio que nos interesa
  2. Realizar búsquedas o filtros, para que solamente sean afectadas las filas que cumplan con la condición que establecimos

Lo más común es que los índices estén compuestos por una o más columnas en forma directa. Veamos un ejemplo:

indices01

Captura 1. Si haces clic en la imagen la verás más grande

indices02

Captura 2. Si haces clic en la imagen la verás más grande

Tenemos una tabla ALUMNOS y para ordenar a los alumnos por APELLIDOS y por NOMBRES podríamos crear un índice como el siguiente:

Listado 1.

   CREATE INDEX IDX_ALUMNOS ON ALUMNOS(ALU_APELLD, ALU_NOMBRE);

Y está muy bien, funcionará perfectamente.

Podríamos escribir entonces un SELECT como el siguiente:

Listado 2.

SELECT
   *
FROM
   ALUMNOS
ORDER BY
   ALU_APELLD,
   ALU_NOMBRE

Y así obtendríamos un resultado como este:

indices03

Captura 3. Si haces clic en la imagen la verás más grande

Donde como puedes observar, los resultados aparecen ordenados por ALU_APELLD. Pero si queremos saber la diferencia entre ALU_TOTANO y ALU_TOTCOB no es posible usar un índice normal.

¿Y entonces?

Entonces la solución es crear un índice de expresión.

¿Qué es un índice de expresión?

Un índice en el cual se utiliza una expresión aritmética o una expresión alfanumérica o funciones internas.

Ejemplos de índices de expresión:

Listado 3.

   CREATE INDEX IDX_ALUMNOS2 ON ALUMNOS COMPUTED BY (LEFT(ALU_APELLD, 1) || LEFT(ALU_NOMBRE, 1));

   CREATE INDEX IDX_ALUMNOS3 ON ALUMNOS COMPUTED BY (ALU_TOTANO - ALU_TOTCOB);

Como puedes ver, la diferencia entre el índice creado en el Listado 1. y los índices creados en el Listado 3., es que en estos últimos se escribieron las palabras COMPUTED BY y también se usó la función LEFT() en IDX_ALUMNOS2 y una operación aritmética de resta en IDX_ALUMNOS3.

En todos los índices de expresión se deben escribir las palabras COMPUTED BY, tal como vimos en el Listado 3.

Usando índices de expresión

Algo muy importante a recordar es que cuando usamos índices de expresión debemos usarlos exactamente igual a como los definimos.

Listado 4.

SELECT
   *
FROM
   ALUMNOS
WHERE
   LEFT(ALU_APELLD, 1) || LEFT(ALU_NOMBRE, 1) = 'KM'

En este caso el Firebird usará el índice IDX_ALUMNOS2 porque la expresión escrita en la cláusula WHERE es la misma expresión usada en la definición del índice IDX_ALUMNOS2.

Listado 5.

SELECT
   *
FROM
   ALUMNOS
WHERE
   LEFT(ALU_NOMBRE, 1) || LEFT(ALU_APELLD, 1) = 'MK'

En el SELECT del Listado 5. el Firebird no usará el índice IDX_ALUMNOS2 porque la condición escrita en la cláusula WHERE no es igual a la expresión usada en la definición del índice IDX_ALUMNOS2.

Listado 6.

SELECT
   *
FROM
   ALUMNOS
WHERE
   ALU_TOTANO - ALU_TOTCOB > 1000

En el SELECT del Listado 6. el Firebird usará el índice IDX_ALUMNOS3 porque la condición escrita en la cláusula WHERE es la misma expresión escrita en la definición del índice IDX_ALUMNOS3.

Listado 7.

SELECT
   *
FROM
   ALUMNOS
ORDER BY
   ALU_TOTANO - ALU_TOTCOB

En el SELECT del Listado 7. también se usará el índice IDX_ALUMNOS3 porque la expresión escrita en la cláusula ORDER BY es la misma expresión que se usó en la definición del índice IDX_ALUMNOS3.

Conclusión:

Los índices de expresión pueden ser muy útiles en algunos casos, es bueno saber que contamos con esta herramienta para poder usarla cuando nos haga falta.

Artículos relacionados:

Optimizando un SELECT que compara columnas de la misma tabla (2)

El índice del blog Firebird21

El foro del blog Firebird21

 

Indices ascendentes y descendentes

2 comentarios

Los índices en Firebird pueden ser ascendentes (1, 2, 3, 4, 5, 6, …) o descendentes (999, 998, 997, 996, …)

Lo importante a recordar es que son siempre recorridos en el mismo orden en que fueron creados. O sea que un índice ascendente siempre se recorre de menor a mayor y un índice descendente siempre se recorre de mayor a menor.

¿Por qué es importante saber eso?

Porque tener el índice adecuado puede hacer que nuestras consultas sean rapidísimas.

Veamos un ejemplo:

Listado 1.

SELECT
   MAX (MiColumna)
FROM
   MiTabla

Aquí tenemos tres posibilidades:

  1. No hay un índice sobre la columna MiColumna
  2. Hay un índice ascendente sobre la columna MiColumna
  3. Hay un índice descendente sobre la columna MiColumna

En el caso 1. y en el caso 2. para hallar el valor máximo de MiColumna el Firebird debe recorrer la tabla completa, desde la primera fila hasta la última fila, porque no sabe cual de esas filas tendrá al mayor valor. Un índice ascendente jamás se usaría en este caso.

En cambio, en el caso 3. el valor máximo estará sí o sí en la primera fila del índice.

La diferencia en tiempo puede ser muy grande. En tablas que tienen millones de filas la diferencia será muy notoria porque el caso 1. y el caso 2. deben recorrer todas esas millones de filas y en cambio el caso 3. siempre encontrará el valor buscado en la primera fila.

¿Y si en lugar de escribir la función MAX() escribimos la función MIN()?

Pues allí se usará un índice ascendente, si es que existe.

Recuerda que crear un índice tiene sus ventajas y sus desventajas:

  • La ventaja es que si se lo utiliza en un SELECT entonces los resultados se obtendrán muy rápido
  • La desventaja es que debe ser actualizado cada vez que se realiza un INSERT, un UPDATE o un DELETE en esa tabla

Hay por lo tanto que comprobar si vale la pena crear un índice.

Artículos relacionados:

El índice del blog Firebird21

El foro del blog Firebird21

 

Indexando una columna computada

Deja un comentario

Como sabes, las columnas computadas son muy útiles para ver su contenido en los SELECTs y también cuando las usamos en la cláusula WHERE (de un UPDATE, de un DELETE, o de un SELECT) o en la cláusula ORDER BY.

Ya las habíamos visto en varios artículos anteriormente, ahora veremos una de sus características más interesantes: pueden ser indexadas.

Las primeras versiones del Firebird no lo permitían, pero a partir de la versión 2.0 ya tenemos esa posibilidad.

Por ejemplo, la tabla de SUCURSALES tiene estos datos:

INDICE1

Captura 1. Si haces clic en la imagen la verás más grande

Como podemos ver, algunos nombres están en mayúsculas y algunos nombres están en minúsculas. Podríamos crear el siguiente índice:


CREATE INDEX IDX_SUCURSALES1 ON SUCURSALES COMPUTED BY (UPPER(SUC_NOMBRE));

COMMIT;

Y luego usarlo en comandos como los siguientes:


SELECT * FROM SUCURSALES ORDER BY UPPER(SUC_NOMBRE)

INDICE2

Captura 2. Si haces clic en la imagen la verás más grande

Aquí, los nombres de las sucursales aparecen ordenados alfabéticamente y al mirar el PLAN vemos que efectivamente se usó el índice recientemente creado.


SELECT * FROM SUCURSALES WHERE UPPER(SUC_NOMBRE) STARTING WITH 'BOG'

INDICE3

Captura 3. Si haces clic en la imagen la verás más grande

Aquí le pedimos que nos muestre los nombres de todas las sucursales que empiezan con las letras ‘BOG’ y vemos que también usó el índice computado que habíamos creado anteriormente.


DELETE FROM SUCURSALES WHERE UPPER(SUC_NOMBRE) = 'MIAMI - ESTADOS UNIDOS'

INDICE4

También en este caso se usó el índice de la columna computada, como podemos comprobarlo mirando el PLAN.

Otros ejemplos:


CREATE INDEX IDX_MOVIMCAB1 ON MOVIMCAB COMPUTED BY (EXTRACT(YEAR FROM MVC_FECHAX))

CREATE INDEX IDX_MOVIMCAB2 ON MOVIMCAB COMPUTED BY (EXTRACT(YEAR FROM MVC_FECHAX) || MVC_NRODOC)

CREATE DESCENDING INDEX IDX_MOVIMCAB3 ON MOVIMCAB COMPUTED BY (EXTRACT(YEAR FROM MVC_FECHAX) || MVC_NRODOC)

El índice IDX_MOVIMCAB1 de la tabla MOVIMCAB (cabecera de movimientos) la indexa según el año del movimiento.

El índice IDX_MOVIMCAB2 de la tabla MOVIMCAB (cabecera de movimientos) la indexa según el año del movimiento y el número del documento.

El índice IDX_MOVIMCAB3 de la tabla MOVIMCAB (cabecera de movimientos) es muy parecido al índice IDX_MOVIMCAB2 pero ahora el índice es descendente (es decir, de mayor a menor).

Conclusión:

Tener columnas computadas es algo muy bueno porque nos facilita la vida. Poder indexar a esas columnas computadas es algo más bueno aún.

Como hemos visto en los ejemplos anteriores, el Firebird usará a los índices de las columnas computadas cada vez que los necesita.

Esos índices pueden involucrar a una sola columna o a varias columnas (como en los dos últimos ejemplos donde IDX_MOVIMCAB2 y IDX_MOVIMCAB3 involucran a las columnas MVC_FECHAX y MVC_NRODOC).

Artículos relacionados:

Columnas computadas

Un truco para encontrar valores que pueden estar en varias columnas

Usando un SELECT en una columna computada

Utilizando columnas computadas

Algunos ejemplos de uso de las columnas computadas

Usando un PLAN

Algo más sobre PLAN

Entendiendo el contenido de un PLAN

El índice del blog Firebird21

El foro del blog Firebird21

Entendiendo el contenido de un PLAN

6 comentarios

En Firebird todos los SELECTs que ejecutemos (manualmente o dentro de una vista) sí o sí usarán un PLAN. Ese PLAN puede ser elegido por el Firebird o por nosotros mismos. En general el Firebird hace un excelente trabajo en hallar el PLAN más adecuado pero en ocasiones no es así y allí nosotros podemos especificar uno mejor.

NOTA: Estos conocimientos no son necesarios para quienes son principiantes en Firebird, tales personas lo mejor que pueden hacer es dejarle al Firebird que especifique el PLAN a ser usado. Pero para quienes tienen un nivel más avanzado sí es importante, porque en algunos casos podrán mejorar drásticamente la velocidad de sus SELECTs.

Para entender mejor lo que es un PLAN puedes leer estos artículos:

Usando un PLAN

Algo más sobre PLAN

 En un PLAN lo que se especifica son los índices que serán usados. Las posibilidades son:

  • No hay un índice y no se usa un índice. Verás la palabra NATURAL.
  • No hay un índice y se necesita usar un índice. Verás la palabra SORT.
  • Hay un índice y no se usa ese índice. Verás la palabra NATURAL.
  • Hay un índice y se usa ese índice. Verás la palabra INDEX.

Si el SELECT tiene un JOIN (o más de uno, claro) entonces las posibilidades son:

  • No se necesita ordenar los conjuntos de resultados usados. Verás la palabra JOIN
  • Se necesita ordenar los conjuntos de resultados usados. Verás la palabra MERGE o más comúnmente las palabras SORT MERGE

Si se quiere mostrar las filas ordenadas:

  • Si hay un índice verás la palabra ORDER
  • Si no hay un índice verás la palabra SORT

El significado de NATURAL

Si ves esta palabra en el PLAN significa que no existe un índice o que sí existe pero no se lo utiliza. Si no se lo utiliza en general es por la pobre selectividad del mismo. Para saber más sobre la selectividad puedes leer estos artículos:

Selectividad de los índices

Recreando índices y calculando estadísticas

NATURAL significa que las filas son extraídas con el mismo orden en el cual fueron guardadas. Esto implica que todas las páginas deben ser leídas antes de que pueda ejecutarse el filtro (la condición que hayas puesto en la cláusula WHERE). Eso puede ser muy lento en tablas grandes.

Sin embargo, NATURAL no siempre es malo, si no te interesa mostrar a las filas con algún orden en particular entonces NATURAL es lo correcto porque es más rápido que usar un índice. Si se usa un índice primero se busca la clave en el índice y luego se extrae la fila correspondiente, si se usa NATURAL se ahorra el primer paso y por lo tanto es más rápido; la diferencia es muy notoria en tablas muy grandes.

El significado de SORT

La palabra SORT significa “ordenar” en inglés y eso es lo que hace el Firebird. Ordena el conjunto de resultados creando archivos temporales. Este proceso puede ser muy lento en tablas grandes porque crear esos archivos temporales puede demorar mucho tiempo. Si encuentras la palabra SORT eso es muy malo para el SELECT. Pero cuidado con esto, que sea malo para el SELECT no implica que necesariamente será malo para la Base de Datos. ¿Por qué? porque para evitar el SORT tendríamos que crear un índice, pero el mantenimiento de los índices es costoso, ya que cada vez que se hace un INSERT, un UPDATE, o un DELETE el índice debe ser modificado, eso toma tiempo y en tablas que constantemente están cambiando le provocarán un sobrecosto sustancial. Por lo tanto, la regla es crear un índice solamente si se lo usará frecuentemente. Por ejemplo, si tu SELECT se ejecutará una sola vez al año (o muy pocas veces en el año) y las filas involucradas se cuentan por millones entonces probablemente lo mejor sea no tener un índice y que el Firebird haga el SORT cuando lo necesite. Es cierto que el SELECT será lento, pero los INSERT, UPDATE, y DELETE serán más rápidos y como las filas se cuentan por millones entonces valdrá la pena no crear un índice.

El significado de INDEX

Esta palabra significa que existe un índice y que se lo utiliza.

El significado de JOIN

La palabra JOIN significa “juntar” en inglés. Si la ves en un PLAN significa que no se requiere ordenar ambos conjuntos de resultados porque ya se encuentran en el orden correcto.

El significado de MERGE o SORT MERGE

La palabra MERGE significa “combinar” o “mezclar” en inglés. MERGE y SORT MERGE hacen exactamente lo mismo, pero se suele escribir SORT MERGE para que quede más claro lo que hacen.

Lo que hacen es lo siguiente: un SORT al primer conjunto de resultados (como vimos más arriba), un SORT al segundo conjunto de resultados (como vimos más arriba) y luego combinar ambos conjuntos de resultados, ya ordenados.

Fíjate que hay dos SORT y una combinación de resultados. Cuidado con eso, algo así puede ser lentísimo en tablas grandes.

Un SORT MERGE es lo peor que puedes tener en un PLAN.

El significado de ORDER

La palabra ORDER significa “ordenar” en inglés, aunque en este caso la podríamos traducir “ordenar por el índice” y a continuación el nombre del índice que usa.

Analizando el PLAN

Para que todo esto quede más claro a continuación veremos varios ejemplos de SELECT con sus respectivos planes de ejecución; y explicaremos cada uno de ellos.

Ejemplo 1:

SELECT
   *
FROM
   BANCOS

PLAN (BANCOS NATURAL)

¿Qué significa este PLAN? que como no le dijimos en cual orden queremos ver a los Bancos entonces los mostró en el mismo orden conque los guardó en la tabla. Eso está muy bien, es lo correcto, porque es lo más rápido.

Ejemplo 2:

SELECT
   *
FROM
   BANCOS
ORDER BY
   BAN_CODSUC,
   BAN_IDENTI

PLAN (BANCOS ORDER PK_BANCOS)

¿Qué significa este PLAN? que como le dijimos que queremos ver a las filas ordenadas por Código de la Sucursal y por Identificador y tenemos un índice con esas columnas entonces usó a ese índice, cuyo nombre es PK_BANCOS.

Esto es más lento que el Ejemplo 1., pero si es lo que necesitamos mostrar entonces está ok.

Ejemplo 3:

SELECT
   *
FROM
   BANCOS
ORDER BY
   BAN_NOMBRE

PLAN SORT ((BANCOS NATURAL))

¿Qué significa este PLAN? que la tabla de BANCOS no estaba ordenada por la columna BAN_NOMBRE (por eso la palabra NATURAL) y que el Firebird tuvo que ordenarla (por eso la palabra SORT) antes de mostrar las filas. Ese ordenamiento crea archivos temporarios (en la memoria caché o en el disco duro) y puede ser muy lento en tablas muy grandes.

Ejemplo 4:

SELECT
   *
FROM
   BANCOS
WHERE
   BAN_CODSUC = 0 AND
   BAN_IDENTI > 10

PLAN (BANCOS INDEX (PK_BANCOS))

¿Qué significa este PLAN? que la tabla BANCOS tiene un índice según las columnas BAN_CODSUC y BAN_IDENTI (que son las usadas para filtrar con la cláusula WHERE) y que el Firebird usó ese índice.

En los WHERE hay siempre que tratar de usar índices porque eso aumenta muchísimo la velocidad.

Ejemplo 5:

SELECT
   *
FROM
   BANCOS
WHERE
   BAN_NOMBRE LIKE 'BAN%'

PLAN (BANCOS NATURAL)

¿Qué significa este PLAN? que la tabla BANCOS no tiene un índice según la columna BAN_NOMBRE y que como no le pedimos que muestre las filas con algún orden en particular las mostró como fueron guardadas.

Ejemplo 6:

SELECT
   *
FROM
   BANCOS
WHERE
   BAN_NOMBRE LIKE 'BAN%'
ORDER BY
   BAN_NOMBRE

PLAN SORT ((BANCOS NATURAL))

¿Qué significa este PLAN? que la tabla BANCOS no tiene un orden según la columna BAN_NOMBRE pero como se necesita mostrar a las filas ordenadas según esa columna entonces se hizo un SORT (es decir, se creó un archivo temporal para poder mostrar a las filas ordenadas).

No te olvides que hacer un SORT es más lento que usar un índice, a veces muchísimo más lento, porque las filas deben ser ordenadas y guardadas en un archivo temporal antes de ser mostradas y eso toma su tiempo.

Ejemplo 7:

SELECT
   B.BAN_CODSUC,
   S.SUC_NOMBRE,
   B.BAN_NOMBRE
FROM
   BANCOS     B
JOIN
   SUCURSALES S
      ON B.BAN_CODSUC = S.SUC_CODIGO

PLAN JOIN (B NATURAL, S INDEX (UQ_SUCURSALES))

¿Qué significa este PLAN? que la tabla S tiene un índice llamado UQ_SUCURSALES según su columna SUC_CODIGO, la tabla BANCOS no tiene un índice sobre su columna BAN_CODSUC y que se hará el JOIN sin ordenar las filas de la tabla B, así como están serán mostradas.

Ejemplo 8:

SELECT
   B.BAN_CODSUC,
   S.SUC_NOMBRE,
   B.BAN_NOMBRE
FROM
   BANCOS     B
JOIN
   SUCURSALES S
      ON B.BAN_CODSUC = S.SUC_CODIGO
ORDER BY
   B.BAN_NOMBRE

PLAN SORT (JOIN (B NATURAL, S INDEX (UQ_SUCURSALES)))

¿Qué significa este PLAN? que la tabla S tiene un índice llamado UQ_SUCURSALES según su columna SUC_CODIGO, la tabla BANCOS no tiene un índice sobre su columna BAN_CODSUC, que se hará el JOIN de esas filas (como vimos en el Ejemplo 7.) y posteriormente se hará un SORT para mostrar a las filas ordenadas según la columna BAN_NOMBRE.

Conclusión:

Entender el contenido del PLAN es muy útil por dos cosas: a) porque sabremos lo que está haciendo el Firebird para mostrar las filas del SELECT, y b) porque podremos analizarlo y quizás descubrir una mejor alternativa. Claro que esto último lleva su buen tiempo, no es algo que conseguiremos hacer enseguida pero vale la pena el esfuerzo porque un PLAN adecuado muestra a las filas muy rápidamente.

Si en el PLAN vemos la palabra SORT eso es algo malo y si vemos las palabras SORT MERGE eso es mucho más malo. Pero son malos para el SELECT, quizás no lo sean para la Base de Datos. Eso porque para evitar los SORT hay que crear índices, pero los índices deben ser mantenidos cada vez que se realiza un INSERT, un UPDATE o un DELETE, y ese mantenimiento no es instantáneo, toma su tiempo, que en tablas muy grandes y con muchas operaciones de inserción, actualización y borrado puede ser muy grande.

Por lo tanto debemos estudiar bien el caso y decidir si creamos el índice o si dejamos que se haga el SORT. En general, si el SELECT será ejecutado muchas veces, frecuentemente, deberíamos crear el índice. En cambio, si se lo ejecutará raramente lo mejor puede ser no crear el índice.

Otro aspecto muy importante a tener en cuenta es que las tablas son dinámicas, constantemente están cambiando, entonces un PLAN que hoy puede ser perfecto podría no serlo dentro de algunos meses o años. Si vamos a escribir el PLAN manualmente debemos tener bien presente que eso implicará más trabajo para nosotros porque de vez en cuando tendremos que verificar que siga funcionando muy bien, y si no es así entonces buscar y escribir un mejor PLAN. Pero encontrar un mejor PLAN no es algo que haremos en dos segundos, puede hacernos perder muchos minutos o horas inclusive.

Artículos relacionados:

Usando un PLAN

Algo más sobre PLAN

Selectividad de los índices

Recreando índices y calculando estadísticas

El índice del blog Firebird21

El foro del blog Firebird21

Entendiendo los índices compuestos

Deja un comentario

Como sabes, cuando creas un índice éste puede tener una columna o más de una columna. Si el índice tiene una sola columna se lo llama “simple” y si tiene más de una columna se lo llama “compuesto”. ¿Y en qué casos el Firebird utiliza un índice compuesto?

Supongamos que tu índice está compuesto por tres columnas. Firebird podría no usar ese índice, o usar solamente la primera columna, o usar la primera columna y la segunda columna, o usar las tres columnas. Es decir que estas son las posibilidades:

  1. No se usa el índice compuesto
  2. Se usa solamente la primera columna
  3. Se usan la primera columna y la segunda columna
  4. Se usan la primera columna y la segunda columna y la tercera columna

¿De qué depende?

De lo que hayas escrito en la cláusula WHERE. Para que una columna de un índice compuesto se utilice las columnas anteriores deben ser comparadas por igualdad. No por menor, ni por mayor, ni por menor o igual, ni por mayor o igual, ni por distinto. Por igualdad.

 Ejemplo:

Supongamos que tenemos un índice compuesto por tres columnas, (MiColumna1, MiColumna2, MiColumna3). Si escribimos:

WHERE
   MiColumna1 >= 21 AND
   MiColumna2 >= 500 AND
   MiColumna3 <= 2000

se usará solamente la columna MiColumna1 del índice compuesto ¿Por qué? porque en MiColumna1 no se usó una igualdad. En cambio, si escribimos:

WHERE
   MiColumna1 = 21 AND
   MiColumna2 >= 500 AND
   MiColumna3 <= 2000

se usarán las columnas MiColumna1 y MiColumna2 del índice compuesto ¿por qué? porque MiColumna1 fue comparada por igualdad, entonces se usa esa columna y también la siguiente. En cambio, si escribimos:

WHERE
   MiColumna1 = 21 AND
   MiColumna2 = 500 AND
   MiColumna3 <= 2000

se usarán las columnas MiColumna1 y MiColumna2 y MiColumna3. ¿Por qué? porque MiColumna1 y MiColumna2 fueron comparadas por igualdad, entonces se usan esas dos columnas y también la siguiente.

¿Y si se requiere que en todos los casos se usen las tres columnas del índice?

En ese caso la solución es no crear un índice compuesto sino crear tres índices simples, uno por cada columna. De esta manera te asegurarás de que siempre las tres columnas utilicen un índice.

Conclusión:

Es muy importante entender en que circunstancias el Firebird usa los índices compuestos para no crearlos innecesariamente. No te olvides que cada índice ocupa espacio en el disco duro y además hace que todas las operaciones de inserción, actualización, y borrado se realicen más lentamente. Mejoran la velocidad de las consultas pero empeoran las demás operaciones.

Como todo, los índices compuestos tienen sus ventajas y sus desventajas. La ventaja es que es más fácil mantener un solo índice compuesto que varios índices independientes. La desventaja es que podrías tener un índice compuesto que usas muy poco porque en tus consultas usas muy pocas igualdades. Si ése es el caso entonces muy probablemente te convendrá tener varios índices simples y no un índice compuesto.

¿Tienes dudas sobre si te conviene o no crear un índice compuesto?

Haz pruebas. Créalo y verifica el rendimiento. Luego elimina el índice compuesto y crea índices simples y verifica el rendimiento. Compara esos rendimientos y así sabrás si te conviene o no tener un índice compuesto.

Artículo relacionado:

El índice del blog Firebird21

 

Evitando que se use un índice en una consulta

Deja un comentario

El optimizador de consultas de Firebird es muy bueno y en casi la totalidad de los casos elegirá la mejor alternativa: cual de los índices usar o no usar un índice.

Sin embargo no es perfecto y alguna vez podrías descubrir que está usando un índice cuando no debería hacerlo. Esto en general ocurre cuando el índice tiene poca selectivdad (es decir: muchos valores repetidos).

Puedes saber cual índice se usa en una consulta mirando el PLAN de la misma.

Si descubres que usando un índice obtienes los resultados a una cierta velocidad (por ejemplo: 1500 ms) y sin usarlo obtienes los resultados a una velocidad mayor (por ejemplo: 100 ms) lo que debes hacer es pedirle al Firebird que no use ese índice. Eso lo consigues agregándole a tu SELECT la cláusula PLAN.

Ejemplo:

Queremos obtener los nombres de los proveedores que empiezan con “H”. La tabla de PROVEEDORES tiene un índice según las columnas (PRO_CODSUC y PRO_NOMBRE).

En PRO_CODSUC se guarda el código de la Sucursal.

En PRO_NOMBRE se guarda el nombre del Proveedor.

SELECT
   PRO_NOMBRE
FROM
   PROVEEDORES
WHERE
   PRO_CODSUC = 0 AND
   PRO_NOMBRE LIKE 'H%'
ORDER BY
   PRO_CODSUC,
   PRO_NOMBRE

Verificamos el tiempo que tardó el SELECT en extraer los datos de la tabla de PROVEEDORES y encontramos que es de 1500 ms y que está usando el índice, así que ahora le pedimos que no use el índice:

SELECT
   PRO_NOMBRE
FROM
   PROVEEDORES
WHERE
   PRO_CODSUC = 0 AND
   PRO_NOMBRE LIKE 'H%'
PLAN
   (PROVEEDORES NATURAL)
ORDER BY
   PRO_CODSUC,
   PRO_NOMBRE

y encontramos que el tiempo disminuyó a solamente 100 ms.

Conclusión:

Aunque casi siempre el Firebird hace un muy buen trabajo eligiendo el índice más adecuado para una consulta no debes asumir que siempre será así, a veces puede equivocarse. Para descubrirlo siempre tienes que observar el tiempo de extracción de los datos y si es mayor que 500 ms entonces es casi seguro que el PLAN puede mejorarse. Un buen profesional siempre verifica los tiempos de extracción de los datos y los optimiza al máximo. Esto es de suma importancia cuando las consultas son ejecutadas frecuentemente y las tablas involucradas tienen muchas filas.

Artículos relacionados:

Usando un PLAN

Algo más sobre PLAN

El índice del blog Firebird21

Recreando todos los índices de todas las tablas

3 comentarios

Como hemos visto en estos artículos:

Recreando los índices de las tablas

Usando índices en Firebird

es muy importante que todos los índices de todas nuestras tablas estén bien balanceados para que cuando realicemos las operaciones de INSERT, UPDATE, DELETE no se pierda más tiempo del debido en mantenerlos actualizados. Un índice desbalanceado tarda más en actualizarse que uno correctamente balanceado; por ese motivo debemos tratar de tener siempre a todos los índices de todas las tablas bien balanceados.

Los siguientes comandos tienen por objetivo recrear un índice, o reindexarlo como también se dice:

ALTER INDEX MiIndice INACTIVE

ALTER INDEX MiIndice ACTIVE

y funcionan muy bien pero … ¿y si queremos reindexar no solamente un índice sino todos los índices de todas las tablas? Evidentemente escribir esos comandos para cada índice de cada tabla tomará mucho tiempo, será muy tedioso y además correremos el riesgo de olvidarnos de alguno. Por ello, escribí el siguiente stored procedure que se encarga de dicha tarea:

SET TERM ^ ;

CREATE PROCEDURE SP_ACTUALIZAR_INDICES
AS
   DECLARE VARIABLE lcNombreIndice VARCHAR(31);
BEGIN

   FOR SELECT
      RDB$INDEX_NAME
   FROM
      RDB$INDICES
   WHERE
      LEFT(RDB$INDEX_NAME, 4) <> 'RDB$'
   INTO
      :lcNombreIndice
   DO BEGIN
      IF (NOT EXISTS(SELECT RDB$INDEX_NAME FROM RDB$RELATION_CONSTRAINTS WHERE RDB$INDEX_NAME = :lcNombreIndice)) THEN BEGIN
         EXECUTE STATEMENT 'ALTER INDEX ' || :lcNombreIndice || ' INACTIVE ;' ;
         EXECUTE STATEMENT 'ALTER INDEX ' || :lcNombreIndice || ' ACTIVE ;' ;
      END
   END

END^

SET TERM ; ^

¿Qué hace este stored procedure?

  1. Obtiene los nombres de todos los índices de todas las tablas cuyas primeras 4 letras sean distintas que “RDB$”. ¿Por qué eso? porque los nombres de las tablas y de los índices de los metadatos siempre empiezan con “RDB$” y los nombres de nuestras tablas y de nuestros índices no deberían empezar con esas letras. En otras palabras, lo que se obtiene son los nombres de nuestros índices, no los nombres de los índices que usa internamente el Firebird
  2. Los índices de las restricciones (Primary Key, Foreign Key, Unique Key) no pueden ser desactivados, el Firebird no lo permite. Y es muy lógico, si se desactivara el índice de una restricción entonces el Firebird no podría verificar esa restricción, por lo tanto hace la fácil: no te permite desactivar el índice de una restricción. Entonces en el stored procedure se verifica que el índice no pertenezca a una restricción. Eso se consigue buscando su nombre en la tabla RDB$RELATION_CONSTRAINTS pues en esa tabla se guardan los datos de todas las restricciones.
  3. Si el nombre del índice no empieza con RDB$ (o sea, si es un índice nuestro) y no es el índice asociado a una restricción entonces se lo inactiva y se lo activa. Este ciclo desactivar/activar tiene por efecto recrear al índice y nos asegura que el nuevo índice esté correctamente balanceado.

Otra versión del stored procedure:

Aquí hay otra versión del stored procedure de arriba, en esta es seleccionable para que puedas ver los nombres de los índices que se están recreando.

SET TERM ^ ;

CREATE PROCEDURE SP_ACTUALIZAR_INDICES
RETURNS(
   tcNombreIndice VARCHAR(31))
AS
BEGIN

   FOR SELECT
      RDB$INDEX_NAME
   FROM
      RDB$INDICES
   WHERE
      LEFT(RDB$INDEX_NAME, 4) <> 'RDB$'
   INTO
      :tcNombreIndice
   DO BEGIN
      IF (NOT EXISTS(SELECT RDB$INDEX_NAME FROM RDB$RELATION_CONSTRAINTS WHERE RDB$INDEX_NAME = :tcNombreIndice)) THEN BEGIN
         EXECUTE STATEMENT 'ALTER INDEX ' || :tcNombreIndice || ' INACTIVE ;' ;
         EXECUTE STATEMENT 'ALTER INDEX ' || :tcNombreIndice || ' ACTIVE ;' ;
         SUSPEND ;
      END
   END

END^

SET TERM ; ^

Y llamarías a este stored procedure así:

SELECT * FROM SP_ACTUALIZAR_INDICES

El problema con los índices de las restricciones

¿Y qué pasa con los índices de las restricciones? ¿No pueden quedar desabalanceados? Por supuesto que sí pueden estar desabalanceados, son índices comunes, no son mágicos. Pero como nosotros no podemos recrearlos tenemos una sola alternativa: realizar un ciclo backup/restore porque al restaurar un backup se crean nuevamente todos los índices, los de las restricciones incluidos.

Artículos relacionados:

Recreando los índices de las tablas

Usando índices en Firebird

El índice del blog Firebird21

Usando índices en Firebird

9 comentarios

Como seguramente ya sabes, los índices sirven para dos cosas:

  1. Buscar datos
  2. Mostrar las consultas ordenadas

Para poder realizar efectivamente ambas tareas los índices deben encontrarse en buen estado y es tu responsabilidad asegurarte de que eso sea así. El mantenimiento de los índices de Firebird debe contemplar dos puntos:

  1. Que el índice esté bien balanceado
  2. Que las estadísticas de los índices sean las correctas

Balanceando los índices:

Si un índice no está bien balanceado entonces las operaciones INSERT, UPDATE, DELETE, FECTCH y SELECT de esa tabla tomarán más tiempo del debido, haciendo lentas operaciones que podrían ejecutarse más rápidamente. Para balancear un índice debes escribir:

ALTER INDEX MiIndice INACTIVE

ALTER INDEX MiIndice ACTIVE

Este ciclo de inactivar/activar un índice tiene como efecto volver a crearlo. Es el equivalente al comando REINDEX de los lenguajes xBase (dBase, Clipper, FoxPro, etc.). Es conveniente ejecutar esos comandos después de haber realizado muchas operaciones en la tabla (por ejemplo: 20.000 ó más) para tener la seguridad de que el índice se encuentra bien balanceado.

En este artículo encontrarás más información:

https://firebird21.wordpress.com/2013/03/03/recreando-los-indices-de-las-tablas/

Recalculando las estadísticas:

El optimizador de consultas del Firebird revisa la estadística de un índice para decidir si lo utilizará en el PLAN de una consulta o no. Si la estadística de un índice no está actualizada entonces podría dejar de utilizar un índice que sí debería haber utilizado y el resultado será que tendrás una consulta mucho más lenta de lo que debería haber sido si el optimizador usaba el índice.

Es por ese motivo que siempre debemos asegurarnos de tener las estadísticas actualizadas, para que la decisión del optimizador sea la mejor posible. Como el Firebird calcula la estadística solamente cuando crea el índice y después de un ciclo backup/restore, es tu responsabilidad recalcularla periódicamente porque todas las operaciones de INSERT, UPDATE, DELETE que realices en esa tabla afectarán a esa selectividad.

Para recalcular la estadística de un índice debes escribir:

SET STATISTICS INDEX MiIndice

Puedes encontrar más información en este artículo:

https://firebird21.wordpress.com/2013/03/09/selectividad-de-los-indices/

¿Cuáles columnas indexar?

Puede ser tentador pensar que si se indexan todas las columnas de una tabla se conseguirá mucha velocidad en todas las consultas a esa tabla. Eso generalmente es falso. Indexar una columna que tiene pocos valores distintos es un error porque lo más probable es que el optimizador de consultas no utilice ese índice, y todas las operaciones de inserción, actualización, borrado que involucren a esa columna modificarán al índice y eso lleva tiempo entonces ¿para qué indexar esa columna si su índice nunca será usado y actualizarlo hará las operaciones más lentas?

La decisión de indexar o no una columna debe estar basada en si se obtiene una ganancia de velocidad considerable utilizándolo. Si no se obtiene una ganancia de velocidad o si la ganancia de velocidad obtenida es muy pequeña, lo correcto es no indexar esa columna para no gastar recursos en ella.

No tiene sentido indexar una columna si no se realizan búsquedas utilizando esa columna ni se ordenan las consultas según esa columna ni se obtiene una buena ganancia de tiempo.

Tener los índices correctos en cada tabla, y solamente los índices correctos, es una decisión de diseño que debe estar bien estudiada para evitar indexar una columna que no debería estar indexada o dejar de indexar una columna que sí debería estarlo.

Para complicar las cosas, un índice que ahora es bueno dentro de unos meses podría dejar de serlo. Y viceversa. Porque al aumentar grandemente la cantidad de datos en la tabla la selectividad varía y por lo tanto la bondad de indexar o no esa columna. Es por lo tanto necesario que al menos en las tablas más grandes de nuestra Base de Datos periódicamente verifiquemos los índices, su selectividad, y si son o no usados en los PLANES de los SELECTS. Un índice que nunca es utilizado en una búsqueda o en el ordenamiento de una consulta no tiene razón de ser, está de más, está sobrando, es inútil, solamente gasta recursos.

Tampoco es correcto indexar una columna y que su índice se use poquísimo, quizás una o dos veces al mes en una consulta para ganar 2 ó 3 segundos.

Conclusión:

Tener siempre los índices bien balanceados y con sus estadísticas actualizadas es lo que debes preocuparte en conseguir para que todas las operaciones en tus tablas (INSERT, UPDATE, DELETE, FETCH, SELECT) sean lo más rápidas posibles. Un índice en mal estado de conservación perjudica más de lo que ayuda. Por ello es conveniente que tengas un stored procedure que se encargue de esas tareas, el cual podrá ser llamado desde tu aplicación (el programa que hiciste en Visual FoxPro, Delphi, C, C++, etc.)

En muchos lenguajes de programación es el propio programador quien decide si utilizará un índice o no y en caso afirmativo cual índice utilizará, pero en el caso de Firebird es el optimizador de consultas quien toma esa decisión (si no se le ha dicho lo contrario) y basa esa decisión en la selectividad de los índices. ¿Quiéres que tome la decisión más correcta? entonces la selectividad de tus índices debe estar actualizada.

Artículos relacionados:

Recreando los índices de las tablas

Selectividad de los índices

Usando un PLAN

Usando índices correctos para aumentar la velocidad de las consultas

Optimizando las consultas

El índice del blog Firebird21

Columnas computadas

7 comentarios

Firebird además de las columnas normales nos permite tener columnas computadas.

¿Qué es una columna computada?

Una columna cuyo contenido depende de otras columnas, previamente definidas.

¿Para qué se usan las columnas computadas?

Para poder referenciar a los datos de varias formas, o sea que los datos pueden encontrarse en sus columnas originales o en las columnas computadas. También para escribir fórmulas que estén siempre accesibles.

¿Cómo se declara una columna computada?

Con la cláusula COMPUTED BY

Ejemplo:

Tenemos una tabla llamada ALUMNOS que tiene estas columnas (y varias más, pero que ahora no nos interesan):

CREATE TABLE ALUMNOS (
   ALU_IDENTI D_IDENTIFICADOR NOT NULL,
   ALU_NOMBRE D_NOMBRE25      NOT NULL,
   ALU_APELLD D_NOMBRE25      NOT NULL) ;

El dominio D_IDENTIFICADOR es un INTEGER y el dominio D_NOMBRE25 es un VARCHAR(25).

En la columna ALU_IDENTI se guarda el identificador del alumno, en ALU_NOMBRE sus nombres y en ALU_APELLD sus apellidos.

Pero en los informes queremos que aparezcan primero los apellidos, luego una coma, luego un espacio en blanco, y luego los nombres. Claro que podríamos conseguirlo realizando las concatenaciones correspondientes en cada caso pero lo más sencillo es tener una columna computada, como la siguiente:

ALU_APENOM COMPUTED BY (TRIM(ALU_APELLD) || ', ' || ALU_NOMBRE)

Entonces, la estructura de la tabla ALUMNOS quedaría así:

CREATE TABLE ALUMNOS (
   ALU_IDENTI D_IDENTIFICADOR NOT NULL,
   ALU_NOMBRE D_NOMBRE25      NOT NULL,
   ALU_APELLD D_NOMBRE25      NOT NULL,
   ALU_APENOM COMPUTED BY (TRIM(ALU_APELLD) || ', ' || ALU_NOMBRE)) ;

Si usas EMS SQL Manager, le dices que quieres agregar una columna a la tabla, como siempre lo haces, pero ahora eliges la opción “COMPUTED BY expression” y escribes la expresión que deseas:

COMPUTED3

Captura 1. Si haces clic en la imagen la verás más grande

 Si ahora escribimos un SELECT para ver los datos de los alumnos obtendremos algo similar a esto:

COMPUTED1Captura 2. Si haces clic en la imagen la verás más grande

Fíjate en la columna ALU_APENOM, tiene los mismos datos que las columnas ALU_NOMBRE y ALU_APELLD, pero concatenados de la forma que determinamos anteriormente.

¿Cómo se cambia el valor de una columna computada?

La única forma es cambiando los valores de sus columnas base (en este ejemplo, de ALU_NOMBRE y de ALU_APELLD), si intentas algo como esto:

UPDATE
   ALUMNOS
SET
   ALU_APENOM = 'PRUEBA DE CAMBIO DE NOMBRE'
WHERE
   ALU_IDENTI = 1200

El Firebird se enojará contigo y te mostrará el mensaje:

This column cannot be updated because it is derived from an SQL function or expression.Attempted update of read-only column.

O sea que la columna no puede ser actualizada porque es derivada de una función o expresión SQL. Intentaste actualizar una columna que es read-only (sólo lectura).

¿Dónde se usa una columna computada?

  • En el comando SELECT para ver sus valores, para poner una condición de filtro (cláusula WHERE) o para mostrar a los datos ordenados (cláusula ORDER BY).
  • En el comando UPDATE y en el comando DELETE, cuando se coloca una condición de filtro (cláusula WHERE)

No se puede insertar un valor, ni actualizar un valor, ya que las columnas computadas obtienen sus valores de columnas previamente definidas.

SELECT
   ALU_APENOM
FROM
   ALUMNOS

Sí se puede (desde Firebird 2.0) indexar a una columna computada, pero hay que hacerlo de esta forma:

CREATE INDEX IDX_ALUMNOS1 ON ALUMNOS COMPUTED BY (TRIM(ALU_APELLD) || ', ' || ALU_NOMBRE);

Ya que si se intenta así:

CREATE INDEX IDX_ALUMNOS1 ON ALUMNOS ALU_APENOM

Se obtendrá un mensaje de error similar al siguiente:

Unsuccessful metadata update.
Attempt to index COMPUTED BY column in INDEX IDX_ALUMNOS1.

SQL Code: -607
IB Error Number: 335544351

El mensaje dice que la actualización de los metadatos no tuvo éxito, porque se intentó indexar una columna computada.


SELECT
   ALU_APENOM
FROM
   ALUMNOS
ORDER BY
   ALU_APENOM

COMPUTED3

Captura 3. Si haces clic en la imagen la verás más grande

¿Se puede cambiar una columna computada?

Se puede cambiar el tipo de datos y la expresión. No se puede cambiar una columna base en una columna computada o viceversa.

¿Las columnas computadas se usan solamente para concatenar columnas alfanuméricas?

No, también pueden usarse con columnas numéricas. Supongamos que tenemos una tabla de PRODUCTOS y deseamos saber cual es nuestro porcentaje de ganancia sobre el precio de costo, entonces podríamos escribir algo como esto:

PRD_PORCEN COMPUTED BY (PRD_PREVTA * 100 / PRD_PRECTO)

Donde PRD_PREVTA es el precio de venta del producto y PRD_PRECTO es el precio de costo del producto. En la columna PRD_PORCEN siempre tendremos los porcentajes de ganancia.

Desde luego que podríamos escribir cualquier otra fórmula matemática, no solamente esa.

Conclusión:

Las columnas computadas son muy útiles para que escribamos menos y podamos visualizar los datos en varias formas distintas, además las podemos usar con las cláusulas WHERE y ORDER BY.

No se puede indexar a una columna computada, pero sí se puede indexar las columnas que la componen.

Artículos relacionados:

Utilizando columnas computadas

Algunos ejemplos de uso de las columnas computadas

Usando un SELECT en una columna computada

Indexando una columna computada

El índice del blog Firebird21

El foro del blog Firebird21

Optimizando las consultas

2 comentarios

Este artículo está basado en el documento “Planos de Optimizaçao do Firebird” de Gladiston Santana.

Seguramente ya has leído o escuchado que el Firebird es rapidísimo para devolver el resultado de las consultas. ¿A qué se debe esa gran velocidad?

Quienes están acostumbrados a usar el modelo desktop (tablas .DBF, Paradox, Access) saben que el rendimiento está directamente ligado al rendimiento del Servidor de archivos, rendimiento de la red, tamaño de las tablas y el uso de índices correctos. Todo eso también es cierto en Firebird pero éste dispone de algo más, muy poderoso: el PLAN de optimización (“PLAN optimizer” en inglés).

¿Qué es el PLAN de optimización?

Es la lista del índice (o ningún índice) de cada tabla involucrada en una consulta que el Firebird utilizará para devolver el resultado de esa consulta (o sea, de ese SELECT)

La intención al usar un PLAN es que los resultados sean devueltos lo más rápidamente posible. Todas las consultas, absolutamente todas, tienen un PLAN de optimización, el cual puede ser puesto:

  • Automáticamente, por el Firebird
  • Manualmente, por el programador

¿Cómo el Firebird determina el PLAN?

Cuando el programador no le dice cual PLAN usar, el Firebird crea su propio PLAN usando para ello un módulo llamado “Query optimizer”, en castellano: optimizador de la consulta.

La tarea de este optimizador es analizar la consulta, evaluando: índices, combinaciones de índices, agrupamientos como sort, union, (inner, left, outer) join, y muchas cosas más y después de haber analizado todo eso evaluar el costo de esa consulta.

Este “costo” es una nota, una calificación, que indica si la optimización de la consulta es ventajosa o desventajosa. Si el optimizador encuentra que la nota es desventajosa entonces puede realizar una optimización distinta en la consulta o decidir no usar optimización. A este último caso se le llama NATURAL PLAN (o sea, un PLAN que no usa índices).

¿En qué casos se usaría el NATURAL PLAN?

Podría parecer extraña la idea de no utilizar optimización pero en algunos casos la mejor alternativa es no usar optimización ya que si se debe re-evaluar una operación o seleccionar un índice, eso a veces toma más tiempo que recorrer la tabla entera. Y en este caso no vale la pena usar el optimizador.

Este es el caso de tablas pequeñas o de SELECTs que no precisan de índices para realizar una búsqueda. También puede ocurrir que una tabla tenga una columna indexada pero que el optimizador elija no utilizar ese índice.

Ejemplo 1:

Tenemos una tabla llamada PERSONAS, la cual tiene una columna llamada PER_APELLD (apellidos de las personas) y un índice llamado IDX_PERSONAS sobre esa columna:

CREATE INDEX IDX_PERSONAS ON PERSONAS(PER_APELLD);

Ahora escribimos esta consulta, porque deseamos obtener los datos de todas las personas cuyo apellido sea ‘TORRES’ o cuyo apellido sea ‘CABRAL’:

SELECT
   *
FROM
   PERSONAS
WHERE
   PER_APELLD LIKE '%TORRES%CABRAL%'

El Firebird usará el índice IDX_PERSONAS, ¿verdad?. No, falso. No usará el índice IDX_PERSONAS como podemos ver al revisar el PLAN utilizado.

PLAN1

(haciendo clic en la imagen la verás más grande)

¿Por qué no usó el índice? Porque hay un “%” en el inicio del LIKE y eso implica que debe recorrer la tabla completa para mostrar el resultado de la consulta. Y no tiene sentido usar un índice en ese caso, ya que tomará más tiempo y no se obtendrá algún beneficio.

Inclusive, llamar al optimizador ya sería un desperdicio porque ningún índice ayudaría a acelerar esta consulta, entonces ni siquiera llamará al optimizador.

Para este caso de consultas que no pueden ser optimizadas es que existe el PLAN NATURAL.

Ejemplo 2:

Usando la misma tabla del ejemplo 1 escribimos esta consulta:

SELECT
   *
FROM
   PERSONAS
WHERE
   PER_APELLD LIKE 'TORRES%CABRAL%'

Fíjate que se borró el “%” que estaba al principio del LIKE. Volvemos a revisar cual es el PLAN utilizado y nos muestra:

PLAN2

(haciendo clic en la imagen la verás más grande)

 Y ahora sí está usando el índice porque es adecuado usarlo.

El SELECT extendido

Los SELECTs que escribimos en los dos ejemplos anteriores no son los que realmente utilizará el Firebird cuando los ejecutemos. ¿Por qué no? porque el Firebird automáticamente le agrega la cláusula PLAN a todos los SELECTs que no tengan dicha cláusula escrita.

Así, para el primer ejemplo, el SELECT que usará el Firebird es:

SELECT
   *
FROM
   PERSONAS
WHERE
   PER_APELLD LIKE '%TORRES%CABRAL%'
PLAN
   (PERSONAS NATURAL)

Fíjate que le agrega las palabras PLAN y luego el PLAN utilizado (en este caso PERSONAS NATURAL). PERSONAS es el nombre de la tabla y NATURAL le indica que no debe usar un índice.

Para el segundo ejemplo el SELECT que usará el Firebird es:

SELECT
   *
FROM
   PERSONAS
WHERE
   PER_APELLD LIKE 'TORRES%CABRAL%'
PLAN
   (PERSONAS INDEX (IDX_PERSONAS))

Fíjate que ahora sí usa el índice IDX_PERSONAS

Preparando un PLAN

El proceso de someter una consulta al optimizador para que éste la analice y determine el mejor PLAN se llama “Prepare query” (preparar la consulta). Los programadores experimentados siempre escriben un PLAN en el SELECT porque eso ahorra tiempo y mejora el rendimiento ya que el Firebird usa el PLAN especificado y no debe llamar al optimizador para determinar cual PLAN utilizará.

En EMS SQL Manager, haríamos clic en la opción “Prepare query” para preparar la consulta  y en “Explain query” para ver cual será el PLAN que utilizará el Firebird, como se ve a continuación:

PLAN3

(haciendo clic en la imagen la verás más grande)

 Entonces, como ya se le indica cual PLAN debe usar el Firebird no pierde tiempo analizando la consulta y determinando ese PLAN.

Advertencia

Si al escribir un SELECT especificamos el PLAN a utilizar y ese PLAN es el más adecuado entonces el SELECT se ejecutará más rápido, sin embargo hay que tener en cuenta algo muy importante: a veces, el PLAN que hoy es excelente puede dejar de serlo conforme las condiciones de la Base de Datos van cambiando, por ejemplo si se le van agregando nuevos índices, fórmulas, e inclusive el mismo aumento en la cantidad de datos. Esto implica que periódicamente debemos verificar el rendimiento de nuestros SELECTs para detectar aquellos planes que ya no son los más adecuados.

Un PLAN inadecuado no es solamente malo para esa consulta en particular, es malo para toda la Base de Datos porque consultas concurrentes podrían sumarse al problema y tornar al Servidor en una verdadera tortuga de tres patas.

Tiempos de ejecución

Veamos ahora cuanto tarda ejecutar cada uno de los SELECTs de los ejemplos anteriores:

PLAN4

(haciendo clic en la imagen la verás más grande)

 La tabla PERSONAS tiene 3.516.272 registros y ejecutar el primer SELECT (el que tiene PLAN NATURAL y por lo tanto no usa índices) tardó 14,093 segundos.

PLAN5

(haciendo clic en la imagen la verás más grande)

 Al ejecutar el segundo SELECT (que usa al índice IDX_PERSONAS) el tiempo se redujo considerablemente, ahora es de solamente 1,282 segundos. ¿Y qué ocurrirá si quitamos el apellido CABRAL de la consulta y dejamos solamente al apellido TORRES? ¿El tiempo mejorará o empeorará? veamos:

PLAN6

(haciendo clic en la imagen la verás más grande)

 Esto puede parecer muy extraño, ya que ahora tenemos muchos más datos (ya que hay más apellidos ‘TORRES’ que ‘TORRES CABRAL’) sin embargo el tiempo de la consulta disminuyó.

¿Por qué?

Porque el Firebird recupera los datos por páginas y en una página generalmente caben muchos registros. Esos registros ya están en la memoria y por lo tanto mostrarlos es muy rápido.

Veamos ahora lo que sucede al volver a ejecutar el último SELECT

PLAN7

(haciendo clic en la imagen la verás más grande)

 El tiempo se redujo aún más, ahora ni siquiera tardó 1 segundo en finalizar.

¿Por qué?

Porque esa consulta ya estaba en la memoria, en el caché del Firebird, y por lo tanto no tuvo que ser leída desde el disco duro, con lo cual se aceleró aún más obtener los resultados pedidos.

Lo bueno de esto es que si el mismo usuario ejecuta el mismo SELECT o si algún otro usuario lo hace, como los datos ya están en la memoria mostrarlos es súper rápido.

Estos tiempos de ejecución fueron obtenidos con una tabla que tiene 3.516.272 registros y una computadora que ya tiene varios años: Pentium IV con 4 Gbytes de RAM y de un solo núcleo. En cualquier computadora nueva los tiempos serán mucho menores.

En Firebird si multiplicamos por 10 la cantidad de registros de una tabla no aumenta por 10 el tiempo de consulta. Si la tabla PERSONAS tuviera 35.162.720 registros el tiempo de ejecución de la consulta aún sería de alrededor de 1 segundo.

En cambio, en las tablas desktop (.DBF, Paradox, Access) si multiplicamos por 10 la cantidad de registros el tiempo de consulta se multiplica por 10, por 12, por 15.

Conclusión:

Entender lo que es el PLAN y aprender a usar el PLAN correcto hará que tus consultas sean rapidísimas. Sin embargo debes recordar que a veces el PLAN va decayendo en su rendimiento cuando le vas agregando índices o fórmulas a las tablas o mismo por el aumento de los datos así que es buena práctica revisar los planes periódicamente para asegurar que siguen siendo los más adecuados.

La gran diferencia en el rendimiento que se observa cuando se usa Firebird en comparación con tablas .DBF, Paradox y Access es que Firebird, al igual que Oracle, Sybase, MSSQL, usa un PLAN. Pero mientras que por usar esos SGBD debes pagar mucho dinero, por usar Firebird no pagas, es gratis.

Artículos relacionados:

Usando un PLAN

Algo más sobre PLAN

El índice del blog Firebird21

Older Entries