Ejecutar consultas preparadas: DatabaseRead/Bind

Las consultas preparadas se ejecutan mediante las funciones DatabaseRead y DatabaseReadBind. La primera función extrae los resultados de la base de datos de forma que posteriormente se puedan leer campos individuales de cada registro recibido a su vez como respuesta, y la segunda extrae cada registro coincidente en su totalidad, en forma de estructura.

bool DatabaseRead(int request)

En la primera llamada, después de Database Prepare o DatabaseReset, la función DatabaseRead ejecuta la consulta y establece el puntero interno de resultado de la consulta en el primer registro recuperado (si la consulta espera que se devuelvan registros). Las funciones DatabaseColumn permiten leer los valores de los campos del registro, es decir, las columnas especificadas en la consulta.

En las siguientes llamadas, la función DatabaseRead salta al siguiente registro de los resultados de la consulta hasta llegar al final.

La función devuelve true una vez completada con éxito. El valor false se utiliza como indicador de un error (por ejemplo, la base de datos puede estar bloqueada u ocupada), así como cuando se alcanza normalmente el final de los resultados, por lo que debe analizar el código en _LastError. En concreto, el valor ERR_DATABASE_NO_MORE_DATA (5126) indica que los resultados han finalizado.

¡Atención! Si DatabaseRead se utiliza para ejecutar consultas que no devuelven datos, como INSERT, UPDATE, etc., la función devuelve inmediatamente false y establece el código de error ERR_DATABASE_NO_MORE_DATA si la solicitud se ha realizado correctamente.

El patrón habitual de uso de la función se ilustra con el siguiente pseudocódigo (DatabaseColumn las funciones para los distintos tipos se presentan en la sección siguiente).

int r = DatabasePrepare(db"SELECT... WHERE...?",
   param));                            //compiling the query(optional with parameters)
while(DatabaseRead(r))                 // query execution (on the first iteration)
{                                      //    and loop through result records
   int count;
   DatabaseColumnInteger(r0count); // read one field from the current record
   double number;
   DatabaseColumnDouble(r1number); // read another field from the current record
   ...                                 // column types and numbers in record are determined by program
                                       // process the received values of count, number, etc.
}                                      // loop is interrupted when the end of the results is reached
DatabaseFinalize(r);

Obsérvese que, dado que la consulta (lectura de datos condicionales) sólo se ejecuta una vez (en la primera iteración), no es necesario llamar a DatabaseReset, como hicimos al registrar los datos cambiantes. No obstante, si queremos volver a ejecutar la consulta y «recorrer» los nuevos resultados, sería necesario llamar a DatabaseReset.

bool DatabaseReadBind(int request, void &object)

La función DatabaseReadBind opera de forma similar a DatabaseRead: la primera llamada ejecuta la consulta SQL y, en caso de éxito (hay datos adecuados en el resultado), rellena la estructura object pasada por referencia con los campos del primer registro; las llamadas posteriores continúan moviendo el puntero interno a través de los registros de los resultados de la consulta, rellenando la estructura con los datos del siguiente registro.

La estructura debe tener sólo tipos numéricos y/o cadenas como miembros (no se permiten arrays), no puede heredar de ni contener miembros estáticos de tipos objeto.

El número de campos de la estructura object no debe superar el número de columnas de los resultados de la consulta; de lo contrario, obtendremos un error. El número de columnas puede determinarse dinámicamente mediante la función DatabaseColumnsCount; sin embargo, el llamante suele necesitar «conocer» de antemano la configuración de datos esperada según la solicitud original.

Si el número de campos de la estructura es inferior al número de campos del registro, se realizará una lectura parcial. El resto de los datos pueden obtenerse utilizando las funciones DatabaseColumn correspondientes.

Se supone que los tipos de campo de la estructura coinciden con los tipos de datos de las columnas de resultados. De lo contrario, se realizará una conversión automática implícita, que puede tener consecuencias inesperadas (por ejemplo, una cadena leída en un campo numérico dará 0).

En el caso más sencillo, cuando calculamos un determinado valor total para los registros de la base de datos, por ejemplo, llamando a una función agregada como SUM(column), COUNT(column) o AVERAGE(column), el resultado de la consulta será un único registro con un único campo.

SELECT SUM(swap) FROM trades;

Dado que la lectura de los resultados está relacionada con las funciones de DatabaseColumn, aplazaremos el desarrollo del ejemplo hasta la siguiente sección, donde éstas se presentan.