Diseño de la aplicación para el rendimiento de la base de datos

No modifique la propiedad text de un objeto SQLStatement tras su ejecución. En su lugar, utilice una instancia de SQLStatement para cada declaración SQL y use los parámetros de declaración para proporcionar diferentes valores.

Antes de que se ejecute una declaración SQL, el motor de ejecución la prepara (compila) para determinar los pasos que se llevan a cabo internamente para realizar la declaración. Cuando llama al método SQLStatement.execute() en una instancia SQLStatement que no se ha ejecutado anteriormente, la declaración se prepara automáticamente antes de que se ejecute. En llamadas posteriores al método execute() , siempre que la propiedad SQLStatement.text no haya cambiado, aún se prepara la declaración. En consecuencia, se ejecuta de forma más rápida.

Para obtener el mayor beneficio de reutilizar las declaraciones, si se deben cambiar los valores entre cada ejecución de declaraciones, utilice los parámetros de declaración para personalizar la declaración. (Los parámetros de declaración se definen usando la propiedad de conjunto asociativa SQLStatement.parameters . A diferencia de cambiar la propiedad text de la instancia SQLStatement, si cambia los valores de los parámetros de declaración el motor de ejecución no necesita preparar la declaración nuevamente.

Cuando vuelve a utilizar una instancia SQLStatement, la aplicación necesita almacenar una referencia a la instancia SQLStatement una vez que se ha preparado. Para mantener una referencia a la instancia, declare la variable como una variable del ámbito de la clase en lugar de una variable en el ámbito de la función. Una buena manera para que SQLStatement sea una variable de ámbito de clase consiste en estructurar la aplicación para que la declaración SQL se agrupe en una sola clase. Un grupo de declaraciones que se ejecutan en combinación también se pueden agrupan en una sola clase. (Esta técnica se conoce como el uso del patrón de diseño Command.) Al definir las instancias como variables de miembros de la clase, éstas permanecen siempre que la instancia de la clase agrupada existe en la aplicación. Como mínimo, simplemente puede definir una variable que contiene la instancia SQLStatement fuera de una función para que la instancia permanezca en la memoria. Por ejemplo, declare la instancia SQLStatement como una variable miembro en una clase ActionScript o como una variable sin función en un archivo JavaScript. A continuación puede definir los valores de la declaración y llamar al método execute() cuando quiere realmente ejecutar la consulta.

Utilice los índices de base de datos para mejorar la velocidad de ejecución para la organización y comparación de información.

Cuando se crea un índice para una columna, la base de datos almacena una copia de los datos de esa columna. Dicha copia se ordena en orden numérico o alfabético. Esto permite que la base de datos relacione valores rápidamente (p. el. cuando se utiliza el operador de igualdad) y ordene los datos de resultados utilizando la cláusula ORDER BY .

Los í­ndices de la base de datos se mantienen actualizados continuamente, por lo que las operaciones de cambio de datos (INSERT o UPDATE) de esa tabla son algo más lentas. No obstante, el aumento de velocidad de recuperación de datos suele ser notable. Debido a este inconveniente menor de rendimiento, no se deben indexar simplemente todas las columnas de cada tabla. Como alternativa, utilice una estrategia para definir los índices. Haga uso de las siguientes indicaciones para planificar la estrategia de indización:

  • Indexe las columnas que se utilicen en tablas de unión, en cláusulas WHERE o cláusulas ORDER BY.

  • Si las columnas se utilizan con frecuencia al mismo tiempo, indéxelas de forma conjunta en un solo índice.

  • Para una columna que contenga datos de texto que se recuperen de forma ordenada alfabéticamente, especifique la intercalación COLLATE NOCASE para el índice.

Considere la compilación previa de declaraciones SQL durante los tiempos de inactividad de la aplicación..

La primer vez que ejecute una declaración SQL, el proceso es más lento, ya que el texto SQL está preparado (compilado) por el motor de la base de datos. Dado que la preparación y la ejecución de una declaración puede ser una operación exigente, una estrategia es cargar previamente los datos iniciales y luego ejecutar las demás declaraciones en segundo plano:

  1. Cargue primero los datos que necesita la aplicación.

  2. Cuando se completan las operaciones de inicio de la aplicación, o en otro momento de “inactividad” de la aplicación, ejecute otras declaraciones.

Por ejemplo, supongamos que la aplicación no accede a la base de datos para mostrar su ventana inicial. En este caso, espere hasta que la ventana se muestre antes de abrir la conexión de base de datos. Finalmente, cree instancias de SQLStatement y ejecute todas las que pueda.

Como alternativa, supongamos que cuando inicia la aplicación inmediatamente muestra algunos datos como el resultado de una determinada consulta. En ese caso, proceda a ejecutar la instancia SQLStatement para esa consulta. Después de que se cargan y se muestran los datos iniciales, cree las instancias SQLStatement para las otras operaciones de la base de datos y, si es posible, ejecute las otras declaraciones que se necesitan más adelante.

En la práctica, si está reutilizando instancias de SQLStatement, el tiempo adicional necesario para preparar la declaración supone solamente un esfuerzo único. Probablemente no tenga un gran impacto en el rendimiento general.

Agrupe varias operaciones de cambio de datos SQL en una transacción.

Supongamos que está ejecutando un gran número de declaraciones SQL que implican añadir o cambiar datos (declaraciones INSERT o UPDATE ). Puede aumentar el rendimiento considerablemente ejecutando todas las declaraciones en una transacción explícita. Si comienza una transacción explícitamente, cada una de las declaraciones se ejecuta en su propia transacción creada automáticamente. Después de que cada transacción (cada declaración) termina de ejecutarse, el motor de ejecución escribe los datos resultantes en el archivo de la base de datos en el disco.

Por otra parte, considere qué sucede si crea explícitamente una transacción y ejecuta las declaraciones en el contexto de dicha transacción. El motor de ejecución hace todos los cambios en la memoria, luego escribe todos los cambios en el archivo de la base de datos de una vez cuando se confirma la transacción. Generalmente, escribir los datos en el disco es la parte que lleva más tiempo en la operación. En consecuencia, si se escribe en el disco una sola vez en lugar de una vez por cada declaración SQL puede mejorar significativamente el rendimiento.

Procese grandes resultados de consultas SELECT en partes, utilizando el método execute() de la clase SQLStatement (con el parámetro prefetch ) y el método next() .

Supongamos que se ejecuta una declaración SQL que recupera un gran conjunto de resultados. Posteriormente la aplicación procesa cada fila de datos en un bucle. Por ejemplo, aplica formato a los datos o crea objetos a partir de los mismos. El procesamiento de los datos puede tardar bastante tiempo, lo que puede suponer problemas de representación como, por ejemplo, pantalla bloqueada o sin respuesta. Tal y como se describe en Operaciones asíncronas , una solución consiste en dividir el trabajo en segmentos. La API de la base de datos SQL facilita la división del procesamiento de datos.

El método execute() de la clase SQLStatement cuenta con un parámetro prefetch opcional (el primer parámetro). Si se proporciona un valor, éste especifica el número máximo de filas de resultados que devuelve la base de datos una vez finalizada la ejecución:

dbStatement.addEventListener(SQLEvent.RESULT, resultHandler); 
dbStatement.execute(100); // 100 rows maximum returned in the first set

Una vez devuelto el primer conjunto de datos de resultado, se puede llamar al método next() para continuar ejecutando la sentencia y recuperar otro conjunto de filas de resultados. Al igual que sucede con el método execute() , el método next() acepta un parámetro prefetch para especificar un número máximo de filas para devolver:

// This method is called when the execute() or next() method completes 
function resultHandler(event:SQLEvent):void 
{ 
    var result:SQLResult = dbStatement.getResult(); 
    if (result != null) 
    { 
        var numRows:int = result.data.length; 
        for (var i:int = 0; i < numRows; i++) 
        { 
            // Process the result data 
        } 
         
        if (!result.complete) 
        { 
            dbStatement.next(100); 
        } 
    } 
}

Se puede continuar llamando al método next() hasta que se carguen todos los datos. Tal y como se muestra en el anterior listado, es posible determinar el momento en que se han cargado todos los datos. Compruebe la propiedad complete del objeto SQLResult que se crea cada vez que finaliza el método execute() o next() .

Nota: utilice el parámetro prefetch y el método next() para dividir el procesamiento de los datos de resultado. No utilice este parámetro y método para limitar los resultados de una consulta a una parte de su conjunto de resultados. Si sólo desea recuperar un subconjunto de filas en un conjunto de resultados de una sentencia, utilice la cláusula LIMIT de la sentencia SELECT . Si el conjunto de resultados es amplio, aún puede utilizar el parámetro prefetch y el método next() para dividir el procesamiento de los resultados.
Considere el uso de varios objetos SQLConnection asíncronos con una sola base de datos para ejecutar varias sentencias simultáneamente.

Si un objeto SQLConnection está conectado a una base de datos utilizando el método openAsync() , se ejecuta en segundo plano en lugar de en el subproceso de ejecución del motor de ejecución principal. Asimismo, cada conexión SQL se ejecuta en su propio subproceso en segundo plano. Con el uso de varios objetos SQLConnection, se pueden ejecutar eficazmente varias declaraciones SQL de forma simultánea.

Este enfoque también implica posibles desventajas. Y lo que es más importante, cada nuevo objeto SQLStatement requiere memoria adicional. Asimismo, las ejecuciones simultáneas suponen más trabajo para el procesador, especialmente en equipos que sólo cuentan con una CPU. Por estos aspectos, este enfoque no se recomienda para su uso en dispositivos móviles.

Un aspecto adicional para tener en cuenta es que puede perderse el potencial beneficio de la reutilización de los objetos SQLStatement, ya que un objeto SQLStatement está vinculado a un solo objeto SQLConnection. Por lo tanto, el objeto SQLStatement no puede reutilizarse si su objeto SQLConnection asociado ya está en uso.

Si opta por utilizar varios objetos SQLConnection conectados a una sola base de datos, se debe tener en cuenta que cada uno de ellos ejecuta sus sentencias en su propia transacción. Asegúrese de tener en cuenta estas transacciones independientes en cualquier código que modifique datos como, por ejemplo, adición, modificación o eliminación de datos.

Paul Robertson ha creado una biblioteca de código fuente abierto que ayuda a incorporar las ventajas del uso de varios objetos SQLConnection mientras se reducen los posibles inconvenientes. La biblioteca utiliza un grupo de objetos SQLConnection y administra los objetos SQLStatement asociados. De este modo, garantiza la reutilización de los objetos SQLStatement y varios objetos SQLConnection están disponibles para ejecutar varias sentencias simultáneamente. Para obtener más información y descargar la biblioteca, visite http://probertson.com/projects/air-sqlite/ .