Acerca de:

Este blog contiene los códigos, ejemplos y bases de datos que he usado cuando aprendía acerca de algún tema específico. En lugar de borrarlos (una vez dominado ya el tema), he decidido publicarlos :)
Mostrando entradas con la etiqueta VB6 ADO. Mostrar todas las entradas
Mostrando entradas con la etiqueta VB6 ADO. Mostrar todas las entradas

martes, 29 de noviembre de 2022

Accediendo a una base de datos en Visual Basic 6

Este tutorial es viejo viejo, de cuando recién aprendía a hacer conexiones a bases de datos con el VB6 en aquellos fines de los 90s/inicios de los 2000s. Lo dejo aquí como parte de la historia de la informatica.

La coneja no tiene nada que ver con el tutorial, pero creo que es un bonito separador :)

Empezamos:


Sucede que quienes programamos, en algún momento, tenemos que toparnos con las bases de datos queramos o no. Lo malo es que éstas son un mundo muy diferente a los punteros, al manejo de puertos, a la captura de señales o a las llamadas a Apis. Lo bueno es que una vez que se domina lo básico son lo más fácil del mundo. Y lo mejor es que los lenguajes de programación de alto nivel ya vienen con herramientas para manejas bases de datos de todo tipo.

Y eso es lo que quiero mostrar aquí: lo básico para manejar una pequeña base de datos en Access 2002 en el lenguaje de programación más fácil de entender de la Historia: Visual Basic 6.

Antes que nada hay que cargar el componente ADO (Click derecho en la barra de herramientas -> Componentes -> Microsoft ADO Data Control 6.0) y ponerlo en el formulario. Luego se guarda y se borra el componente ADO y, si se quiere, la referencia en la barra de herramientas. Esto es para que el Visual Basic reconozca los objetos Recordset y Connection.

El programa (con su código fuente) se pueden bajar de aquí. 

 


Y ahora una explicación:

Los datos a agregar o eliminar se ingresan en Text1 y Text2. La navegación se hace con los botones Anterior y Siguiente (Command3 y Command4). El botón Limpiar (Command2) es sólo para borrar lo escrito en Text1 y Text2. En el control List1 aparecen todos los registros que coinciden con la búsqueda del texto en Text3 realizada al oprimir "Buscar Todos" (Command7), en cambio el botón "Buscar" (Command5) sólo hace aparecer el primer resultado encontrado.

Para manejar la base de datos no es necesario añadir ningún control especial, basta declarar dos objetos:

Dim rs As Recordset
Dim db As Connection


"db" realiza la conexión con la base de datos y "rs" permitirá recorrer los resgistros, borrarlos, añadirlos y buscarlos. Para conextar con la base de datos (llamada "bd1.mdb") bastan estas líneas:

db.Open "PROVIDER=Microsoft.Jet.OLEDB.4.0;Data Source=" & pathBD & ";"
rs.Open "select * from Datos", db, adOpenDynamic, adLockOptimistic


bd1.mdb sólo tiene dos tablas: "Datos" y "datos2". El programa sólo trabaja con "Datos". El programador debe saber qué tablas y qué campos tiene su base de datos para poder trabajar con ella.

Es importante saber también en qué programa y en qué versión está hecha la base de datos, sino no podrá conectar. Para versiones anteriores de Access se pone:

db.Open "PROVIDER=Microsoft.Jet.OLEDB.3.51;Data Source=" & pathBD & ";"

Aquí hay que observar el primer parámetro que se le pasa a rs.Open: "select * from Datos" esto significa que rs dee recibir todos los registros de la tabla Datos de la base de datos.

"select" es un comando de los Procedimientos Almacenados de las bases de datos. Los Procedimientos Almacenados son órdenes que éstas interpretan y ejecutan y se basan en la lógica booleana. Así, si yo quisiera todos los registros cuyos Nombres coinciden con lo que se ingresa en Text3 (una búsqueda) basta poner:

rs.Close
rs.Open "select * from Datos where Nombre = '" & Text3.Text & "'", db, adOpenDynamic, adLockOptimistic


Para buscar todos los registros que coincidan con Text1 ó con Text2 (Nombre o Apellido) se pondría:

rs.Open "select * from Datos where Nombre = '" & Text1.Text & "' or Apellido = '" & Text2.Text & "'", db, adOpenDynamic, adLockOptimistic


(hay que tener cuidado de que el contenido de Text1 y Text2, o las condiciones de búsqueda si son de tipo String, estén entre comillas simples).

Como rs se vuelve a abrir, es necesario cerrarlo previamente.

Otro comando es "delete", cuando se lo usa el objeto rs se cierra después de la ejecución porllo que ya no es necesario poner rs.Close. Pero para que se pueda seguir navegando entre todos los registros de la tabla Datos se debe vovler a abrir.

rs.Open "delete * from Datos where Nombre = '" & Text1.Text & "' and Apellido = '" & Text2.Text & "'", db, adOpenDynamic, adLockOptimistic


Esta orden borra los registros cuyo Nombre y Apellido coincidan con los textos en Text1 y Text2.

Los procedimientos almacenados permiten realizar muchas cosas en la base de datos. Por ejemplo, imaginando que se tiene una tabla "Empleados" con campos "Edad" que contiene data numérica, "ID" y "Profesion". Luego de hacer las conexiones respectivas, para obtener a todos los ID y las profesiones correspondientes a mayores de edad se puede poner:

select ID, Profesion from Empleados where Edad > 18

Para agregar un registro basta una función propia de rs: AddNew y luego especificar los campos con la función "Fields" a donde se añadirán los nuevos datos. Al recorrer los registros de atrás o hacia adelante (propiedades MoveNext, MovePrevious, MoveLast y MoveFirst), o al hacer una búsqueda, "Fields" se coloca automáticamente en el registro actual.

Las propiedades de rs: EOF y BOF indican si se está al final o al principio de la tabla Datos. Mientras no se lo esté, sus valores son False. Tanto las búsquedas como los recorridos y los borrados de los registros en las tablas de la base de datos deben hacerse mientras EOF y BOF son False, si no nos "saldremos" de la base de datos y nos botará error.

Si en una búsqueda no se encuentra nada, EOF y BOF serán True.

 

jueves, 11 de octubre de 2012

VB6: Error al conectar un recordset a un archivo xls modificado con Open Office Calc

Me encontré con este error mientras experimentaba un poco con Open Office: en Visual Basic 6 conectaba un recordset a una hoja en un archivo xls, luego listaba las celdas de la primera fila en un control List (el recordset considera el archivo xls como una base de datos, sus hojas como tablas, y las celdas de la primera fila como los campos si la propiedad de la cadena de conexión HDR es "Yes".).
Todo salía bien mientras el archivo xls fuera modificado con MS Excel, pero si lo modificaba con Open Office Calc, la aplicación me tiraba un error de "La tabla externa no tiene el formato esperado".

El proyecto, junto a los archivos xls de prueba, pueden descargarse de aquí.

Mi teoría es que Open Office realiza algún cambio a las hojas de los archivos xls que no realiza MS Excel. Los archivos resultantes pueden abrirse tanto con MS Excel y Calc, y parecer idénticos, pero el cambio introducido al guardar con Calc hace que un recordset de VB6 ya no pueda reconocer las hojas como tablas válidas.

Por ejemplo, tengo este archivo creado con MS Excel:

 
Al guardarlo con Open Office Calc queda como:


¿Eliminará Open Office información que no usan ni MS Excel ni Calc, pero sí un recordset?
Dado el cambio en el tamaño del archivo, es probable.

miércoles, 20 de junio de 2012

VB6 error: no se pudo encontrar el archivo ISAM instalable

Me conectaba a una base de datos en Access 2003 usando un objeto ADODB.Connection llamado miDB, y un Recordset llamado miRS. Luego cerraba ambas conexiones haciendo:

If miDB.State <> adStateClosed Then miDB.Close
If miRS.State <> adStateClosed Then miRS.Close

miDB.Open miConnectionStringaDSN
miRS.Open "select * from Tabla", miDB

La propiedad State de ambas variables me dice si están cerradas o abiertas.

En resumen, una vez cerrada la conexión a la base de datos en Access, conectaba miDB a otra base de datos, a través de un DSN (ya creado mediante ODBC usando el aplicativo odbcad32.exe del Windows) y usando el formato de cadena de conexión para System DSN de la web de ConnectionStrings.

Y entonces me saltaba este error:


El mensaje es: "Error 80004005 en tiempo de ejecución: no se pudo encontrar el archivo ISAM instalable."

Parecía que me faltaba instalar algo, pero cuando realizaba primero la conexión por DSN (sin conectar miDB ó miRS a ninguna base de datos con anterioridad) la aplicación funcionaba de maravilla.

Deduje que no era problemas de instalación. Cuando conectaba miDB y miRS a una base de datos, cerraba ambos objetos e intentaba abrir una nueva conexión a DSN (¡y esto sólo ocurría al conectar por DSN!), "algo" se quedaba bloqueando un archivo, en algún lugar, y como la nueva conexión por DSN no podía abrirlo, consideraba que el archivo no existía y me lanzaba el error.

Entonces cambié mi código a:

If miDB.State <> adStateClosed Then miDB.Close
If miRS.State <> adStateClosed Then miRS.Close

Set miDB = Nothing
Set miRS = Nothing
Set miDB = New Connection
Set miRS = New Recordset

miDB.Open miConnectionStringaDSN
miRS.Open "select * from Tabla", miDB

Y resolví el problema, ya no aparece el error de ISAM instalable, y pude conectarme a mi DSN sin más inconvenientes, pues al anular ambos objetos (miDB y miRS) destruyo cualquier enlace que quede flotando, en algún lugar, perdido en las entrañas de mi Windows.

miércoles, 14 de marzo de 2012

VB6: Error '-2147217887 (80040e21)' en tiempo de ejecución

El mensaje completo de mi error es:
"Error '-2147217887 (80040e21)' en tiempo de ejecución. Error en el método 'DataSource' del objeto 'IMSHFlexGrid'"


El error saltaba en esta línea de código:
miHierarchicalFlexGrid.DataSource = unRecordset

Lo curioso es que el recordset mostraba sus datos correctamente en varias cajas de texto. Algo debía haber en el recordset que no era compatible con el Hierarchical FlexGrid al que quería enlazarlo.

La base de datos que estoy utilizando es AdventureWorks para SQL Server 2005. Ésta es una base de datos de ejemplo y se puede descargar de Internet. La tabla a la que se conecta el recordset es "Persons.Contact":


Y mirando la tabla Persons.Contact noté que uno de los campos tiene un tipo de datos que no había visto antes: XML. Decidí cambiarlo a VarChar y correr nuevamente el programa.
Ya no sale el error y el recordset sí se puede mostrar en el Hierarchical FlexGrid.

Conclusión: El Hierarchical FlexGrid de Visual Basic 6 no es compatible con tipos de datos XML.

lunes, 24 de octubre de 2011

Error al actualizar un Recordset: "El Campo no es Actualizable"

Intentaba modificar un registro de una base de datos en Access desde VB6, cuando, al hacer miRecordset.Update, me saltaba el siguiente error:


En general, el mensaje de error es: "No se puede actualizar 'nombre del campo'; el campo no es actualizable".

Esto se debe a que ese campo específico tiene alguna característica que hace que no se puede reescribir. En mi base d edatos, el campo "Id" es llave primaria y es auto incrementable. 

Aunque los permisos de la base de datos permitan hacer modificaciones a sus datos, un campo del recordset no permite cambiar su contenido si es auto incrementable. Antes de actualizar mi recordset, necesitaba saber qué campos son autoincrementables para no tocarlos cuando quiera modificar su contenido.

Después de mucho buscar por internet, di con este foro:
http://www.experts-exchange.com/Programming/Languages/Visual_Basic/Q_20248270.html

Son rescatables estos posts:

Expert Comment

Posted by rspahitz on 21/12/01 at 8:20 AM
I don't know the answer, but since (I think) Properties is a collection, you should be able to cycle through and find the valid choices:

for i = 0 to rs.Fields(x).Properties.count - 1
   debug.print rs.Fields(x).Properties(i).name
next i



Author Comment

Posted by andyknott on 21/12/01 at 8:28 AM
Hi rspahitz,

Thanks that helped a bit, the properties listed are

BASECATALOGNAME
BASECOLUMNNAME
BASESCHEMANAME
BASETABLENAME
COLLATINGSEQUENCE
COMPUTEMODE
DATETIMEPRECISION
ISAUTOINCREMENT
ISCASESENSITIVE
ISSEARCHABLE
OCTETLENGTH
KEYCOLUMN
COMPFLAGS
SORTID
BASETABLEINSTANCE
TDSCOLLATION



Para saber, por ejemplo, si un campo con índice x es auto incrementable, se hace:
If miRecordset(x).Properties("IsAutoIncrement").Value Then _
Debug.Print "Es Auto Incrementable"

En mi caso, al modificar mi recordset, hago:
For i = 0 To miRecordset.Fields.count - 1
    If Not miRecordset(i).Properties("IsAutoIncrement").Value Then
        miRecordset(i) = nuevoValor
    End If
Next

miRecordset.Update

Con esto elimino el error de Campo no Actualizable.

viernes, 11 de marzo de 2011

Obtener los nombres de las tablas y los nombres de sus esquemas de una BD en SQLServer desde VB6

Un día intentaba conectarme a AdventureWorks sample BD desde una app en Visual Basic 6.

Declaraba un recordset como sigue:

db.Open miConnectionString 'db es un objeto tipo Connection
Set rs = db.OpenSchema(adSchemaTables, Array(Empty, Empty, Empty, "TABLE"))
para poder listar todas sus tablas, usando un bucle.
Mi problema era que al ejecutar un query en SQL:

Dim s as String

Do Until rs.EOF
s="Select * from " & rs!Table_Name
' Hacer muchas muchas cosas con la variable "s"
rs.MoveNext
Loop

Me botaba error, ya que sólo tenía el nombre de la tabla. Para que funcionara, el query dentro de la variable "s" necessitaba también el nombre del esquema (Schema Name) y contruirlo todo de la siguiente forma:
Select * from Schema_Name.Tabla

Luego de mucho rebuscar en Google, hallé esta web
http://www.techrepublic.com/article/much-ado-about-field-properties/1045310

Donde explican lo que hace OpenSchema. Para mi caso, utilizo el SchemaID "AdSchemaTables", el cual incluye un filtro llamado "TABLE_SCHEMA".

Finalmente mi query queda:
Dim s as String

Do Until rs.EOF
s="Select * from " & rs!Table_Schema & "." & rs!Table_Name
' Hacer muchas muchas cosas con la variable "s" 
rs.MoveNext
Loop

Update!
Cómo saber si una tabla tiene un nombre de esquema asignado:
Basta añadir este código en el bucle ("miTabla" es una variable tipo String):

miTabla = vbNullString
miTabla = rs!TABLE_SCHEMA
    
If miTabla <> vbNullString Then
      miTabla = rs!TABLE_SCHEMA & "." & rs!TABLE_NAME
Else
      miTabla = rs!TABLE_NAME
End If