Mostrando entradas con la etiqueta Base de datos. Mostrar todas las entradas
Mostrando entradas con la etiqueta Base de datos. Mostrar todas las entradas

domingo, 8 de noviembre de 2020

TZQuery.ExecSQL

Actualización 26-12-2020: downgrade de Zeos 7.2.6 (también probé la 7.2.8) a Zeos 7.1.3.a stable del año del jopo.

No hace mucho actualicé tanto el IDE como el compilador, algunos componentes que forman parte de Lazarus se actualizan y otros, como ZeosLib no. Años usando la versión estable 7.1.3 (si no me equivoco) ya que las primeras versiones de la 7.2 me tiraba errores por todos lados. Finalmente decidí ir por la 7.2.6 y al principio iba todo bien, pero un sistema de los primeros que hice, hace ya más de 3 años, y sin las mejores técnicas de programación precisamente, me pidieron una modificación, nada del otro mundo y ahí empezaron los problemas con el querido Zeos. La documentación de los cambios de Zeos no es la mejor del mundo.

En dicho programa, tenía mucho código que actualizaba la base de datos con TZQuery en lugar de TZConnection.ExecuteDirect. Motivo: se pueden utilizar parámetros, me resulta más cómodo. Por ejemplo tenía códigos de este tipo:

ZQTHab.SQL.Text:='DELETE FROM tashaber;';
ZQTHab.Open;
ZQTHab.Close; 

Funcionaba sin problemas.

Ahora arroja un error: "Can not open a Resultset." 

 



Se soluciona con TZQuery.ExecSQL que la verdad no sé si es un método nuevo o si siempre existió.

ZQTHab.SQL.Text:='DELETE FROM tashaber;';
ZQTHab.ExecSQL; 

Lo bueno es que no se necesita ni abrir ni cerrar la consulta, más legible.

También me saltaron errores de base de datos bloqueada y tuve problemas con la edición e inserción de registros. Pues bien, hoy salió una nueva versión de ZeosLib, la 7.2.8 que no corrige esos bugs, pero anuncia que los mismos serán corregidos en la versión 8.0.

"When using Cached Updates, it is not possible to add a row and then edit
that row before posting to the database. This bug cannot be fixed in Zeos 7.2.
It has been fixed in the upcoming Zeos 8.0. Please use database transactions
instead.".

Respecto de la base de datos bloqueada, era cuando usaba una segunda conexión a la base de datos, tuve que eliminar esa segunda conexión. El tema sin solución es que el programa se usa en red y cuando dos o más usuarios se conectan a la base de datos se bloquea. Además era una de las gracias de utilizar el conector de Zeos con la propiedad Autocommit en true y funcionaba de maravilla.

Deberé, otra vez, regresar a la versión 7.1.3 donde todo funcionaba bien.

domingo, 9 de junio de 2019

TZConnection.ExecuteDirect y TZQuery.

Estos dos componentes pertenecen a Zeos Lib.
TZConnection se utiliza primariamente para establecer una conexión a una base de datos. Entre sus métodos está ExecuteDirect, una función sobrecargada (overloaded). Nada mejor que ver el código fuente:

function ExecuteDirect(SQL:string):boolean;overload;
function ExecuteDirect(SQL:string; var RowsAffected:integer):boolean;overload;


Si únicamente enviamos un parámetro, el string con la sentencia SQL, entonces la primera será llamada. Si usamos los dos parámetros, entonces se llamará a la segunda. Breve aclaración de overload.

Generalmente ExecuteDirect se utiliza para todo lo referido a actualizar la base de datos, por ejemplo: UPDATE, CREATE, DELETE, INSERT, VACUUM.

Como vemos, esta función siempre retorna un boolean en ambas versiones, que será True si hubo éxito o False si hubo un error.
Si además usamos el segundo parámetro, que como observamos es por referencia, el mismo se actualizará con el número de filas afectadas.

Por ejemplo, si mandamos VACUUM, siempre devolverá 0 (cero); en cambio si utilizamos un UPDATE nos devolverá la cantidad de registros actualizados.

Una vez más, veamos el código fuente de la implementación de las funciones:

{**
Executes the SQL statement immediately without the need of a TZQuery component
@param SQL the statement to be executed.
Returns an indication if execution was succesfull.
}


function TZAbstractConnection.ExecuteDirect(SQL : String) : boolean;
var
  dummy : Integer;
begin
  result:= ExecuteDirect(SQL,dummy);
end;

{**
Executes the SQL statement immediately without the need of a TZQuery component
@param SQL the statement to be executed.
@param RowsAffected the number of rows that were affected by the statement.
Returns an indication if execution was succesfull.
}


function TZAbstractConnection.ExecuteDirect(SQL: string; var RowsAffected: integer):boolean;
var
  stmt : IZStatement;
begin
  try
    try
      CheckConnected;
      stmt := DbcConnection.CreateStatement;
      RowsAffected:= stmt.ExecuteUpdate(SQL);
      result := (RowsAffected <> -1);
    except
      RowsAffected := -1;
      result := False;
      raise; {------ added by Henk 09-10-2012 --------}
    end;
  finally
    stmt:=nil;
  end;
end;


Ahora veamos la siguiente línea de código:

if ZConnection1.ExecuteDirect(Memo1.Text, n) then Memo1.Lines.Add('OK! '+IntToStr(n)+' filas.');

Sí bien es casi rídiculo hacer esto, un SELECT con ExecuteDirect, se puede, claro que siempre retornará cero, aunque la tabla tenga 500 filas.
En realidad también se puede, por ejemplo usar VACUUM desde un consulta TZQuery. El tema es saber cual de los dos métodos utilizar según lo que necesitemos, desde ya, una consulta será con TZQuery. Un INSERT puede ser tanto con ExecuteDirect o con los métodos de TZQuery, nuevamente, según lo que necesitemos y el estilo propio de cada programador.


Este UPDATE .. SET en realidad no hace nada, pero es válido como ejemplo, ExecuteDirect devuelve 51 en la variable n pasada por referencia, que desde ya, coincide con la cantidad total de filas de la tabla.

TZQuery:

De entrada conviene aclarar una especie de mito que hay de que siempre debe estar asociada con componente TDataSource, esto es falso, así de simple. Solo necesitaremos un TDataSource si los datos de la consulta deben ser mostrados en otros componentes, como ser un TDBGrid, TDBEdit, TDBLookUpComboBox, etc. Muchas veces veo ejemplos de LazReport donde se incluye innecesariamente un TDataSource.

Otra cosa elemental que no se debe intentar hacer, es editar una consulta que contiene JOIN, el error será inevitable. Es un error muy común tener una consulta con JOIN en un TDBGrid y querer actualizar una fila.

Para insertar un registro en una consulta del tipo SELECT campos FROM tabla (y puede también contener WHERE pero nunca JOIN), primero se debe invocar al método INSERT de TZQUery:

ZQuery1.Insert;

Luego, lo más común es utlizar el método FieldByName:

ZQuery1.FieldByName('nombre').asString:=edNombre.Text;
ZQuery1.FieldByName('edad').asInteger:=nEdad;


Y finalmente se usa el método Post para concretar la transacción. Si la propiedad Autocommit de TZConnection es True, entonces la transacción será inmediata:

ZQuery1.Post;

Un campo INTEGER con AUTOINCREMENT: al hacer un INSERT debe ignorarse siempre, ya sea utilizando ExecuteDirect o TZquery.

También puede utilizarse un TZQuery para un UPDATE, SET, etc.:


DataM.ZQa.Close;
DataM.ZQa.SQL.Text:='UPDATE reg SET saldo=saldo-:importe WHERE banco=:elbanco AND fecha>:regfecha;';
DataM.ZQa.Params.ParamByName('elbanco').AsInteger:=elbanco;
DataM.ZQa.Params.ParamByName('regfecha').AsString:=regfecha;
DataM.ZQa.Params.ParamByName('importe').AsCurrency:=importe;
DataM.ZQa.Open;
DataM.ZQa.Close;

Ventaja: se puede usar Params, algo que no se puede con ExecuteDirect.

lunes, 24 de septiembre de 2018

¿Cómo usar dos tablas de distintas bases de datos en la misma consulta?

Para adjuntar una tabla a un DataSet, en este caso TZQuery, y que no produzca un error, la solución que no encontré en ningún lado, la descubrí mediante el sistema de prueba y error hasta que salga. Y como suele suceder cuando no se encuentra algo en toda la web, es porque ese algo, es muy obvio y este caso no fue la excepción.

Primero establecemos la conexión con una de las dos bases de datos mediante el componente TZConnection. Luego hacemos una consulta para adjuntar la base de datos, la abrimos y la cerramos, listo, ya está adjuntada. Luego escribimos la consulta que necesitemos.

Ejemplo:

ZQ.Close;
ZQ.SQL.Text:='ATTACH DATABASE '+QuotedStr(strDB)+' AS realgestdb;';
ZQ.Open;
ZQ.Close;
ZQ.SQL.Text:='SELECT cfecha, cprovid, nombre, ccomp, realgestdb.comprob.ccomprobalias, cletracomp, '+
'cnrocomp, ccaeocai, ccai, cuit, cnetogravado, cnogravado, cimpinternos, cpercib, cperciva, ctasaiva, '+
'civa, cnetogravado1, ctasaiva1, civa1, cnetogravado2, ctasaiva2, civa2, ctotal '+
'FROM ccompras '+
'INNER JOIN cprov ON cprovid=provid '+
'INNER JOIN realgestdb.comprob ON ccomp=realgestdb.comprob.id '+
'WHERE cfecha BETWEEN '+QuotedStr(desde)+' AND '+QuotedStr(hasta)+
'ORDER BY cfecha, cprovid ;';
ZQ.Open;


Las tres primera lineas realizan el ATTACH DATABE y ya queda disponible para cualquier consulta que se realice en el mismo dataset, hasta que se des adjunte, para ello:

ZQ.Close;
ZQ.SQL.Text:='DETACH DATABASE '+QuotedStr('realgestdb')+';';
ZQ.Open;
ZQ.Close;


Es importante para adjuntar, enviar el path completo de la base de datos y entre comillas simples, para eso nada más cómodo que la función QuotedStr. En este caso strDB es una variable del tipo string que contiene el path completo de la base de datos a adjuntar. Luego con AS le establecemos un alias para luego referenciarla en las consultas SQL. El alias puede ser cualquier nombre.
Pero ojo, que para realizar el DETACH DATABASE se utiliza el Alias, no el path completo de la base de datos. Esto se debe que, al menos SQLite, permite adjuntar variar veces una misma base de datos bajo distintos Alias (AS).

La cuarta línea cierra la consulta. La quinta, cambia la consulta y para acceder al campo ccomprobalias de la tabla comprob de la base de datos adjuntada bajo el alias de realgestdb lo hacemos de la forma Alias.tabla.campo.

miércoles, 23 de mayo de 2018

TDataSource cuándo usarlo?

Lazarus, Free Pascal y muchos de sus componentes son sencillamente espectaculares, más aún teniendo en cuenta que es un proyecto de código abierto que comenzó hace muchos años (2001 creo), con pocos programadores para tamaño proyecto, actualmente entre Free Pascal y Lazarus, son unos 20.
El problema, lo dije desde un principio cuando retomé la programación, motivado por la existencia del IDE Lazarus, es la documentación, muchas veces inexistente y otras veces hay que dedicar mucho tiempo para dar con ella.
Los tutoriales para trabajar con bases de datos son pocos, algunos desactualizados y casi siempre los mismo ejemplos.
Es así como aprendí que para conectar y trabajar con una BD se necesitan 3 componentes: el conector (TZConnection), la consulta (TZQuery) y la fuente de datos (TDataSource). Pues no es así. Me di cuenta leyendo y participando del foro, cuando un usuario planteó una duda y detecté que en su código no utilizaba ningún TDataSource, le pregunté y me respondió si era necesario. Me sembró la duda y de hecho la respuesta es: No. Eso es cambia mucho mi panorama, para empezar no necesitaré tantos data modules si solo tengo un para de consultas, puedo declarar las conexiones y consultas sin necesidad de utilizar el data aware y que no molesten en el Form. Ahora entiendo por qué una vez me dijeron que cuando comienzas con esto, usas todo data aware y luego vas directamente por el código, y así es.

El data aware TDataSource solo es necesario cuando necesitamos enlazar los datos con un componente como puede ser un DBGrid, DBComboBox, etc. caso contrario no es necesario.

El ícono es muy claro, el componente envía los datos de un TDataSet hacia otros componentes, es indispensable para un TDBGrid por ejemplo, por eso es que el DataSource se debe vincular con un DataSet (y éste con un conector) y lo que alimenta al DBGrid es el DataSource. Ahora si no necesitamos alimentar ningún control, entonces no es necesario utilizar ningún DataSource.

jueves, 10 de mayo de 2018

SQL: Insertar registros en una tabla con campo auto increment.

¿Cómo ejecutar correctamente el comando SQL para insertar filas que contienen una columna auto incremental?

Por ejemplo, una tabla (tabla1) con 4 campos: id, nombre, apellido y edad.

CREATE TABLE tabla1 (id INTEGER UNIQUE NOT NULL PRIMARY KEY AUTOINCREMENT, nombre VARCHAR(50), apellido VARCHAR(50), edad INTEGER);

Error común:

INSERT INTO tabla1 VALUES ('Juan', 'Pérez', 25);

Esto arrojará un error del tipo "la tabla tiene 4 columnas pero solo se proporcionan 3 valores" y es cierto, pero claro, no se puede pasar el valor id porque el mismo debe establecerlo SQL.
La solución es simplemente especificar los campos:

INSERT INTO tabla1 (nombre, apellido, edad) VALUES ('Juan', 'Pérez', 25);

En caso de utilizar Zeos esto se realiza mediante ZConnection1.ExecuteDirect o también puede hacerse mediante el dataset ZQuery que sería algo así: (con la tabla ya creada)

ZQuery1.SQL.Text('SELECT * FROM tabla1;');
ZQuery1.Insert;
ZQuery1.FieldByName('nombre').AsString:='Juan';
ZQuery1.FieldByName('apellido').AsString:='Pérez';
ZQuery1.FieldByName('age').AsInteger:=25;
ZQuery1.Post

viernes, 9 de febrero de 2018

Guardar y leer imágenes en bases de datos.

Además de ver como se guarda una imagen de cualquier formato (JGP, PNG, etc.) en una base de datos SQL (SQLite en este caso), también veremos como leerla y mostrarla en un TImage en un formulario.
Logotipo: TImage;
Al TImage del Form1 lo llamamos Logotipo.
Para leer la imagen y mostrarla en un TImage en un Form:
procedure TFom1.CargoDatos;
var
  unstream:TMemoryStream;
begin
  //Se cargan la campos "normales"...
  if ZQuery1.FieldByName('logo').IsNull then
  begin
    Logotipo.Picture.Clear;
    Exit;
  end;
  unstream:=TMemoryStream.Create;
  unstream.Position:=0;
  TBlobField(ZQuery1.FieldByName('logo')).SaveToStream(unstream);
  unstream.Position:=0;
  Logotipo.Picture.LoadFromStream(unstream);
  unstream.Free;
end;
Declaramos una variable del tipo TMemoryStream donde almacenaremos el contenido de la imagen que se encuentra en el campo "logo" de una tabla.
Para no tener problemas, averiguamos si existe tal imagen, lo hacemos con FieldByName('logo').IsNull. Si esto devuelve True entonces borramos la imagen de LogoTipo, si no hacemos esto quedará la imagen cargada anteriormente si la hubiere. Si IsNull devuelve False quiere decir que hay una imagen (o debería haberla), entonces creamos el stream, lo posicionamos en 0 (cero) y lo cargamos con TBlobField(el campo).SaveToStream y cabe aclarar que no es necesario definir ninguna variable del tipo TBlobField, este procedimiento se encarga de todo. Ahora ya tenemos la imagen del campo logo de una tabla de una base de datos cargada en un stream de memoria, el paso final es mostrarla en el formulario, para eso posicionamos nuevamente a 0 (cero) el stream, y lo mandamos al TImage nombrado Logotipo y no olvidarse de liberar el stream utilizando el método Free.

Ahora lo inverso, leer la imagen y guardarla en la base de datos. Se omite la carga desde archivo en este ejemplo.
procedure TForm1.GuardarDatos(Sender: TObject);
var
  ms:TMemoryStream;
begin
  //Se pone el dataset en modo edit o insert y se graban los 
  //campos "normales"...
  if (logotipo.Picture.Width>0) then
    begin
      ms:=TMemoryStream.Create;
      Logotipo.Picture.SaveToStream(ms);
      ms.Position:=0;
      TBlobField(ZQuery1.FieldByName('logo')).LoadFromStream(ms);
      ms.Free;
    end
  else
    begin
      ZQuery1.FieldByName('logo').AsString:='';
    end;
  ZQuery1.Post;   
end;
Como en el código anterior, necesitamos una variable para el stream en memoria. Y nuevamente para no tener problemas, mediante Logotipo.Picture.Width>0 determinamos si hay alguna imagen que guardar, caso contrario se guarda NULL, AsString:='' hace eso.
Si hay imagen, creamos el stream y le asignamos la imagen que está contenida en la propiedad Picture de Logotipo (TImage). Posicionamos en 0 (cero) el stream que ya contiene la imagen y nuevamente nos valemos de TBlobField que asignará el stream al campo "logo" y liberamos con Free el stream.

Desde ya es un código orientativo, pero testeado que este método funciona, al menos para SQlite utilizando ZeosLib.

Hay muchas formas de hacer esto, pero esta forma es la que menos problemas me trajo y es bastante simple y "limpia". He probado antes con DBImage, pero a veces ejecutando el código desde la IDE me tiraba un error EReadError que podía ignorar y todo seguía bien, de hecho ejecutando el programa (fuera de la IDE) esos errores no se mostraban, hasta que detecté que si la imagen que leía DBImage no era JPG entonces largaba ese error, eso me motivo a deshacerme de TDBImage y hacerlo como lo muestro, básicamente con un stream, el procedimiento TBlobField y un TImage.

domingo, 14 de enero de 2018

LazReport: incluir imagen de campo BLOB

Antes esto era una tarea un poco complicada según pude observar después de varias búsquedas que me mostraban hilos de foros de hace unos cuantos años. Por suerte esto ya no es así e incluir una imagen de cualquier formato (dentro de los más populares) almacenada en un campo o columna del tipo BLOB es tan sencillo que no requiere ni una línea de código.

Desde el diseñador LazReport debemos incluir un objeto del tipo imagen y nos aparecerá el siguiente diálogo:


La opción Cargar es para cargar una imagen contenida en un archivo, no es el caso. Debemos hacer click en Texto.


Y aquí tanto solo indicamos el campo que contiene la imagen. Si el dataset está conectado, podemos agregarlo desde el botón Campo de DB.
Eso es todo.


domingo, 26 de noviembre de 2017

Recorrer un DataSet y actualizar registros

Si queremos actualizar un campo de una fila o registro mientras recorremos el TDataSet por ejemplo mediante un while, obtendremos un error en tiempo de ejecución. Con un ejemplo será más sencillo:

WHILE NOT ZQuery1.EOF DO
begin
  ZQuery1.Edit;
  ZQuery1.FieldByName('alguncampo').AsInteger:=0;   
  ZQuery1.Post
  ZQuery1..Next;
end;


El código precendente nos dará el error y es correcto que lo haga, porque no situamos el puntero al comienzo del DataSet que sería con ZQuery1.First.

ZQuery1.First;
WHILE NOT ZQuery1.EOF DO
begin
  ZQuery1.Edit;
  ZQuery1.FieldByName('alguncampo').AsInteger:=0;   
  ZQuery1.Post
  ZQuery1..Next;
end;


Otra manera de resolver esto es mediante el uso de ExecuteDirect (y más rápido si se trata de un gran volumen), si tenemos un identificador único, mejor:

ZQuery1.First;
WHILE NOT ZQuery1.EOF DO
begin
  ZConnection1.ExecuteDirect('UPDATE tabla SET alguncampo=0 WHERE    identificaorunico='+ZQuery1.FieldByName('id').AsString'+' ;');
  ZQuery1..Next;
end;


Se recomienda hacer esto cuando no podamos hacerlo mediante una sentencia SQL UPDATE, ya que puede ser lento dependiendo del volumen de datos.

viernes, 24 de noviembre de 2017

Borrar registros de un dataset mediante While

El problema aquí es que pasa con el puntero cuando se elimina un registro y el next. Pues bien, cuando se elimina el registro, pasa al siguiente, por lo tanto hay que evitar el next cuando se borra.

While not (DMF.ZQReg.EOF ) do
begin 

  if algunacondicion then
    begin
      nEmple:=DMF.ZQReg.FieldByName('regemple').AsInteger;
      dFechaHora:=DMF.ZQReg.FieldByName('regfechahora').AsDateTime;
      DMF.ZQReg.Edit;
      DMF.ZQReg.Delete;
      Inc(nContador);
    end
  else
    begin
      nEmple:=DMF.ZQReg.FieldByName('regemple').AsInteger;
      dFechaHora:=DMF.ZQReg.FieldByName('regfechahora').AsDateTime;
      DMF.ZQReg.Next;
    end;
end; 


En este caso debo eliminar registros duplicados de lector biométrico, en algunacondicion evaluo si el registro es duplicado y tomo los datos antes de borrarlo, porque a veces los datos están hasta quintuplicados.
Como puede verse, el Next lo realizo solo si no se elimina el registro (fila).

Si el volumen de datos a procesar es grande, conviene utilizar una conexión sin autocommit y utilizar BEGIN y COMMIT tal como se explica aquí.

miércoles, 6 de septiembre de 2017

DBGrid: Formato de la columna según el tipo de campo

En tiempo de diseño y conociendo la tabla es una cosa, pero en tiempo de ejecución y sin saber nada de la tabla, ya es distinto. Para empezar nos encontramos con tipos de datos enumerados y por ende, debemos conocerlos. Luego hay que recorrer las columnas o campos y definir el formato según lo que necesitemos, siendo lo normal los tipos de dato numéricos y de fecha. Para el ejemplo se recorrerán las columnas de una tabla SQL y se cambiará el formato del DBGird si el campo es numérico decimal.

procedure TForm1.FormatoColumnasGrid;
var
  ind:Integer;
begin
  FormatSettings.DecimalSeparator:='.';
  for ind:=0 to ZQ.FieldCount-1 do
    if (ZQ.FieldDefs.Items[ind].DataType=ftFloat) or (ZQ.FieldDefs.Items[ind].DataType=ftCurrency)
    or (ZQ.FieldDefs.Items[ind].DataType=ftBCD) then
      DBGrid1.Columns[ind].DisplayFormat:='#0.00';
end;


Este procedimiento se llama luego de habilitarse el DBGrid. Se establece el separador decimal para establecer correctamente el formato aunque también se podría hacer de forma automática o preguntarle al usuario.

Se utiliza FieldCount – 1 porque la indexación del ZQuery (de ZeosLib) comienza por cero. DataType es del tipo enumerado, para SQL los valores de numéricos con decimales son: ftFloat, ftCurrency y ftBCD,

Listado de tipos de datos completo en español 

Documentación de TFieldType

Insert masivo y rápido en SQLite

El que sabe, sabe; y el que no, se pasa horas buscando, por eso este simple post para algo tan simple, para los que estamos eternamente aprendiendo.

No hay ningún problema con la ejecución directa de sentencias SQL cuando se trata de pocos registros a insertar, haríamos algo así: (pseudo código)

While not Eof do
  ExecuteDitect('lo que sea');


Si nuestro conector con la base de datos tiene la propiedad autocommit en True y son pocos registros, el usuario no lo notará. El problema es que cada vez que se completa una transacción, SQLite requiere dos completas rotaciones del plato del disco, tendiendo en cuenta unas 7.200 rotaciones por minuto, con suerte, viento a favor y sin usar progressbar, podríamos insertar 60 registros por segundo, es decir, 1.200 registros tomaría 20 segundos, entonces debemos incluir una barra de progreso para que el usuario no piense que el programa dejó de funcionar, la querida barra de progreso relentizará aún más el proceso. Hasta aquí la explicación de por qué demora tanto.

Solución: BEGIN …. COMMIT es decir, encerrar las transacciones entre un BEGIN y un COMMIT.

Ejemplo con el componente ZConnection de ZeosLib:

if ZConnection1.Connected then ZConnection1.Disconnect;
ZConnection1.AutoCommit:=False;
Zconnection1.Connect;
ZConnection1.ExecuteDirect('BEGIN; ');
while not EOF(f) do
begin
  ReadLn(f,s);
  ZConnection1.ExecuteDirect(s);
end;
CloseFile(f);
ZConnection1.ExecuteDirect('COMMIT; ');
ZConnection1.Disconnect;


En este caso, f es un archivo de texto plano que contiene lenguaje SQL. La velocidad es increíble, un archivo de 7,7 MB  en menos de 2 segundos, para más de 45.000 registros de 12 campos.

La propiedad autocommit de ZConnection1 debe estar en False, podemos hacerlo en el inspector de objetos de Lazarus o por código.

Obtener la lista de tablas de una base de datos SQL

Es algo muy simple desde consola o en tiempo de diseño, pero en tiempo de ejecución? También es sencillo, lo difícil fue encontrar cómo hacerlo. Resulta que Zeos (ZeosLib), más precisamente su principal componente, ZConnection, posee un procedimiento llamado GetTableNames que nos devuelve un parámetro del tipo TStrings pasado como referencia (lógico, si no fuese pasado como referencia no devolvería nada) con la lista de todas las tablas de la base de datos conectada. Se le puede pasar un TStringList o un ListBox.items por ejemplo.

 Primer ejemplo con TStringList y un TMemo:

 // Definir procedimiento o función o agregar el código donde sea necesario
var
  listatablas:TStringList;
  i:Integer;        
begin
  listatablas:=TStringList.Create; 
  ZConnection1.GetTableNames('',listatablas);   // La base de datos debe estar conectada...
  for i:=0 to listatablas.Count-1 do
    Memo1.Lines.Add(listatablas[i]);     // Memo1 debe estar en el Form
  ...
end;


El primer parámetro pasado podría haber sido por ejemplo ‘c*’ en cuyo caso hubiésemos obtenido la lista de tablas cuyos nombres comiencen con c. Al pasarlo vacío le indicamos que llene el StringList con todas las tablas.

Este ejemplo, con muy poco código, alcanza y sobra para mostrar los nombres de las tablas, pero si queremos que además el usuario pueda elegir una (o varias) tablas podemos valernos de un ListBox. Para ello, no le pasaremos como parámetro el ListBox sino ListBox.Items que es del tipo TStringList.

ZConnection1.GetTableNames('',ListBox1.Items) // Requiere un ListBox en el Form
 

martes, 5 de septiembre de 2017

SQLite crear una base de datos con código

A diferencia de otras bases de dato SQL, SQLite carece del famoso CREATE DATABASE porque si la base de datos no existe, la crea automáticamente.

En tiempo de diseño solo debemos completar la propiedad Database de un componente Zconnection y activar la propiedad Connected, con eso ya se creo la base de datos.


Con código sería así:
   
ZConnection1.Database:='prueba.db';
ZConnection1.Connect;

Aclaración: Zconnection es un componente de ZeosLib, un conjunto de componentes para manejar bases de datos.