ARCHIVOS Y BASES DE DATOS EN VISUAL BASIC

Recuerdo mi alegría cuando se me ocurrió la idea de rellenar los datos con espacios en blanco para darles un largo fijo y  archivaros como una tabla. Mucho después vine a saber que mi "invento" era un procedimiento estándar en los archivos aleatorios y se podia hacer con una simple instrucción..

Claro que aprendí a programar en un computador Casio FX-900P, que tenía un sistema de archivos bien primitivo y  reinventar los archivos aleatorios, secuenciales e indexados fue muy bueno para entender mejor como funcionan las cosas 

Esta explicación práctica sobre archivos y bases de datos en Visual Basic, está orientada al principiante y también para los que creen que la única forma decente de almacenar información es en una  base de datos.

Almacenar datos en una tabla

Esta es la forma mas usada por ser sencilla e intuitiva, es lo que usan las bases de datos "relacionales" y los archivos planos de tipo "random" o aleatorio. Consiste en definir campos, nombres y largos, con lo que describimos una grilla cuyas "lineas" corresponden a "registros" y las "columnas" a "campos". Los que conocen las bases de datos o las hojas de cálculo conocen bien esta idea, por ejemplo:

NRO NOMBRE DIRECCION TELEFONO
1 PEDRO LOA 123 223344
2 JUAN ACACIAS 222 345566
3 DIEGO CODORNICES 324 765544

También existieron en su época las bases de datos jerárquicas (recuerdo la Q&A que era sensacional) pero nunca tivieron tanto éxito como las relacionales. El hecho es que estamos acostumbrados a almacenar datos en tablas

Los Archivos Random

¿Como se guarda esto en un archivo plano usando Visual Basic?, pensemos un poco en lo que necesitamos para que quede físicamente grabado en el disco:

  • Necesitamos guardar en algún lado la cantidad de registros almacenados, para saber en que posicion se guardará el registro siguiente (en el ejemplo mostrado hay 3 registros). Esto podria hacerse en otro pequeño archivo o, por ejemplo, en la primera posicion del mismo archivo, el que quedaría físicamente así:

3

PEDRO LOA 123 223344
JUAN ACACIAS 222 345566
DIEGO CODORNICES 324 765544

Nótese que eliminamos el campo correspondiente al "número", éste no necesitamos grabarlo pues está implícito en la posición física donde va grabado el registro.

  • Luego necesitamos grabar los datos "alineados" es decir todos los campos deben tener el mismo largo. Supongamos que definimos de antemano que el campo "nombre" debe tener 30 caracteres, el campo "direccion" 40 caracteres y el campo "teléfono" 20 caracteres ¿como los alineamos? Simplemente agregándole espacios en blanco. Por ejemplo si entramos el valor "PEDRO" en el campo de nombre (5 caracteres) tendremos que agregarle 25 espacios en blanco, a "JUAN" (4 caracteres) le agregamos 26 espacios, etc. No es necesario hacer una rutina para agregar espacios pues esto se hace fácilmente en Visual Basic con:

Type Registro_de_agenda
        nombre as string * 30
        direccion as string * 40
        telefono as string * 20
End Type


Global agenda as Registro_de_agenda

Este código se guarda en un módulo .Bas donde van las declaraciones, valores de inicialización y variables globales o públicas, etc.

Las instrucciones Type....End Type convierten los valores que entramos en esas variables en largo fijo y la declaración Global (o Dim, Public o Private, según nos convenga) sirve para crear variables "con apellido" llamadas "tipos de datos definidos por el usuario", así de ahora en adelante las variables se llamarán:

agenda.nombre
agenda.direccion
agenda.telefono

Esta es una notación muy conveniente y emparentada con la notación  de objetos: es decir el objeto "Agenda" tendría las propiedades "nombre", "dirección" y "telefono"

Luego, para crear un archivo del tipo "random" nos falta usar solo tres instruciones más "Open" (abre un archivo), "Put" (graba un registro), "get" (lee un registro)  y "Close" (cierra el archivo),. Para "iniializar" un archivo con 1000 registros en blanco por ejemplo, abrimos una Form, le colocamos un botón con el Captios "Inicializar (Borrar todo)" y le programamos el evento click con:

Sub Inicializar()
   Open "Archivo_de_Agenda" as 1 for Random
   For z=1 to 1000 
       agenda.nombre=""
       agenda.direccion=""
       agenda.telefono=""
       Put 1, z, agenda
   Next z
   Close
End Sub

¿Se podría usar el archivo sin inicializarlo? si, el único inconveniente sería al leer registros vacíos aparecería "basura" es decir cualquier información que haya en el buffer en ese momento, lo que es bastante incómodo si estamos listando por ejemplo

Colocando los textboxes Text1, Text2 y Text3 en el form para ingresar los campos, la rutina para agregar registros sería

Sub agregar()
    Open archivo For Random As 1
    rem
   
rem leer el indice, incrementar y grabarlo
    rem
   
Get 1, 1, agenda
    ultimo = Val(agenda.nombre)
    If Val(ultimo) = 0 Then ultimo = 1 rem si esta vacio lo pone en 1
    puntero = ultimo + 1
    agenda.nombre = puntero
    Put 1, 1, agenda
    rem
    rem puntero almacena donde debe grabarse el nuevo registro
    rem
    agenda.nombre = Text1.Text
    agenda.direccion = Text2.Text
    agenda.telefono = Text3.Text
    Put 1, puntero, agenda
    Close
End sub


Para listar el archivo simplemente se lee el indice y se hace un ciclo for - next partiendo desde la posición 2 asi:

Sub listar()
     Open archivo For Random As 1
     rem leer el indice
     Get 1, 1, agenda
     ultimo = Val(dat.nombre)
     rem listar
     For z%=2 to indice
           Get 1, z%, dat
            List1.AddItem dat.nombre
       Next z
Close

Existen dos problemas que a menudo están relacionados al usar archivos random, son los de buscar (y encontrar) rápidamente un registro y presentar el archivo ordenado según algún criterio. En ambos casos se usa uno o más  archivos auxiliares de índice, que almacenan la ubicación física de los registros en distintos órdenes según se requiera. Programar estos índices y usar métodos de ordenación y búsqueda son procedimientos más o menos complicados, y es una de las razones principales que justifican el uso de motores de base de datos que automatizan estas dos tareas. En la programación convencional sin usar bases de datos, se usan normalmente el método ISAM (Indexed Sequencias Access Mode) y el algoritmo Quick Sort.

Archivos Secuenciales

En los archivos secuenciales los datos no se almacenan en campos de largo fijo sino como una secuencia continua de datos con algúl caracter delimitador que los separa, el ejemplo anterior quedaría asi:

Pedro,Loa123,223344,Juan,Acacias 222,345566,Diego,Codornices 324,765544

Un archivo secuencial es como una tira de salchichas donde los datos solo pueden ir agregandose al final y se debe leer y escribir la secuencia completa cada vez. Son por lo general complicados de actualizar, ordenar y bastante lentos, pero tienen algunos usos interesantes como por ejemplo grabar archivos de texto completos y particularmente archivos del tipo .ini, muy usados en Windows para ingresar settings


Para usar archivos secuenciales en almacenamiento de datos el procedimiento es trabajar con toda la sarta de salchichas almacenada en un array en memoria. Esto limita bastante el tamaño y el rendimiento de estos archivos. Me parece que las hojas de cálculo usan una estrategia similar y por eso son tán limitadas en cuanto al manejo de hojas grandes.

Para leer un archivo secuencial de texto en una lista List1, usamos:

Sub leerarchivo()
        Dim lineatemporal As String
        List1.Clear
        Open "Archivo_de_Datos" For Input as 1
        While not EOF(1)
                   Line Input 1, Lineatemporal
                   List1.Additem Lineatemporal        
        Wend
        Close 1
End Sub

Y para escribir (agregar al final) en un archivo secuencial de texto, usamos:

Sub escribearchivo()
         Open "Archivo_de_Datos" for Append Shared As 1
          Print 1,  "dato a grabar"
          Close 1
End Sub

Bases de Datos

Como los conceptos de manejo de bases de datos son bien conocidos por todos los programadores formados en la última década no los voy a repetir, solo pondré algunos problemas que yo veo en el uso de bases de datos en sistemas pequeños.

Para entender la ideología que hay detrás de las bases de datos hay que  recordar los problemas de la programación directa de archivos.

 Recuerdo que el primer motor de bases de datos para PC que conocí se llamaba DBMS, creo que era de Ashton Tate y fue el predecesor del Dbase. Me pareció una idea estupenda ¿como a nadie se le había ocurrido hacer un utilitario que automatizara todas las rutinas de creacion y mantencion de archivos, los índices y ordenaciones?, de nuevo estaba atrasado de noticias pues en los equipos grandes desde hace algunos años se venían incluyendo los primeros motores de base de datos como parte de los servicios de archivos del sistema operativo.

Así, la idea prendió rápidamente y el Dbase de Ashon Tate se convirtió en componente muy popular de la programación de aplicaciones, el resto es historia conocida: la extensión de los comandos del Dbase hasta convertirlo en un lenguaje bastante funcional, la enorme cantidad de gente que se formo programando aplicaciones en Dbase, la aparición del Clipper primero como compilador de Dbase y luego como entorno de desarrollo independiente, etc.

En forma paralela fue surgiendo en círculos académicos toda una ideología para justificar las bases de datos como único método aceptable para almacenar la información, esto ocurrió por una serie de factores entre ellos:

  • La complejidad creciente de algunas aplicaiones que hacían poco práctico tener que concentrarse en los procesos de grabación física de los datos
  • La necesidad de estandarización de métodos y formatos de archivo, producida por la aparición de grandes sistemas que debían relacionar archivos de distinta procedencia
  • La programación cooperativa, que obliga a restringir las herramientas y métodos del programador para que los distintos eslabones puedan entenderse entre sí en un proyecto grande

El actual enfoque académico se centra en el tratamiento de los datos como entidades abstractas, modelandolos en una capa común que todos pueden entender (Teoría de Conjuntos) y prohibiendo el acceso del programador a los detalles de la grabación física de los datos. Incluso para las instrucciones de manipulación de datos y consultas se desarrolló una especie de esperanto que es prácticamente estándar: el SQL

Esto tiene grandes ventajas para los sistemas grandes, donde la estandarización y cooperación son prioritarias, pero también tiene sus inconvenientes que se notan especialmente en los sistemas pequeños.

El principal problema de desentenderse del "trabajo sucio" de administrar la grabación y recuperación física en el disco es que algún tercero tiene que hacerlo. Este tercero es el proveedor del lenguaje o del motor de base de datos y el es quien tiene que optar por una gran cantidad de compromisos por la enorme variedad de requerimientos que tienen los usuarios. Es el viejo problema de un traje hecho a la medida en comparación con otro comprado en una tienda.

En Visual Basic por ejemplo, para llenar un simple archivo en el disco usando base de datos hay que cargar enormes y complejas bibliotecas de referencia, aparte del propio motor Jet incluído en el lenguaje.¿cual es la razón de esto? simplemente el fabricante del lenguaje en este caso no tiene modo de saber si usaremos una simple tabla de datos o bien una inmensa base compuesta por cientos de tablas remostas en distintos formatos, así está obligado a ponerse el parche antes de la herida y cargar todas las funcionalidades incluso para los archivos más simples

Eso explica que casi en cada versión de Visual Basic aparezca un modelo de acceso a bases de datos nuevo, mas pesado, complejo y a veces incompatible con el anterior, así hemos visto al RDO, DAO, ADO y quien sabe qué nos traerá el futuro.

Para sistemas pequeños, que trabajan en ambientes PC pobremente estandarizados (y que por naturaleza jamás llegarán a estandarizarse totalmente) este esquema presenta muchos problemas: la distribución de las aplicaciones no siempre es fácil por el problema de múltiples Dll y OCX que requieren los objetos de acceso a datos, incluso la aplicación más simple se complica al tener que usar tal cantidad de bibliotecas de apoyo.

Lo que es peor, por la filosofía de aislar al programador del almacenamiento físico de los datos, cuando la base de datos se corrompe nos topamos con una muralla de "magia negra" que solo los que diseñaron el motor de base de datos comprenden.

A mi modo de ver, el ocultamiento físico de los datos es muy perjudicial en sistemas pequeños, si se trata de protegerlos contra intrusión o cosas así hay medios mucho más lógicos, pero siempre dejando entrada al programador que esté debidamente habilitado. Las bases de datos debieran, en mi opinión grabarse físicamente en un formato realmente estandar y simple: archivos ASCII planos y debieran usar un motor diferente según el grado de complejidad de la estructura. Eso de "one size fits all" nunca me ha convencido.

 
Cuando Conviene  Usar una Base de Datos

Primero que todo, cuando usarlas. Según San Yo, es ventajoso usar bases de datos cuando:

  • Hay que hacer clasificaciones, ordenaciones y demás reportes que hacen necesario el uso de índices

  • Hay que hacer búsquedas rápidas en grandes archivos por claves no relacionables a la ubicación física, por ejemplo por nombres. Con los equipos actuales una búsqueda secuencial simple es aceptable hasta unos 10.000 registros, más allá de eso son recomendables las bases de datos

Para archivos menores que no requieren reordenamiento, guardar los datos en archivo ASCII plano produce aplicaciones robustas, rápidas, compactas y de fácil distribución y mantención. A `prueba de balas.

Diseño de las Base de Datos

No repetiré los conceptos bien conocidos de eliminar la redundancia, modelar la aplicación, establecer los índices y normalizar los datos. Creo que eso es lo que se enseña en todas partes. Lo que diré tiene relación a criterios prácticos que hay que tener siempre en cuenta al diseñar BD en sistemas pequeños.

  1. Simplicidad: el modelamiento que se enseña tradicionalmente está enfocado a grandes bases de datos y se centra en la eficiencia. Para sistemas pequeños la eficiencia es muchas veces despreciable, no asi la simplicidad del diseño
  2. En relación a lo anterior el número de tablas en un sistema pequeño debe mantenerse en el mínimo, lo mismo que los índices. El ideal es una sola tabla, un solo índice
  3. La redundancia no es necesariamente mala en los sistemas pequeños, siempre y cuando no presente riesgos de inconsistencia, es preferible un sistema con datos redundantes a un sistema segmentado en varias tablas
El Administrador Visual y el Data Control en Visual Basic

Pasar de la idea de archivo a la de base de datos requiere un pequeño ajuste: se supone que una base de datos puede contener distintas tablas, las tablas corresponden a nuestra idea de archivo de datos. Para sistemas pequeños es usual tener una base de datos consistente en una sola tabla es decir un solo archivo.

Visual Basic 6.0 tiene estas dos herramientas básicas que permiten hacer gran parte del trabajo básico con una gran sencillez. Para sistemas pequeños es muy recomendable usarlas.

El administrador visual de datos permite de manera sencilla y en base a menús:

  • Crear una nueva base de datos
  • Agregar tablas
  • Cambiar campos en la tabla
  • Agregar índice a la tabla
  • Modificar la estructura de la tabla
  • Cambiar nombre, copiar, eliminar una tabla

En palabras simples, usar el administrador visual es como usar el antiguo Dbase, lo mismo puede hacerse por ejemplo desde Microsoft Accsess u otras herramientas similares

El Data control es un control OCX que al agregarlo a un form permite enlazar con nuestra base de datos a los siguientes controles:

  • Textbox
  • Label
  • Checkbox
  • Picturebox
  • Image

¿Como se hace? es bastante sencillo, al data control se le colocan las propiedades DatabaseName (nombre de la base de datos) y RecordSource (nombre de la tabla). El control se encargará de hacer el trabajo de conexión y de proveernos de las facilidades de navegación que necesitemos creando en la memoria un Recordset en tiempo de ejecución. Luego ponemos controles enlazados (típicamente Textboxes) a los que le ajustamos las propiedades correspondientes a datos (DataSource, DataField)

¿Qué es un Recordset?

Paremos un momento el asunto práctico para ver como trabajan físicamente las bases de datos. Ya sabemos que el programador queda aislado de la forma en que los datos son físicamente grabados, así es que solo tiene acceso a una "imágen" en memoria de los datos físicos (por algo las bases de datos son tan sensibles a los cortes de energía)

Esta imágen es lo que se llama un Recordset (conjunto de registros), existen distintos tipos de recordset que el motor de base de datos entrega por ejemplo:

  • Dynaset (recordset dinámico) cambia al mismo tiempo que cambian los datos en la BD subyacente
  • Snapshot (instantánea) muestra la BD subyacente en un momento dado, no muestra los cambios posteriores (obviamente se usa para emitir reportes)
Comandos VB para Aplicaciones Básicas

Las instrucciones SQL (Structured Query Language, o Lenguaje Estructurado de Consultas) son el método estándar de manipular bases de datos por ejemplo:

SELECT * FROM mercaderias ORDER BY descripcion

Es un típico filtro SQL que ordena el recordset mercaderiass según la clave descripcion (recordemos que el recordset es una vista en memoria de la tabla de la base de datos y que descripcion es el nombre de uno de los campos de la tabla)

mercaderias.Recordset.AddNew

Permite agregar al final de la tabla mercaderias un nuevo registro

mercaderias.Recordset. Delete
If Not mercaderias.Recordset. EOF Then
        mercaderias.Recordset. MoveNext
Else
        mercaderias.RecordsetMovelast
End If

Borra el registro y recorre el Recordset hasta el final para actualizarlo

En suma, el data control y las instrucciones SQL son todo lo que se necesita para la mayoría de las aplicaciones pequeñas de base de datos, programar usando DAO, RDO o ADO una aplicación sencilla es casi siempre un desperdicio de esfuerzo y de código

DAO, RDO, OBDC, ADO ¿Para qué Sirven?

Fundamentalmente estos tres métodos de programación de bases de datos ofrecen:

  • Más instrucciones, y la posibilidad de hacer todas las tareas programáticamente, sin necesidad de usar Datacontrol ni controles enlazados
  • Más control sobre la manera en que se actualizan los datos, posibilidad de recibir datos automáticamente sin intervención de la consola
  • Permite usar instrucciones de programa además de las SQL usuales
  • Permite usar distintos origenes de datos, aún cuando no estén basados en PC, como Oracle, Servidor SQL, etc.

DAO = Data Access Objects (Objetos de Acceso a Datos) es el método antiguo (usado en VB 5) para programar bases de datos. DAO usa la antigua tecnología OLE para conectarse con las bases de datos.

RDO=-Remote Data Objects (Objetos de Datos Remotos) es el método usado para conectar PCs con bases de datos remotas en un ambiente cliente-servidor (por ejemplo una base de datos Oracle en un equipo remoto Unix), Usa la ODBC (Open Data Base Conectivity, o Conectividad Abierta de Bases de Datos), los controladores OBDC son el método que emplea en general Windows 9x para conectarse con bases de datos externas. También existe un método abreviado para estas conexiones a bases de datos externas, es usando el Remote Data Control RDC, que es el análogo al DataControl  para el DAO

ADO = ActiveX Data Objects (Objetos de Datos ActiveX) es el método usado por VB 6.0 para acceder por programa a bases de datos. Usa la tecnología OCX en lugar de OLE y existe, desgraciadamente, incompatibilidades entre DAO y ADO

En resumidas cuentas ¿que significa toda esta sopa de letras?. físicamente son bibliotecas, Dll, objetos OLE y OCX así como bibliotecas del Windows (en el caso de OBDC), que deben cargarse junto con el programa e incluirse en la distribución. Para apliocaciones pequeñas no recomendaría usar ninguno de estos métodos que hacen a las aplicaciones pesadas, potencialmente inestables y difíiles de distribuír y portar

BASES DE DATOS, EJEMPLOS PRACTICOS
Con el data control se automatiza la conexión entre la aplicación Visual Basic y la Base de Datos . El data control accede a los registros uno a la vez (si queremos usar varias tablas simultaneamente debemos poner tantos controles como tablas). Algunas de las bases de datos a que nos podemos conectar son:
  • Access
  • FoxPro
  • dDase - xBase (¿se acuerdan?)
  • Paradox
  • Btrieve
  • Excel
  • Archivos de Texto
  • OBDC (conectividad abierta para otras como Oracle)

 Las propiedades relevantes del data control son:

  • Connet (conexión): Esta propiedad le dice a Visual Basic en qué formato están los datos.
  • DatabaseName (nombre de la base de datos): Esta propiedad identifica al archivo de Base de Datos.
  • RecordSource (fuente del registro): Esta propiedad le dice a Visual Basic de dónde obtener los datos. Por ejemplo el nombre de una tabla.

Los controles Image y PictureBox se pueden usar para mostrar imágenes almacenadas en la base de datos; un control Check Box muestra valores booleanos; y los controles Label y TextBox pueden utilizarse para presentar información numérica o de texto.

Las propiedad de enlace son DataSource (nommbre del control), y  DataField(campo) 

Instrucciones de programa para el data control

Refresh (refrescar): Se utiliza cuando la Base de Datos se abre para ser utilizada, o se cierra y se vuelve a abrir si ya esta abierta.

MoveFirst (mover al primero): Se utiliza para ir al primer registro de una tabla.

MoveNext (mover al siguiente): Se utiliza para ir al siguiente registro del conjunto de registros, desde el registro actual.

MovePrevious (mover al anterior): Se utiliza para ir al anterior registro del conjunto de registros, desde el registro actual.

MoveLast (mover al último): Se utiliza para ir al último registro de una tabla.

AddNew (añadir nuevo): Se añade un registro nuevo al conjunto de registros.

Update (actualizar): Se escriben todos los campos editados en la base de datos.

Delete (borrar): Se elimina el registro actual

 Buscar registros 

Se pueden usar los métodos FindFirst (buscar primero), FindNext (buscar el siguiente) y findLast (buscar el último) 

Por ejemplo, para recuperar todos los datos en el campo "apellido" que empiecen por la letra "B" en una base de datos:

Sub Recuperar_Apellidos()
        Criterio= " apellido = 'B'"
        Data1.Recorset.FindFirst Criterio
        Do While Not Data1.Recordset.NoMatch
        Debug.print Data1.Recordset.Fields("apellido").Value
        Data1.Recordset.FindNext Criterio
Loop

 
Ejemplo Práctico Usando el Data Control
Nota: gran parte de la información de estos ejemplos provienen de el sitio web de Jorge Andrés Sanchez www.jsanchez@yupimail.com. Lamentablemente perdí la dirección de su página en Geocities por lo que no puedo dar los credítos completos tal como quisiera

Usando Access o el Administrador Visual de datos creamos la siguiente base de datos:

NOMBRE DE LA BASE DE DATOS: Teléfonos.mdb

NOMBRE DE LA TABLA: Teléfonos

NOMBRE DEL CAMPO

TIPO DE DATOS

TAMAÑO

INDEXADO

Nombre Texto 50 Si (Con duplicados)
Dirección Texto 50 No
Teléfono Texto 50 No
Notas Texto 240 No

 Luego en una form creamos los botones Borrar, Buscar, Cancelar, Command1, Command2, Command3, Command4, Command5, Command6, Editar, Grabar, Nuevo y Refrescar 

Los códigos respectivos son:


Private Sub Borrar_Click()
        Dim R As Integer
        On Error GoTo fin
        R = MsgBox("¿Desea borrar el registro?", 4 + 64, "Atención")
         If R <> 6 Then Exit Sub
               Data1.Recordset.Delete
               Data1.Recordset.MoveNext
          If Data1.Recordset.EOF Then
                Data1.Recordset.MoveLast
          End If
fin:
          If Err.Number <> 0 Then
                 MsgBox Error, , "Se ha producido un error:"
                 Data1.UpdateControls
           End If
End Sub


Private Sub Buscar_Click()
         Dim Buscado As String
         Dim Criterio As String
         Buscado = InputBox("¿Que nombre desea buscar?")
         Criterio = "nombre like '*" & Buscado & "*'"
         Data1.Recordset.FindFirst Criterio
         If Data1.Recordset.NoMatch Then
                 cadena = "El registro " & UCase(Buscado) & " no existe"
                  MsgBox cadena, 64, "Resultado de la busqueda"
                  Data1.Recordset.MoveLast
           End If
End Sub


Private Sub Cancelar_Click()
           Data1.UpdateControls
           HabilitarBotones
           Grabar.Enabled = False
           InhabilitarCajas
End Sub


Private Sub Command1_Click()
             Data1.Recordset.MoveFirst
End Sub


Private Sub Command2_Click()
                Data1.Recordset.MovePrevious
                If Data1.Recordset.BOF Then
                         Data1.Recordset.MoveFirst
                 End If
End Sub


Private Sub Command3_Click()
                   Data1.Recordset.MoveNext
                    If Data1.Recordset.EOF Then
                               Data1.Recordset.MoveLast
                     End If
End Sub


Private Sub Command4_Click()
                    Data1.Recordset.MoveLast
End Sub


Private Sub Command5_Click()
                     LN = Chr$(13) + Chr$(10)
                     carreta = "DIRECTORIO TELEFONICO" + LN
                     carreta = carreta + LN
                     carreta = carreta & "Desarrollado por Jorge Andrés Sánchez" + LN
                     carreta = carreta & "Distribución libre" + LN
                     carreta = carreta & "Email: www.jsanchez@yupimail.com"
                     MsgBox carreta, 64, "Acerca de..."
End Sub


Private Sub Command6_Click()
                        End
End Sub


Private Sub editar_Click()
                          HabilitarCajas
                          InhabilitarBotones
                          Grabar.Enabled = True
                          Cancelar.Enabled = True
                           Data1.Recordset.Edit
                           Text1.SetFocus
End Sub


Private Sub Form_Load()
             Grabar.Enabled = False
             InhabilitarCajas
             ChDir App.Path
End Sub


Public Sub InhabilitarCajas()
              Dim N As Integer
              For N = 0 To Controls.Count - 1
                     If TypeOf Controls(N) Is TextBox Then
                          Controls(N).Enabled = False
                      End If
                Next N
End Sub


Public Sub HabilitarCajas()
           Dim N As Integer
           For N = 0 To Controls.Count - 1
                 If TypeOf Controls(N) Is TextBox Then
                     Controls(N).Enabled = True
                 End If
            Next N
End Sub


Public Sub InhabilitarBotones()
          Dim N As Integer
          For N = 0 To Controls.Count - 1
                 If TypeOf Controls(N) Is CommandButton Then
                     Controls(N).Enabled = False
                  End If
            Next N
End Sub


Public Sub HabilitarBotones()
        Dim N As Integer
        For N = 0 To Controls.Count - 1
        If TypeOf Controls(N) Is CommandButton Then
               Controls(N).Enabled = True
         End If
         Next N
End Sub


Private Sub Grabar_Click()
        Data1.Recordset.Update
        HabilitarBotones
        Grabar.Enabled = False
        InhabilitarCajas
End Sub


Private Sub Nuevo_Click()
        HabilitarCajas
        InhabilitarBotones
        Grabar.Enabled = True
        Cancelar.Enabled = True
        Data1.Recordset.AddNew
        Text1.SetFocus
End Sub


Private Sub Refrescar_Click()
        Data1.Refresh
        HabilitarBotones
        Grabar.Enabled = False
End Sub

Ejemplo Práctico Usando DAO e Instrucciones SQL

Nota: Esta  parte es copiada de la Revista digital chilena "Proyecto VB" que se puede encontrar en http://members.xoom.com/proyectovb o por email en proyectovb@hotmail.com 

El sitio y la revista me parecieron muy interesantes por su cantidad de código y contenido práctico

  1. INTRODUCCION
  2. DECLARAR VARIABLES OBJETO DE TIPO DATABASE Y RECORDSET
  3. ABRIR LA BASE Y LA TABLA
  4. LLENAR UN COMBOBOX CON LOS DATOS DE LA TABLA CLIENTES.
  5. TRUCOS PARA OBTENER EL CODIGO SIN HACER CONSULTA NI CONTROL OCULTO.
  6. BUSCAR REGISTROS CON EL METODO FINDFIRST
  7. INGRESAR/ACTUALIZAR DATOS
  8. ELIMINAR DATOS
  9. CERRAR TABLAS Y BASES DE DATOS
  10. COMENTARIO FINAL.

 INTRODUCCIÓN 


EL OBJETO DATABASE NOS PERMITE REALIZAR UNA CONEXION A UNA BASE DE DATOS Y REALIZAR TODO EL MANEJO REFERENTE A ELLA. .

EL OBJETO RECORDSET NOS PERMITE REALIZAR UNA CONEXION A UNA TABLA PARA REALIZAR ALGUNA ACCION SOBRE ELLA (LEER, ESCRIBIR, ELIMINAR, ACTUALIZAR, CONSULTAR. ETC).

PARA USARLO TIENES QUE SELECCIONAR EN EL MENU PROYECTO->REFERENCIAS Y MARCAR LA CASILLA DONDE DICE "MICROSOFT DAO 3.5 OBJECT LIBRARY." (O ALGO CON DAO)


DECLARAR VARIABLES OBJETO DE TIPO DATABASE


AHORA CREA UN NUEVO MÓDULO .BAS Y DIGITA

OPTION EXPLICIT

PUBLIC DB AS DATABASE 'VARIABLE GLOBAL DE TIPO DATABASE
PUBLIC RS AS RECORDSET 'VARIABLE GLOBAL DE TIPO RECORDSET


CREEN UNA BASE DE DATOS LLAMADA PRUEBA.MDB EN EL DIRECTORIO DONDE ESTA INSTALALADO VISUAL BASIC Y CREEN LA TABLA CLIENTES CON TRES CAMPOS. CODIGO, NOMBRE, DIRECCION. PUEDEN USAR EL VISDATA O EL ACCESS.

CAMPO VALOR  TIPO LARGO
CODIGO NUMERICO ENTERO
NOMBRE TEXTO STRING 50
DIRECCION TEXTO STRING 50

AHORA CREAREMOS UNA FUNCIÓN QUE ABRIRA NUESTRA BASE Y NUESTRA TABLA. (PUES SE ABREN UNA SOLA VEZ AL INICIO Y SE CIERRAN AL FINAL. DESPUES SOLO REFRESCAMOS EL RECORDSET.)


 ABRIR LA BASE Y LA TABLA 


'FUNCION QUE ABRE LA BASE DE DATOS UNA SOLA VEZ Y LAS TABLAS ASOCIADAS A ELLA.

PUBLIC FUNCTION ABREDB () AS BOOLEAN
    ON LOCAL ERROR GOTO ERRORABREDB
    DIM RET AS BOOLEAN
    RET=TRUE

    SET DB=WORKSPACES(0).OPENDATABASE(SLASH(APP.PATH) & "PRUEBA.MDB")
    SET RS=DB.OPENRECORDSET("CLIENTES",DBOPENDYNASET)

    'CON EL FLAG "DBOPENDYNASET" PUEDO GRABAR, ACTUALIZAR, ELIMINAR REGISTROS     DE LA TABLA

     GOTO SALIRABREDB

ERRORABREDB:
    RET=FALSE
    MSGBOX "ABREDB " & ERR & " " & ERROR$,VBCRITICAL
    RESUME SALIRABREDB

SALIRABREDB:
     ABREDB=RET
     ERR=0

END FUNCTION

PARA SABER EL VALOR DE UN CAMPO O ASIGNARLO A EL PODEMOS HACERLO DE VARIAS MANERAS.

EJEMPLO :

FORMAS DE SELECCIONAR REGISTROS EN UNA TABLA

SET RSCLIENTES=DB.OPENRECORDSET("CLIENTES",DBOPENDYNASET)
SET RSCLIENTES=DB.OPENRECORDSET("SELECT * FROM CLIENTES",DBOPENDYNASET)
SET RSCLIENTES=DB.OPENRECORDSET("SELECT CODIGO,NOMBRE, DIRECCION FROM CLIENTES",DBOPENDYNASET)


AHORA SUPONGAMOS QUE QUEREMOS SABER EL VALOR DEL CAMPO NOMBRE.

1.- ASOCIANDO DIRECTAMENTE EL VALOR DEL CAMPO
RSCLIENTES.FIELDS("NOMBRE").VALUE

2.- ACORTANDO LA RUTA CON EL OPERADOR !

RSCLIENTES!NOMBRE

3.- DIRECTAMENTE SEGUN LA POSICION DEL CAMPO EN LA TABLA
RSCLIENTES(1).


LLENAR UN COMBOBOX CON LOS DATOS DE LA TABLA CLIENTES.


'FUNCION QUE LLENA LOS NOMBRES DE LOS CLIENTES EN UN CONTROL
     PUBLIC FUNCTION LLENACLIENTES(CBO AS COMBOBOX) AS BOOLEAN
     ON LOCAL ERROR GOTO ERRORLLENACLIENTES

     DIM RET AS BOOLEAN
     DIM CONSQL AS STRING
     DIM E AS INTEGER

     SCREEN.MOUSEPOINTER=VBHOURGLASS

      CBO.CLEAR

      RSCLIENTES.MOVEFIRST
      DO WHILE NOT RSCLIENTES.EOF
             E=DOEVENTS()
             CBO.ADDITEM RSCLIENTES!NOMBRE
'ESTA ES LA PARTE INTERESANTE. MAS ADELANTE LA COMENTARE.
             CBO.ITEMDATA(CBO.NEWINDEX)=RSCLIENTES!CODIGO
             RSCLIENTES.MOVENEXT
        LOOP

        GOTO SALIRLLENACLIENTES

        ERRORLLENACLIENTES:
        RET=FALSE
        MSGBOX "LLENACLIENTES " & ERR & " " & ERROR$,VBCRITICAL
        RESUME SALIRLLENACLIENTES

SALIRLLENACLIENTES:
         LLENACLIENTES=RET
         ERR=0
         SCREEN.MOUSEPOINTER=VBDEFAULT

END FUNCTION


TRUCOS PARA OBTENER EL CODIGO SIN HACER CONSULTA NI CONTROL OCULTO


LA FUNCION ARRIBA SEÑALADA NOS PERMITIRA CARGAR EL NOMBRE EN EL COMBOBOX O LISTBOX AL SELECCIONAR UN ELEMENTO Y A LA VEZ TENDREMOS EL CODIGO DEL CLIENTE SEGUN EL INDICE ASOCIADO.

NOTA : LA MATRIZ ITEMDATA SOLO PERMITE ALMACENAR NUMEROS. UN CASO UTIL PUEDE SER ALMACENAR LOS RUTS PERO SIN EL DIGITO VERIFICADOR.

EJEMPLO :

DIM SRUT AS STRING

....
SRUT=RSCLIENTES!RUT
LISCLIENTES.ITEMDATA(LISCLIENTES.NEWINDEX)=LEFT$(SRUT,LEN(SRUT)-1)
....

AHORA LA MATRIZ ITEMDATA() NOS AHORRA EL TENER QUE IR A BUSCAR EL RUT ASOCIADO AL NOMBRE SELECCIONADO EN LA TABLA CLIENTES O TENER QUE CREAR OTRA LISTA OCULTA A LA CUAL LE ASOCIAMOS EL INDICE (LA CUAL OBVIAMENTE CONTIENE EL RUT).

EJEMPLO :

DIM SCODIGO AS STRING

SCODIGO=""
IF CBOCLIENTES.LISTINDEX<>0 THEN
       SCODIGO=CSTR(CBOCLIENTES.ITEMDATA(CBOCLIENTES.LISTINDEX))
       RSCLIENTES.FINDFIRST "CODIGO = " & SCODIGO
IF RSCLIENTES.NOMATCH THEN
....
END IF
END IF


BUSCAR REGISTROS CON EL METODO FINDFIRST


BUENO AHORA QUE YA SABEMOS ABRIR LA BASE Y LA TABLA Y LLENAR REGISTROS TAMBIEN PODEMOS CONSULTAR REGISTROS.
PARA ELLO USAREMOS EL METODO FINDFIRST DEL OBJETO RECORDSET. REGRESA TRUE (-1) CUANDO NO EXISTE DE LO CONTRARIO REGRESA FALSE (0)

USO : TABLA.FINDFIRST SCONSQL

EJEMPLO :

DIM CONSQL AS STRING
DIM SCODIGO AS STRING

SCODIGO=CBOCLIENTES.ITEMDATA(CBOCLIENTES.LISTINDEX)
CONSQL="CODIGO = " & SCODIGO

RSCLIENTES.FINDFIRST CONSQL

IF RSCLIENTES.NOMATCH THEN
'EL REGISTRO NO EXISTE
     TXTNOMBRE.TEXT=""
     TXTDIRECC.TEXT=""
ELSE
'EL REGISTRO EXISTE
       LBLNOMBRE.CAPTION=TRIM$(RSCLIENTES!NOMBRE)
       LBLDIRECC.CAPTION=TRIM$(RSCLIENTES!DIRECCION)
END IF

....

PODEMOS INCLUIR CONSULTAS QUE TENGAN OPERADORES BOOLEANOS COMO AND Y OR

EJEMPLO :

CONSQL="CODIGO = " & SCODIGO & " AND NOMBRE = '" & SNOMBRE & "'"
CONSQL="CODIGO = " & SCODIGO & " OR SUELDO < " & SSUELDO & " AND SUELDO>10000"


GRABAR/ACTUALIZAR DATOS 


'FUNCION QUE GRABA LOS DATOS DE UN CLIENTE
'
PUBLIC FUNCTION GRABA(BYVAL CODIGO AS STRING,BYVAL NOMBRE AS STRING,BYVAL DIREC AS STRING) AS BOOLEAN

      ON LOCAL ERROR GOTO ERRORGRABAR
      DIM RET AS BOOLEAN
      DIM CONSQL AS STRING
      RET=TRUE
      SCREEN.MOUSEPOINTER=VBHOURGLASS
      CONSQL="CODIGO = " & CODIGO
      RSCLIENTES.FINDFIRST CONSQL
      IF RSCLIENTES.NOMATCH THEN 'CLIENTE NO EXISTE
            CONSQL="INSERT INTO CLIENTES (CODIGO, NOMBRE, DIREC) VALUES ("
            CODIGO & ", '" & NOMBRE & "', '" & DIREC & "')"
       ELSE 'CLIENTE EXISTE
            CONSQL="UPDATE CLIENTES SET NOMBRE = '" & NOMBRE & "',"
            CONSQL=CONSQL & "DIREC = '" & DIREC & "'"
            CONSQL=CONSQL & " WHERE CODIGO = " & CODIGO
'DEBEN INCLUIR SIMPRE EL WHERE [CONDICION]
'O SI NO LES VA A ACTUALIZAR TODOS LOS CLIENTES CON ESE NOMBRE Y DIRECCION O SI           ENDIF
          DB.EXECUTE CONSQL,DBFAILONERROR
          RSCLIENTES.REQUERY 'AQUI REFRESCAMOS LA TABLA
          GOTO SALIRGRABAR
ERRORGRABAR:
          RET=FALSE
           MSGBOX "GRABA " & ERR & " " & ERROR$,VBCRITICAL
RESUME SALIRGRABAR

SALIRGRABAR:
            GRABA=RET
            ERR=0
            SCREEN.MOUSEPOINTER=VBDEFAULT
            END FUNCTION


ELIMINAR DATOS 


PUBLIC FUNCTION ELIMINA(BYVAL CODIGO AS STRING) AS BOOLEAN

      ON LOCAL ERROR GOTO ERRORELIMINA

       DIM RET AS BOOLEAN
       DIM CONSQL AS STRING
       SCREEN.MOUSEPOINTER=VBHOURGLASS
       RET=TRUE
       CONSQL="DELETE FROM CLIENTES WHERE CODIGO = " & CODIGO
       DB.EXECUTE CONSQL,DBFAILONERROR
       RSCLIENTES.REQUERY 'INVESTIGUEN POKE ESTO AGAIN.
       GOTO SALIRELIMINA
ERRORELIMINA:
       RET=FALSE
       MSGBOX "ELIMINA " & ERR & " " & ERROR$,VBCRITICAL

RESUME SALIRELIMINA

SALIRELIMINA:
         ELIMINA=RET
         ERR=0
         SCREEN.MOUSEPOINTER=VBDEFAULT

END FUNCTION


 CERRAR TABLAS Y BASES DE DATOS


RECUERDEN. "CERRAR SIEMPRE LAS TABLAS Y LA BASE DE DATOS AL FINALIZAR EL SISTEMA."

'CON ESTA PEQUEÑA MARAVILLA DE CODIGO PODEMOS CERRAR CUANTA TABLA HAYAMOS ABIERTO PREVIAMENTE
'Y DEVOLVERLE A WINDOZE LOS RECURSOS TOMADOS.
'
PUBLIC SUB CIERRA()

      ON LOCAL ERROR RESUME NEXT

      DIM RS AS RECORDSET

       'AQUI CERRAMOS Y LIBERAMOS RECURSOS DE TODAS LAS TABLAS ASOCIADAS A LA        'BASE DB.
        'AQUI TODOS LAS TABLAS ESTAN EN LA COLECCION RECORDSETS DEL OBJETO               DATABASE.
        WITH DB
             FOR EACH RS IN .RECORDSETS
                    SET RS = NOTHING
              NEXT
         END WITH
         DB.CLOSE
         SET DB=NOTHING
         ERR=0
         END SUB

********************************* COMENTARIO FINAL ************************************
ESPERO QUE ESTE CURSO LES SIRVA PARA SUS SISTEMAS Y PUEDAN APORTAR MAS A LA PRODUCCION DE SU DEPARTAMENTO.

IDEAS, SUGERENCIAS, "CRITICAS CONSTRUCTIVAS" HACERLAS LLEGAR A CHICOVB@HOTMAIL.COM.
HASTA LA PROXIMA.

1