Vous êtes sur la page 1sur 4

Creando consultas SQL con parmetros

En el artculo anterior vimos como realizar consultas SQL para INSERT, DELETE,
UPDATE y SELECT utilizando el componente IBSQL que forma parte de la paleta
de componentes IBExpress.

Tambin qued muy claro que la velocidad de ejecucin de consultas con este
componente respecto a otros como IBQuery es muy superior. Todo lo que hemos
visto esta bien para hacer consultas espordicas sobre alguna tabla que otra, pero
que ocurre si tenemos que realizar miles de consultas SQL de una sola vez?

UTILIZANDO UNA TRANSACCION POR CONSULTA

Supongamos que tenemos que modificar el nombre de 1000 registros de la tabla


CLIENTES:

var
i: Integer;
dwTiempo: DWord;
begin
with Consulta do
begin
////////////// METODO LENTO ////////////////

dwTiempo := TimeGetTime;

for i := 1 to 1000 do
begin
SQL.Clear;
SQL.Add( 'UPDATE CLIENTES' );
SQL.Add( 'SET NOMBRE = ' + QuotedStr( 'NOMBRE CLIENTE N ' +
IntToStr( i ) ) );
SQL.Add( 'WHERE ID = ' + IntToStr( i ) );

Transaction.StartTransaction;

try
ExecQuery;
Transaction.Commit;
except
on E: Exception do
begin
Application.MessageBox( PChar( E.Message ), 'Error de SQL', MB_ICONSTOP );
Transaccion.Rollback;
end;
end;
end;

ShowMessage( 'Tiempo: ' + IntToStr( TimeGetTime - dwTiempo ) + ' milisegundos' );


end;
end;

Como puede verse arriba, por cada cliente actualizado he generado una SQL distinta
abriendo y cerrando una transaccin para cada registro. He utilizado la funcin
TimeGetTime que se encuentra en la unidad MMSystem para calcular el tiempo
que tarda en actualizarme el nombre de los 1000 clientes. En un PC con Pentium 4 a
3 Ghz, 1 GB de RAM y utilizando el motor de bases de datos Firebird 2.0 me ha
tardado 4167 milisegundos.

Aunque las consultas SQL van muy rpidas con los componentes IBSQL aqu el
fallo que cometemos es que por cada registro actualizado se abre y se cierra una
transaccin. En una base de datos local no se nota mucho pero en una red local con
muchos usuarios trabajando a la vez le puede pegar fuego al concentrador.

Lo ideal sera poder modificar la SQL pero sin tener que cerrar la transaccin. Como
eso no se puede hacer en una consulta que esta abierta entonces hay que utilizar los
parmetros. Los parmetros (Params) nos permiten enviar y recoger informacin
de una consulta SQL que se esta ejecutando sin tener que cerrarla y abrila.

UTILIZANDO PARAMETROS EN LA CONSULTA

Para introducir parmetros en una consulta SQL hay que aadir dos puntos delante
del parmetro. Por ejemplo:

UPDATE CLIENTES
SET NOMBRE = :NOMBRE
WHERE ID = :ID

Esta consulta tiene dos parmetros: ID y NOMBRE. Los nombres de los parmetros
no tienen porque coincidir con el nombre del campo. Bien podran ser as:

UPDATE CLIENTES
SET NOMBRE = :NUEVONOMBRE
WHERE ID = :IDACTUAL

De este modo se pueden modificar las condiciones de la consulta SQL sin tener que
cerrar la transaccin. Despus de crear la consulta SQL hay que llamar al mtodo
Prepare para que prepare la consulta con los futuros parmetros que se le van a
suministrar (no es obligatorio pero si recomendable). Veamos el ejemplo anterior
utilizando parmetros y una sla transaccin para los 1000 registros:
var
i: Integer;
dwTiempo: DWord;
begin
with Consulta do
begin
////////////// METODO RPIDO ////////////////

dwTiempo := TimeGetTime;

Transaction.StartTransaction;

SQL.Clear;
SQL.Add( 'UPDATE CLIENTES' );
SQL.Add( 'SET NOMBRE = :NOMBRE' );
SQL.Add( 'WHERE ID = :ID' );
Prepare;

for i := 1 to 1000 do
begin
Params.ByName( 'NOMBRE' ).AsString := 'NOMBRE CLIENTE N '+ IntToStr( i );
Params.ByName( 'ID' ).AsInteger := i;
ExecQuery;
end;

try
Transaction.Commit;
except
on E: Exception do
begin
Application.MessageBox( PChar( E.Message ), 'Error de SQL', MB_ICONSTOP );
Transaccion.Rollback;
end;
end;

ShowMessage( 'Tiempo: ' + IntToStr( TimeGetTime - dwTiempo ) + ' milisegundos' );


end;
end;

En esta ocasin me ha tardado slo 214 milisegundos, es decir, se ha reducido al 5%


del tiempo anterior sin saturar al motor de bases de datos abriendo y cerrando
transacciones sin parar.

Este mtodo puede aplicarse tambin para consultas con INSERT, SELECT y
DELETE. En lo nico en lo que hay que tener precaucin es en no acumular muchos
datos en la transaccin, ya que podra ser peor el remedio que la enfermedad.

Si teneis que actualizar cientos de miles de registros de una sola vez, recomiendo
realizar un Commit cada 1000 registros para no saturar la memoria cach de la
transaccin. Todo depende del nmero de campos que tengan las tablas as como el
nmero de registros a modificar. Utilizad la funcin TimeGetTime para medir
tiempos y sacar conclusiones.

Y si el proceso a realizar va a tardar ms de 2 o 3 segundos utilizar barras de


progreso e hilos de ejecucin, ya que algunos usuarios neurticos podran creer que
nuestro programa se ha colgado y empezaran ha hacer clic como posesos (os
aseguro que existe gente as, antes de que termine la consulta SQL ya te estn
llamando por telfono echndote los perros).