Visual Basic Guía del estudiante Cap. 12 Objetos DAO ACCESO A BASES DE DATOS SIN UTILIZAR EL CONTROL DATA En el capítulo anterior hemos visto los controles capaces de acceder a un Base de Datos, enlazados mediante un control Data. Se comenzó a exponer que no es necesario usar un control Data para acceder a leer datos, añadir registros o cambiar su contenido. Y es más. Comenzaremos ahora a ver que el control Data pese a que puede evitarnos gran cantidad de líneas de código, nos hace perder el control respecto al programa. Es normal. El control Data se ha desarrollado para realizar un trabajo muy estándar. Si nuestra aplicación se separa un poco de lo normal, lo mas probable es que necesitemos realizar las operaciones mediante código. Esto no quiere decir que el Data haya que dejarlo en desuso. Será necesario en aquellas aplicaciones en las que se va a usar un control DBGrid, pues como se recordará, mediante el uso de un control Data metemos en memoria RAM todo el contenido de la base de datos relativo al Recordset que hemos creado. El control Data será necesario en aquellas aplicaciones donde utilicemos un DBGrid, un DBList o un DBCombo. Veremos mas adelante que también será necesario cuando queramos guardar imágenes en una Base de Datos. El control Data también permite consultas más rápidas a la BD. El hecho de guardar el contenido completo del Recordset en la memoria hace que cualquier consulta sea más rápida. Eso sí, estamos empleando mucha más memoria RAM. Pero en este capítulo vamos a ver como se pueden manejar bases de datos utilizando otros objetos de acceso a datos. Concretamente los objetos DAO.- (Data Access Objet). Los objetos DAO utilizan el Motor de Bases de Datos Jet de Microsoft y trabajan directamente sobre el fichero que contiene la base de datos. Existen otros objetos de acceso a datos, como ha podido ver en el capítulo anterior, que no trabajan directamente sobre el fichero, sino sobre una conexión ODBC que enlaza con la base de datos. Son los objetos RDO y ADO, cuyo estudio se realizará en capítulos posteriores. Estos últimos tipos son mas modernos, pero no tienen algunas prestaciones que tienen los DAO, debido precisamente a que no trabajan directamente sobre el fichero. Centrémonos sobre los objetos DAO Estos objetos, pese a que no tienen representación en la interface gráfica, son objetos Visual Basic como los demás, y nos podremos referir a ellos por su nombre como hacíamos con todos los controles. Eso sí, debemos declararlos como se declaran las variables, y siguen siendo válidos los criterios de declaración de variables en cuanto al ámbito de aplicación. Si declaramos un DAO en un procedimiento, no nos podremos referir a él fuera de ese procedimiento. Si queremos que sea válido en toda la aplicación deberemos declararlo en la sección de declaraciones de un módulo, o en la sección de declaraciones de un formulario si fuese suficiente ese ámbito para nuestra aplicación. La primera sorpresa suele ocurrir a la hora de declarar un objeto. Por ejemplo, para declarar un objeto tipo DataBase debemos hacerlo con la siguiente declaración: Dim MiBaseDatos As DataBase Y al ejecutar el programa puede ocurrirle el siguiente error: Error de compilación. No se ha definido el tipo definido por el usuario. Lo que le está ocurriendo es que su programa no conoce el tipo de variable DataBase. Para que la conozca, debe agregarle una referencia. Vaya a Proyecto | Referencias … de la barra de menú y seleccione Microsoft DAO 3.51 Objet Library Haga click en Aceptar y ya tiene agregada esa referencia a su programa. Agregar una referencia significa que le ha dicho a su programa que puede hacer uso de una colección de DLLs donde podrá encontrar la definición del objeto o la variable que no encuentra. Ahora ya no le dará el error anterior, pues en esa LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 1
DLL que acaba de agregarle a su programa está la explicación al secreto de lo que es un objeto DataBase. Verá que hay mas referencias parecidas a Microsoft DAO 3.51. (Las versiones 2.0, 2.1, 2.5/3.5, y si ha instalado Access2000 tendrá la 3.6) Existen tantas versiones distintas como versiones de Access. En realidad esta DLL no es mas que un componente de Access que podemos usar, al igual que lo hace Access, para gestionar una base de datos. Debe elegir la versión mas alta, pero con cuidado. La versión 3.5 corresponde a la versión de Access 97. Cuando cree con su programa una base de datos con esta versión, no podrá abrirla con Access 2.0 No se olvide de la teoría de la compatibilidad de Microsoft, que dice que cualquier versión que Microsoft considere obsoleta no debe reconocer los datos guardados por versiones más modernas del mismo programa. Piense si es posible que alguien que vaya a abrir una base de datos guardada con su aplicación dispone de una versión anterior de Access. Por ejemplo, cuando se está escribiendo este libro, está recién aparecido Access 2000. ¿Cree que es oportuno en estos momentos, en los que todavía se está introduciendo en muchos usuarios Access 97 usar una DLL que nos cree bases de datos que solamente puedan ser abiertas por Access 2000?. La versión Microsoft DAO 3.6 corresponde a Access 2000. Si ha instalado este programa le aparecerá esa referencia en la lista de referencias. No se sorprenda si antes de instalar Access 2000 no tenía esa referencia y después de la instalación sí. Si crea una BD con la versión 3.6 no podrá abrirla con Access 97 ¡Esta es la compatibilidad hacia delante de Microsoft!
Objetos DAO de acceso a datos Son muchos y tienen estructura jerárquica. Es importante resaltar lo de su estructura jerárquica, ya que como verá, un objeto DAO crea los objetos DAO inmediatamente inferiores en jerarquía. Hay que señalar que las colecciones de estos objetos DAO son a su vez objetos de acceso a datos. Esto hay que explicarlo un poco mejor. Por ejemplo, un objeto Database es un objeto DAO que representa una base de datos abierta. Al objeto que agrupa a todas las bases de datos abiertas en ese momento le llamamos Objeto Databases. Si tenemos dos bases de datos abiertas en un determinado momento, el objeto Databases contendrá dos elementos. Todos los objetos DAO excepto el dbEngine tienen colecciones. Es lógico. El dbEngine, como verá mas adelante, es precisamente el Motor de Bases de Datos Jet y solamente existe uno. Comenzaremos precisamente por él la explicación de los objetos DAO. El dbEngine es el motor Jet. Y como vimos en el capítulo anterior, la versión 3.5 puede trabajar directamente sobre el fichero de la base de datos o a través de una conexión ODBC. En el primer caso decimos que está trabajando en el espacio de trabajo Microsoft Jet Si le hacemos trabajar a través de ODBC decimos que estamos trabajando en el espacio de trabajo ODBCDirect Los objetos que emplea en cada uno de los espacios de trabajo pueden verse en las figuras 20.1 y 20.2. Puede observarse que tiene muchos mas objetos en el espacio de trabajo Microsoft Jet que en el de ODBCDirect. Es lógico. Con el primero nos permite CREAR bases de datos, y por lo tanto necesita tener objetos tales como el Tabledef, Index o Relation. Lo verá un poco mas adelante. El objeto dbEngine tiene los siguientes métodos, propiedades y colecciones Métodos CreateWorkspace, CompactDatabase, RepairDatabase, Idle, RegisterDatabase Propiedades DefaultPassword, DefaultUser, IniPath, LoginTimeout, Version Colecciones Errors, Properties, Workspaces Veremos estos métodos y propiedades según vayamos avanzando en el capítulo
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 2
Fig. 20.1 Objetos DAO para el espacio de trabajo Microsoft Jet
En esta figura pueden verse los objetos y sus colecciones. Las colecciones llevan el nombre en plural. Parece un poco complicado, y posiblemente lo será. Lo cierto es que para el trabajo que se hace normalmente con bases de datos este diagrama queda bastante reducido. No se extrañe tampoco de ver que objetos como el Fields y el Field existen en varios niveles. Lo verá mucho mejor mas adelante.
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 3
Fig. 20.2 Objetos DAO para el espacio de trabajo ODBCDirect
Este modelo parece un poco mas asequible. La razón es que no hace todo lo que hace el espacio de trabajo Microsoft Jet.
Objeto Workspace Un objeto Workspace define una sesión de trabajo para un usuario específico. Una sesión de trabajo es precisamente eso, una sesión de trabajo en el más puro estilo informático. Pueden existir varias sesiones de trabajo, pero en la mayoría de los casos eso no es lo normal. Será necesario crear varias sesiones cuando necesitemos imponer restricciones de acceso a una base de datos, cuando tengamos que usar Transacciones (*) en un sistema multiusuario, y en algún caso más. Las sesiones de trabajo tienen dueño (Usuario) y una palabra clave para acceder a ellas. Pero lo normal es tener solamente una sesión abierta. Y visual Basic nos facilita este caso abriendo una sesión de trabajo automáticamente. Cuando se inicia Visual Basic, se crea un Workspace con palabra clave y nombre de usuario Admin. Este Workspace es precisamente el que ocupa el número cero de la colección de Workspaces. Es decir, es el Workspaces(0). No se preocupe de que ahora mismo no lo entienda. Ya lo entenderá. Pero hay que exponerlo ahora. El objeto Workspace contiene: Para el espacio de trabajo Microsoft Jet: Grupos de trabajo, que es un grupo de usuarios de un entorno multiusuario que comparten datos y el mismo sistema de base de datos. Grupos de usuarios, que es una colección de cuentas de usuario. Estos objetos, que no son objetos de acceso a datos, sino de explotación de los recursos del sistema, están enfocados a la seguridad respecto al acceso a las bases de datos que se trabajan conjuntamente. No tendría sentido hablar aquí de ellos si no fuese porque encontrará referencias a estos objetos continuamente en la ayuda de Visual Basic. Bases de Datos (Databases). Este el objeto que ahora nos interesa del objeto Workspace. Un objeto Database es una base de datos abierta. Para el espacio de trabajo ODBCDirect:
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 4
Conexión, que representa una conexión con una base de datos a través de ODBC Bases de Datos (Databases). Ya podemos comenzar a comprender una diferencia entre ambos espacios de trabajo. En el Microsoft Jet tenemos Usuarios y Grupos de Usuarios. En el espacio ODBCDirect no los tenemos, ya que el permiso o denegación de acceso de uno u otro usuario a una base de datos se establece cuando se crea (en Windows) la conexión ODBC. En el espacio ODBCDirect tenemos un objeto llamado Connection, objeto que no tenemos en el Microsoft Jet ya que con este sistema accedemos directamente al fichero de la base de datos. No necesitamos ninguna conexión establecida previamente. Al objeto Workspace se le puede dar un nombre definido por el usuario. Ese nombre habrá que declararlo como nombre de una variable objeto Workspace : Dim Misesion as Workspace La declaración de la variable Objeto tiene las mismas características que cualquier variable en cuanto al ámbito en el que se puede usar. Para que pueda usarse en toda la aplicación deberemos declararla en un Módulo con la sentencia Public Public Misesion as Workspace Esta advertencia es válida para la declaración de todos los objetos DAO. Una vez declarado el nombre del objeto Workspace, hay que crearlo. En realidad, y tal como citábamos mas atrás, cada vez que se inicia una sesión de Visual Basic, se crea automáticamente un Workspace. El número 0 A este Workspace no podemos ponerle ningún tipo de palabra de acceso, ya que se la ha puesto VB : Admin. Utilicemos este Workspace, el número 0 de la colección Workspaces, para comenzar a trabajar. Tiempo tendremos mas adelante de ver como se crea un Workspace. Para hacer que Misesion sea ese Workspace que crea automáticamente VB basta con ejecutar la siguiente línea de código Set Misesion = Workspaces(0) Pero si no queremos aprovechar este Workspace creado automáticamente por Visual Basic, y queremos usar otro, usemos el método CreateWorkspace. Se verá al final del capítulo.
Creación de Objetos DAO Para crear un objeto DAO (Cualquiera que sea) debemos usar una forma que se va a repetir a lo largo de toda su vida profesional, mientras trabaje con Visual Basic y Bases de Datos: Set ObjetoDAOInferior = ObjetoDAOSuperior.Método ( Aquí .... alguna cosa ) Logicamente el término Aquí .... alguna cosa va a depender de cada método y de lo que Vd. quiera hacer, pero la estructura Set DAOInferior = DAOSuperior.Método ( - - - - - - - - - - ) se mantendrá en todas las operaciones de creación y manipulación de objetos DAO. Esta sintaxis es tan simple que un profesor de Visual Basic decía que es como un “juego de niños” No lo olvide y se le quitará el miedo al manejo de bases de datos mediante código. Posiblemente hasta ahora le haya parecido muy difícil y haya optado por usar el control Data para todas sus aplicaciones. Si recuerda este Juego de niños verá que es más sencillo crear objetos DAO que poner un control Data en un formulario. Comenzaremos a explicar la operación de bases de datos mediante DAO explicando como se crean bases de datos Access mediante Visual Basic. ¿Para que se van a crear bases de datos mediante un programa si puedo hacerlo directamente con Access? . Esta pregunta me la han hecho los alumnos durante todos mis años de docencia. La única respuesta para ello es que deseo que Vd. sea programador, no usuario de ofimática.
Objeto Database
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 5
Un objeto Database representa una base de datos abierta. Una colección Databases contiene todos los objetos Database abiertos en un objeto Workspace del motor de bases de datos Microsoft Jet. Un objeto Database puede crearse, bien porque hemos creado una base de datos mediante el procedimiento CreateDataBase, o porque hemos abierto una base de datos existente mediante el procedimiento OpenDatabase. En cualquiera de los dos casos, el objeto Database existe hasta que lo cerremos (mediante el método Close) o hasta que cerremos la aplicación. Al objeto Database se le debe dar un nombre definido por el usuario. Eso sí, hay que declararlo como una variable objeto Database Dim MiDataBase As Database El nombre que le demos al objeto DataBase no tiene nada que ver con el nombre del fichero que alberga esa base de datos. El objeto DataBase es la base de datos que creamos en la memoria RAM del ordenador. Colección Databases Es el conjunto de Objetos Database existentes. La colección Databases pertenece al Workspace.
Crear una Base de Datos ACCESS. Método CreateDatabase Para crear una base de datos deberemos utilizar el método CreateDatabase. Previamente debemos declarar el nombre que queremos dar al objeto DataBase que se va a crear como un objeto Database : Dim MiBaseDatos as Database Si tenemos declarado un Workspace llamado Misesion mediante la declaración : Dim Misesion as Workspace Y hacemos que Misesion sea el Workspace creado automáticamente por VB Set Misesion = Workspaces (0) podremos usar el método del Workspace CreateDatabase para crear ese objeto Database. Recuerde que el objeto Database NO es el fichero que va a contener la base de datos sino que es una estructura de base de datos que está de momento en la memoria RAM del ordenador. El fichero se creará posteriormente cuando cerremos el objeto Database. El nombre del objeto Database tampoco tiene porque coincidir con el nombre del fichero que se va a crear. En el ejemplo que veremos mas adelante, el nombre del objeto Database es MiBaseDatos y el nombre del fichero es MiBase.Mdb. La sintaxis del método CreateDataBase es el “juego de niños” citado antes: Set ObjetoDAOInferior = ObjetoDAOSuperior.Método ( Parámetros ) En nuestro caso el Objeto DAO superior es el Workspace Misesion, y los parámetros que hay que pasar en este caso son:
Nombre (y Path) del fichero de la base de datos Idioma, para permitir la ordenación alfabética de los datos. Para los idiomas español, inglés y francés debe usar dbLangGeneral.
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 6
Opciones, que le permite elegir la versión de la base de datos a crear (Equivalente a la versión de Access) y si deseamos crear una base de datos cifrada. Si no pone nada en este parámetro le crea una base de datos sin cifrar, y de la última versión que le permite la referencia elegida para el motor de bases de datos (Microsoft DAO 3,51, por ejemplo) Vayamos al ejemplo: Set MiBaseDatos = Misesion.CreateDatabase (“C:\MiCarpeta\MiBase.MDB”, dbLangGeneral) Si ahora cierra el programa le creará la Base en el disco. Ejecute Access y abra la BD C:\MiCarpeta\MiBase.MDB. ¡Ya Existe! Pero observará que está completamente vacía. Es normal. Una Base de Datos ACCESS tiene tablas. Y de momento no hemos escrito ningún código para crear esas tablas. Vamos a seguir creando esta base de datos, al tiempo que explicamos el resto de los procedimientos que hay que usar para ello. El Método CreateDataBase Crea un nuevo objeto Database, guarda la base de datos en disco y devuelve un objeto Database abierto. Vaya a la ayuda de VB. Verá que la sintaxis de este método es: Set MiBaseDatos = Misesion.CreateDatabase (nombre_base, escenario, opciones) MiBaseDatos es el nombre del objeto Database por el cual nos referiremos a esa base de datos, NO el nombre del archivo con el que quedará guardada en el disco. Misesion es el nombre del objeto Workspace existente que contendrá la base de datos. Si se omite este argumento, se utilizará el objeto Workspace predeterminado - Workspaces(0) – e incluso, si no se pone nada, usará ese Workspace predeterminado. nombre_base es el nombre del archivo de base de datos que se va a crear. Es decir, el nombre del archivo en el disco. Puede ser una ruta completa y un nombre de archivo, como por ejemplo "C:\MiCarpeta\MiBase.MDB". Si no se indica una extensión, se agregará .MDB. Si la red lo admite, también puede especificar una ruta de red, como por ejemplo "\\MISERVID\MICOMP\MIDIR\MIBD". Con este método sólo pueden crearse archivos de base de datos .MDB. (ACCESS) escenario es una expresión de cadena utilizada para especificar dos cosas: el orden alfabético que se va a usar en esta base de datos, (Obligatorio) que denominaremos inf_local y el Password o palabra clave que quiere usar para restringir su uso. Este Password es opcional. Debe especificar el argumento inf_local o se producirá un error. Consulte la tabla de constantes para inf_local incluida más adelante en este tema. En el argumento Opciones puede combinar varias opciones, según se especifica a mas adelante. Puede combinar varias opciones sumando las constantes correspondientes.
Valores de los parámetros En el argumento inf_local se suministra información de la lengua empleada para especificar la propiedad CollatingOrder del texto para las comparaciones entre cadenas. Es un argumento obligatorio. Para los idiomas Inglés, alemán, francés, portugués, italiano y español moderno se usa la siguiente constante : dbLangGeneral No use dbLangSpanish. pues no hace nada especial respecto a dbLangGeneral
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 7
Este parámetro es obligatorio. Piense que una vez creada la Base de Datos, alguna vez le pedirá que le obtenga un Recordset con los datos ordenados alfabéticamente ( acuérdese de la sentencia SQL ORDER BY Nombredelcampo ) Si desea introducir una palabra clave para restringir el acceso a la base de datos, debe indicarlo a continuación. Deberá concatenarlo con la inf_local mediante el signo & y separarlo mediante punto y coma. Hay que poner pwd antes de la contraseña dbLangGeneral & ";pwd=NuevaContraseña" Para el último parámetro, Opciones se pueden usar las siguientes constantes : Constante
Descripción
dbEncrypt dbVersion10
Crea una base de datos codificada. Crea una base de datos que utiliza la versión 1.0 del motor de base de datos Microsoft Jet. Crea una base de datos que utiliza la versión 1.1 del motor de base de datos Microsoft Jet. Crea una base de datos que utiliza la versión 2.5 del motor de base de datos Microsoft Jet. Crea una base de datos que utiliza la versión 3.0 del motor de base de datos Microsoft Jet. Esta Versión es compatible con la 3.5
dbVersion11 dbVersion25 dbVersion30
Si se omite la constante de codificación, se creará una base de datos no codificada. El método CreateDataBase abre esta nueva base de datos y devuelve un objeto Database, cuya estructura y contenido deberá completar utilizando objetos de acceso a datos adicionales. Es decir, crea una Base de Datos sin nada que deberá rellenarla posteriormente con tablas. (Advertencia sobre las bases de datos codificadas. No piense que al codificar la base de datos va a mantener sus datos confidenciales en secreto. Access se los guardará codificados, pero se los va a presentar de forma clara. Utilice para ello el Password, ya que no podrá abrir la base si no se conoce ese Password. Pero tampoco se fíe mucho. Existen infinidad de craqueadores de contraseñas de Access. El Password vale para proteger la BD de usuarios no “piratas”. No emplee este procedimiento en aplicaciones en las que necesite verdadera confidencialidad. Objetos DAO para introducir en una base de datos Estos objetos que se pueden añadir son : Objetos TableDef, TableDefs, Field, Fields, QueryDef, QueryDefs. Para hacer las cosas poco a poco nos fijaremos solamente en las tablas y los campos. (Objetos Tabledef y Field) El Objeto TableDef es una tabla de una base de datos ACCESS. El Objeto TableDefs es la colección que contiene todas las tablas de la base de datos Un objeto Field representa un campo dentro de una tabla Access. La colección Fields contiene todos los campos de una tabla. Verá mas adelante que también hay objetos Field en otros objetos DAO (Index, QueryDef, Recordset, Relation), pero de momento vamos a fijarnos solamente en las tablas.
Crear una Tabla. Método CreateTableDef. Crea un objeto Tabledef, que no es ni más ni menos que la estructura de una tabla Access, pero que de momento está en la memoria RAM del ordenador. Cuando esa estructura se pase al fichero de la base de datos en el disco, será una Tabla que podemos ver cuando abramos la BD con Access. El objeto Tabledef debe crearlo un objeto DataBase. Puede ser perfectamente el objeto Database creado anteriormente. Pero seguramente lo ha cerrado (cerrando el programa, simplemente) para poder ver con Access la base de datos que acaba de crear. Puede volver a
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 8
crear el objeto DataBase abriendo la base de datos. Aunque se le explicará mas tarde, le adelanto el método para abrir una base de datos existente en el disco: Set MiBaseDatos = Misesion.OpenDatabase (“C:\MiCarpeta\MiBase.MDB”) Declaremos ahora el nombre que quiere ponerle al objeto Tabledef que va a crear: (MiTabla1) Dim Mitabla1 As Tabledef Si queremos introducir varias tablas en la BD deberemos declarar tantos objetos Tabledef como tablas necesitemos: Dim MiTabla2 as Tabledef, MiTabla3 as Tabledef, …… y crearemos las tablas necesarias mediante el método CreateTableDef Si acude a la información de VB para ver los parámetros que hay que pasar en el método CreateTableDef verá que son muchos: ([nombre[, atributos[, origen[, conexión]]]]) Se explicará para que sirven todos ellos, pero de momento nos quedamos únicamente con el primero: Nombre, que es el nombre que podrá ver en Access como nombre de la tabla que va a crear. Set Mitabla1 = MiBaseDatos.CreateTableDef (Nombre) Ejemplo:
Set Mitabla1 = MiBaseDatos.CreateTableDef (“Alumnos”)
Si queremos crear más Tabladefs: Set Mitabla2 = MiBaseDatos.CreateTableDef (“Profesores”) Set Mitabla3 = MiBaseDatos.CreateTableDef (“Asignaturas”) De momento solamente hemos creado uno o varios objetos Tabledef. Pero como siempre, vacíos. Una tabla tiene campos. Un objeto Tabledef tiene Fields. Debemos crear objetos Field (Campos) para poder meterlos en los objetos Tabledef que acabamos de crear.
Crear campos. Método CreateField Crea un objeto Field, que es la estructura de lo que mas tarde será un Campo de una tabla de la BD. El objeto Field solamente está en la memoria RAM del ordenador. Cuando pase a formar parte de una tabla será un campo de esa tabla. El objeto Field debe crearlo el objeto DAO superior a él: el Tabledef. Previamente declararemos los nombres de los Objetos Field a introducir. Dim MiCampo11 as Field, MiCampo121 as Field, MiCampo21 as Field (El Campo11 es el primer campo que meteremos en el Tabledef Tabla1. El Campo12 el segundo, el Campo21 será el primer campo del Tabledef Tabla2, etc. Los parámetros que vamos a pasar en este método son: Nombre Será el nombre de ese campo. P.e. NombreAlumno, Apellidos, etc Tipo Tipo de dato, String, numérico, Date, etc TamañoSólo para los campos String. Indicará el número de caracteres de ese campo. Creamos el objeto Field con la siguiente sintaxis Set MiCampo11 = Mitabla1.CreateField ([nombre[, tipo [, tamaño]]]) (Vea el Anexo 1 Propiedades de los campos al final de este capítulo) Ejemplos
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 9
Set MiCampo11 = Mitabla1.CreateField (“ID_Alumno”, dbText, 8) Set MiCampo12 = Mitabla1.CreateField (“NombreAlumno”, dbText, 20) Set MiCampo13 = Mitabla1.CreateField (“Apellidos”, dbText, 25) Set MiCampo14 = Mitabla1.CreateField (“Edad”, dbInteger) Set MiCampo15 = Mitabla1.CreateField (“Fecha_Ingreso”, dbDate) Set MiCampo21 = Mitabla2.CreateField (“NombreProfesor”, dbText, 20) Se crean todos los campos que se quieren introducir en las tablas. Observe que cada objeto Field debe ser creado por el objeto Tabledef que lo va a contener. (MiTabla1 crea todos sus campos, MiTabla2 los suyos, etc) Ya están todos los campos creados, pero todavía no están metidos en las tablas. Tenemos que añadirlos a la colección de campos de la tabla que los creó. Esa colección de campos es el Objeto Fields de la tabla. Se añade mediante el método Append. MiTabla1.Fields.Append Micampo11 MiTabla1.Fields.Append Micampo12 MiTabla1.Fields.Append Micampo13 MiTabla1.Fields.Append Micampo14 MiTabla1.Fields.Append Micampo15 MiTabla2.Fields.Append Micampo21 Ya tenemos campos formando parte de la colección Fields de las tablas. Ahora debemos añadir las tablas a la colección de tablas de la base de datos. Esa colección de tablas es el objeto Tabledefs de la base de datos, es decir, del objeto DataBase. Lo haremos también mediante el método Append MiBaseDatos.TableDefs.Append Mitabla1 MiBaseDatos.Tabledefs.Append Mitabla2 Si ahora cerramos la base de datos mediante el método Close: MiBaseDatos.Close Ya tenemos la base de datos creada en el disco de la misma forma que lo hubiera hecho Access. Pero puede que le falte algo respecto a una base creada directamente con Access: los Indices y las Relaciones. Un índice es una marca que le podemos poner a cada uno de los registros en un campo. Esa marca puede servir por ejemplo, para ordenar los registros de la BD por ese campo. Puede servir también para evitar que dos registros tengan el mismo valor para un determinado campo. En el ejemplo que estamos preparando, el Campo11 (ID_Alumno) queremos que sea un índice, y además que no se pueda repetir el mismo valor para dos registros distintos, de forma que no puedan existir dos registros con el mismo valor en ese campo. Una Relación es una correspondencia entre un campo de una tabla y otro campo de características similares en otra tabla. Esto nos lleva al concepto de Base de Datos Relacional que seguramente ya sabe de que se trata. En Access se puede establecer una relación de una forma muy sencilla. En Visual Basic también. Pero antes de seguir reflexionemos y recordemos lo que hemos hecho hasta ahora. Observe que, cada vez que creamos un objetos (DataBase, Tabledef, Field) usamos el mencionado “juego de niños”. El método correspondiente para crear un objeto DAO pertenece al objeto DAO inmediatamente superior en jerarquía, es decir, CreateDatabase es un método del objeto Workspace, CreateTableDef es un método del objeto Database, CreateField es un método del objetos TableDef. Las colecciones pertenecen también al objeto inmediatamente superior en jerarquía al tipo de objetos que forman la colección. La colección Fields (Objeto Fields) pertenece a un objeto
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 10
TableDef, la colección TableDefs (objeto TableDefs) pertenece a un objeto Database, y la colección Databases (Objeto Databases) pertenece a un objeto Workspace. La colección Workspaces pertenece al DBEngine, y el DBEngine ya no podemos asignarlo a ningún objeto DAO. Tendremos que decir que pertenece al sistema. Recordemos ese “juego de niños” : Set DAOInferior = DAOSuperior.Método ( - - - - - -) Hemos visto que después de crear un objeto, debemos añadirlo a la colección a la que debe pertenecer con el método Append. El procedimiento es siempre el mismo : Objeto superior.Colección.Append Objeto a añadir Sigamos ahora perfeccionando la base de datos. Vamos a ver como se crea un Indice.
Crear Indices para los campos. Método CreateIndex Para crear un índice debe estar creado el campo al que se le va a aplicar el índice. Puede que le parezca un poco extraño alguno de los métodos que vamos a usar para crear un índice, como por ejemplo volver a usar el procedimiento CreateField para crear un campo que ya existe. Son las incongruencias que tiene a veces Visual Basic. Le recomiendo que si no se acuerda bien de cómo se hace, recurra a la ayuda de VB, que en este caso es exacta y concisa. O si lo prefiere, al ejemplo que ilustra esta Guía del Estudiante como colofón a este capítulo. Primer debemos crear el Objeto Index. Se hace mediante el método del objeto Tabledef, CreateIndex. El índice debe crearlo el Tabledef al que pertenece el campo que queremos que sea índice. La sintaxis de CreateIndex es: Set NombreIndice = NombreTabledef.CreateIndex([Nombre]) Donde NombreIndice es el nombre de una variable declarada como tipo de dato objeto Index. NombreTabledef es el nombre de variable del objeto TableDef que se desea usar para crear el nuevo objeto Index. Nombre es una variable de tipo String que da un nombre único al nuevo objeto Index. Este nombre lo puede ver si abre la base de datos con Access y en la vista de Diseño de la tabla, abre la función Ver | Indices de la barra de menú. En nuestro ejemplo: Declaramos el objeto Index Dim MiIndice as Index Creamos el índice Set MiIndice = MiTabla1.CreateIndex (“Indice1”) Ya tenemos creado el Objeto Index. Ahora, (y aquí empieza la incongruencia citada) este objeto Index debe crear el campo que queremos que sea índice. Pero ese campo ya debe existir en el objeto Tabledef con el que hemos creado el índice (en este caso, en MiTabla1) Set MiCampo11 = MiIndice.CreateField (“ID_Alumno”, dbText, 8) El nombre del campo, tipo y tamaño deben coincidir con los datos que sirvieron para crear el campo en el objeto Tabledef. Pero un índice puede tener varios campos. Por ejemplo, piense en el código de un objeto en un inventario. El código es el número que identifica de forma unívoca a un objeto. Un objeto
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 11
inventariable (por ejemplo una mesa) tiene un código de grupo (por ejemplo, el 123) Hay muchas mesas dentro de un inventario, pero todas ellas tienen un número distinto (una tiene el 001, otra el 002, etc) La combinación de código de grupo más el número del objeto dentro de ese grupo queremos que sea un índice. Ese índice estará formado entonces por dos campos. En este caso deberemos crear los dos campos: Set MiCampo11 = MiIndice.CreateField (“C_Grupo”, dbText, 3) Set MiCampo12 = MiIndice.CreateField (“Numero”, dbText, 3) Ya tenemos el campo o los campos creados por el índice. Ahora debemos añadirlo (o añadirlos) a la colección Fields del objeto Index recién creado. Lo hacemos mediante el método Append MiIndice.Fields.Append MiCampo11 Si fuesen dos los campos MiIndice.Fields.Append MiCampo11 MiIndice.Fields.Append MiCampo12 No crea que ya hemos terminado. Un objeto Index tiene propiedades. Una de ellas ya la hemos visto sin querer, la propiedad Name (nombre del índice, en nuestro ejemplo, Indice1). Otras propiedades son: Primary - El objeto Index representa la clave primaria de la tabla. Unique - No permite repetición de valores en ese campo Required - Indica que todos los campos del objeto Index deben rellenarse. IgnoreNulls - Indica si permite valores Nulos en los campos del índice. (Existen además las propiedades Clustered, Foreign, DistinctCount que no se explican para no complicar mas el tema. Cuando los necesite no tendrá inconveniente en estudiarlos partiendo de la ayuda de VB) Para introducir el valor de una de estas propiedades se procede con la siguiente sintaxis: MiIndice.Uniuqe = True MiIndice.Primary = True Ya está creado el índice y tiene ya metido uno o mas campos y todas sus propiedades. Ahora debemos añadir ese índice a la colección Indexes del Objeto Tabledef. MiTabla1.Indexes.Append MiIndice Lo confieso. Cada vez que hago esto en la vida real tengo que volver a leer este procedimiento en la Guía del Estudiante. Es complicado, pero alguna vez se terminará aprendiendo.
Ya tenemos la base de datos completamente creada. Sin embargo alguien dirá que le falta algo: Relacionar dos tablas
Relaciones entre tablas Método CreateRelation Una Relación es una asociación establecida entre dos campos del mismo tipo ubicados en dos tablas distintas. Se pueden establecer relaciones uno a uno ó uno a varios. Para relacionar un campo con otros, ese campo debe ser clave primaria. A la tabla que contiene a este campo se le llama Tabla Principal. A la tabla que contiene el campo (o los campos) relacionados se le llama Tabla Relacionada.
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 12
Para crear una relación, usaremos un nuevo objeto DAO : El objeto Relation. Este objeto forma parte de una colección, que es a su ves otro objeto DAO : el objeto Relations. Para crear una relación usaremos el Método CreateRelation, que es un método del objeto Database. (Lógico, una relación se establece entre dos tablas. Por lo tanto, la relación debe pertenecer al objeto DAO superior jerárquicamente a las tablas: el Objeto Database. Como para cualquier otro objeto DAO, es necesario declararlo : Dim MiRelacion as Relation Para crear la relación deberemos usar el método CreateRelation Set MiRelacion = MiBaseDatos.CreateRelation ("RelacionUno") Una relación se hace entre dos campos. Una relación debe tener campos. Por lo tanto, deberemos hacer una cosa similar a la que hacíamos para el método CreateIndex, crear los campos mediante el Objeto Relation que acabamos de crear. Pero esos campos ya deben estar creados en las tablas que se van a relacionar. Supongamos que queremos crear una relación en la base de datos creada en el ejemplo anterior, y queremos relacionar el campo Campo11 que está en MiTabla1 y que lo habíamos hecho clave primaria, con el campo Campo21 de MiTabla2. MiTabla1 y MiTabla2 son los nombres reales de las tablas, NO los nombres de los objetos Tabledef. El ejemplo que trae la ayuda de VB puede ser muy aclaratorio, pero le advertimos lo mismo que para los índices, paciencia. Una vez creada la relación, podrá comprobarlo visualizándola con el visor de relaciones del Access Suponemos que la base de datos está abierta. Si no lo está, la abrimos. Set MiBaseDatos = Workspaces(0).OpenDatabase("MIBD.MDB") Creamos el objeto Relation, que tendrá por nombre RelacionUno, pero este nombre NO debe confundirse con el nombre del objeto DAO Relation, que es MiRelacion Set MiRelacion = MiBaseDatos.CreateRelation ("RelacionUno") Una vez creada, le decimos a MiRelacion cual es la Tabla principal MiRelación.Table = "MiTabla1"
'Nombre de la tabla principal.
Le decimos cual es el nombre de la tabla relacionada MiRelación.ForeignTable = "MiTabla2"
‘Nombre de la tabla relacionada
Le ponemos los atributos a la relación. En este caso dbRelationUpdateCascade, para que, si hacemos un cambio en el valor del campo de la tabla principal, ese cambio se refleje en el campo o los campos relacionados con el. MiRelación.Attributes = dbRelationUpdateCascade Le decimos cual es el nombre del campo de la tabla principal que vamos a relacionar, mediante el método CreateField. Deberemos declarar el nombre del objeto Field que vamos a crear para la relación Dim MiCampo as Field Set MiCampo = MiRelación.CreateField("Campo11")
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 13
Le recordamos lo de antes. Campo11 debe estar ya creado en la tabla Tabla1. Parece un poco ilógico usar el método CreateField para un campo que ya está creado. Le decimos ahora cual es el nombre del campo en la tabla relacionada MiCampo.ForeignName = "Campo21" Añadimos el campo creado a la colección Fields del objeto Relation MiRelación.Fields.Append MiCampo Y ahora añadimos el objeto Relation recién creado a la colección Relations del objeto Database MiBaseDatos.Relations.Append MiRelación Solamente nos falta ver que valores puede tener la propiedad Attributes del objeto Relation dbRelationUnique dbRelationDontEnforce dbRelationInherited dbRelationUpdateCascade dbRelationDeleteCascade
La relación es uno a uno. La relación no es impuesta (no hay integridad referencial). La relación existe en una base de datos no activa que contiene las dos tablas vinculadas. Las actualizaciones se realizarán en cascada. Las eliminaciones se realizarán en cascada.
Ahora ya casi podemos decir que tenemos la base de datos creada. Puede que sea así o que le falte alguna cosa. Puede faltarle una o varias consultas. Las consultas también se pueden crear mediante objetos DAO. Precisamente con un objeto QueryDef
Consultas. El Objeto QueryDef Pero habrá observado que una base de datos ACCESS puede contener, además de tablas, CONSULTAS. Las consultas no contienen datos. Contienen una referencia a los registros de las tablas que los contienen. Una consulta podríamos decir que son conjuntos de registros tomados de una o varias tablas (en este último caso, esas tablas deben estar relacionadas) que cumplen unas determinadas condiciones. Pero aunque podemos ver esos registros como tales, con sus datos exactamente igual que si se tratase de los registros de una tabla, las consultas no contienen el dato, sino el número del registro dentro de la tabla que lo contiene. A la hora de presentar los datos de una consulta, lo que estamos presentando son los datos almacenados en la tabla o las tablas que componen esa consulta.
Crear una consulta. Método CreateQueryDef Un objeto QueryDef representa una consulta de la base de datos. El objeto QueryDefs es la colección de objetos QueryDef. La diferencia entre una consulta (Un QueryDef) y una tabla (Un TableDef) es que la Tabla tiene dentro de sí los datos. La consulta tiene dentro una referencia al lugar de las tablas donde se encuentran los datos. Antes de utilizar el método CreateQueryDef debe declarar el nombre de los objetos a crear, declarándolos como Variables Objeto tipo QueryDef . El ámbito es igual que para cualquier variable: Public MiConsulta1 as QueryDef Public MiConsulta2 as QueryDef Ahora podemos utilizar el método CreateQueryDef para crear el nuevo objeto QueryDef en la base de datos. Sintaxis Con la fórmula de siempre :
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 14
Set MiConsulta1 = MiBaseDatos.CreateQueryDef ([Nombre][, Texto_sql]) Donde MiConsulta1 es una variable del tipo QueryDef que previamente se ha declarado como tal. Será el nombre por el que llamemos al Objeto QueryDef en el código de nuestra aplicación. MiBaseDatos es el nombre del objeto Database abierto en el que vamos a introducir el nuevo objeto QueryDef. Nombre es una expresión de cadena que representa el nombre de la nueva consulta que vamos a crear. Este nombre será el que veamos al abrir la base de datos con Access en la pestaña Consultas. Puede omitirlo a la hora de crear la consulta, pero deberá añadirselo posteriormente. Texto_sql es una expresión de cadena (instrucción SQL válida) que define el objeto QueryDef. Lógicamente una consulta nos debe suministrar una serie de datos de una o mas tablas. Esos datos no tienen porqué ser todos los datos de las tablas. Texto_sql es precisamente el filtro de esos datos (expresado mediante una cláusula SQL). Una vez creado el objeto QueryDef, no es necesario añadirlo a la colección QueryDefs de la Base de Datos, excepto que hayamos creado el objeto QueryDef sin nombre. (Sin haber puesto el parámetro Nombre, según se comentó mas atrás). Para añadirlo a la colección QueryDefs : MiBaseDatos.QueryDefs.Append MiConsulta1 Como caso práctico de creación de una consulta, podemos tener Set MiConsulta1 = MiBaseDatos.CreateQueryDef (“Fernandez”, “Select Nombre, Apellido1, Apellido2 From Alumnos Where Apellido1 = ‘Fernandez’ ) Ahora ya tenemos la base de datos creada con todas las posibilidades. Ha llegado el momento de crear una base real para comprobar todo lo expuesto.
Ejemplo práctico de creación de una base de datos. El famoso ejercicio del Videoclub Vayamos a un ejemplo típico en cualquier curso de Visual Basic: el famoso Videoclub. Un videoclub tiene una base de datos en la que tenemos una tabla, de nombre Clientes donde figuran los nombres y dirección de los clientes, así como el número de su cuenta bancaria. Tiene un campo (ID_Cliente) que es el que define al cliente. Será un campo tipo texto, que albergará un número que se incrementará en 1 cada vez que se hace un nuevo cliente. Cada cliente tiene un ID_Cliente y ese ID_Cliente es único para él. (Este campo podría ser un autonumérico, pero personalmente no me gusta usar autonuméricos. Un autonumérico es un Long –numérico- y quiero ser coherente con lo expuesto en el capítulo 1 donde se decía que solamente se usarían campos numéricos en aquellos datos con los que se hagan operaciones matemáticas) El campo ID_Cliente será clave primaria, ya que no pueden existir dos clientes con el mismo ID_Cliente. Otra tabla necesaria será la tabla Peliculas, donde introduciremos todos los datos relativos a las películas (existentes o no en el videoclub), tal como título, director, artistas, resumen, calificación, precio, etc. Tendrá un campo, ID_Pelicula que identificará a esa película. Pero puede haber versiones en varios idiomas, por lo tanto, existirá otro campo Idioma de tipo texto, para introducir ese dato. Por lo tanto, una película deberemos definirla por el conjunto formado por su ID_Pelicula y por su Idioma. El conjunto de esos dos campos será la clave primaria. (Si cree que hay campos que no tienen sentido en esta tabla (Idioma), piense que esto es un ejemplo para poder explicar de la forma más didáctica todas las posibles variaciones de una instrucción) Existe otra tabla denominada Cintas, donde figurarán todas las cintas existentes en el videoclub. Para poder relacionarla con la tabla Peliculas, le ponemos un campo llamado
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 15
ID_Pelicula que en esta tabla no será clave primaria. También le pondremos el campo Idioma, sobre el que no haremos ningún tipo de relación. Tendrá un campo ID_Cinta que será la combinación de varios datos, uno que nos indique la película que tiene grabada esa cinta (Será la combinación de los campos ID_Pelicula e Idioma) y de un número secuencial que indicará el número de la copia. Si le pone imaginación y este conjunto de datos puede meterse en un código de barras, le facilitará la operación de alquiler y devolución. En este ejercicio haremos que sea así, dándole a este campo un tamaño de 13 dígitos para poder meterlo en un código EAN-13. Podemos añadirle mas campos a nivel administrativo, como fecha de alta, fecha de baja, precio de esta copia, etc. Existirá una tercera tabla, Alquileres, que relacionará al cliente con la cinta que ha alquilado. Tendrá un campo llamado ID_Cliente y otro ID_Cinta. Aparte tendrá otros dos campos, fecha de alquiler y fecha de devolución. La base de datos deberá tener dos relaciones, una, entre el campo ID_Cliente de la tabla Clientes y el campo ID_Clientes de la tabla alquileres (Será uno a infinito) y otra relación, entre el campo ID_Cinta de la tabla Cintas y el campo ID_Cinta de la tabla Alquileres (Relación 1 a 1 ya que solamente existe una cinta con esa ID_Cinta). Para darle más alegría al ejercicio le pondremos una relación entre los campos ID_Pelicula e Idioma de las tablas Peliculas y Cintas. (Se ha puesto el nombre de Pelicula al campo relacionado con ID_Pelicula para que se vea que dos campos relacionados no tienen porqué tener el mismo nombre. Eso sí, deben tener las mismas características) Como colofón a todo esto, crearemos una consulta en la que utilizaremos todas las relaciones. Para llevar a cabo este ejercicio se ha partido de una interface gráfica en la que pueden verse tres botones (Borrar la base de datos, crearla y salir) y un TextBox donde se ha puesto el nombre del fichero de la base de datos en su propiedad Text. Veamos el código de cada uno de los botones (por orden inverso de complejidad del código)
Fig. 20.3 Interface gráfica de la parte de crear bases de datos para la aplicación del Videoclub CODIGO Private Sub BSalir_Click() Unload Me End Sub Private Sub BBorrar_Click() On Error GoTo RutErr Dim HayDir As String HayDir = Dir(TBNombreBase) If HayDir <> "" Then Kill TBNombreBase End If RutErr: If Err = 75 Then MsgBox "Tiene la Base de Datos abierta por otro programa" End If End Sub Creación de la base de datos
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 16
Private Sub BCrear_Click() On Error GoTo RutErr Dim HayDir As String ‘Se comprueba que existe la BD y se invita a borrarla HayDir = Dir(TBNombreBase) If HayDir <> "" Then MsgBox "Ya existe el fichero con la base de datos. Debe borrarlo previamente" Exit Sub End If ‘ Se declaran todas las variables tipo objeto Dim MiBaseDatos As Database Dim MiTabla1 As TableDef, MiTabla2 As TableDef, MiTabla3 As TableDef Dim MiTabla4 As TableDef Dim MiCampo11 As Field, MiCampo12 As Field, MiCampo13 As Field Dim MiCampo14 As Field, MiCampo15 As Field, MiCampo21 As Field Dim MiCampo22 As Field, MiCampo23 As Field, MiCampo24 As Field Dim MiCampo25 As Field, MiCampo31 As Field, MiCampo32 As Field Dim MiCampo33 As Field, MiCampo34 As Field, MiCampo35 As Field Dim MiCampo36 As Field, MiCampo41 As Field, MiCampo42 As Field Dim MiCampo43 As Field, MiCampo44 As Field ‘Se crea el Objeto DataBase (Se toma el nombre del fichero del TextBox TBNombreBase Set MiBaseDatos = Workspaces(0).CreateDatabase(TBNombreBase, dbLangGeneral) ‘Se Set Set Set Set
crean los Objetos Tabledef MiTabla1 = MiBaseDatos.CreateTableDef("Clientes") MiTabla2 = MiBaseDatos.CreateTableDef("Peliculas") MiTabla3 = MiBaseDatos.CreateTableDef("Cintas") MiTabla4 = MiBaseDatos.CreateTableDef("Alquileres")
‘Cada Tabledef crea sus propios Objetos Field Set MiCampo11 = MiTabla1.CreateField("ID_Cliente", dbText, 8) Set MiCampo12 = MiTabla1.CreateField("Nombre", dbText, 20) Set MiCampo13 = MiTabla1.CreateField("Apellidos", dbText, 50) Set MiCampo14 = MiTabla1.CreateField("Direccion", dbText, 50) Set MiCampo15 = MiTabla1.CreateField("Telefono", dbText, 15) Set Set Set Set Set
MiCampo21 = MiCampo22 = MiCampo23 = MiCampo24 = MiCampo25 =
MiTabla2.CreateField("ID_Pelicula", dbText, 8) MiTabla2.CreateField("Titulo", dbText, 20) MiTabla2.CreateField("Idioma", dbText, 1) MiTabla2.CreateField("Director", dbText, 25) MiTabla2.CreateField("Resumen", dbText, 255)
Set Set Set Set Set Set
MiCampo31 = MiCampo32 = MiCampo33 = MiCampo34 = MiCampo35 = MiCampo36 =
MiTabla3.CreateField("ID_Cinta", dbText, 13) MiTabla3.CreateField("Pelicula", dbText, 20) MiTabla3.CreateField("Idioma", dbText, 1) MiTabla3.CreateField("Precio", dbSingle) MiTabla3.CreateField("Fecha_Alta", dbDate) MiTabla3.CreateField("Fecha_Baja", dbDate)
Set Set Set Set
MiCampo41 = MiCampo42 = MiCampo43 = MiCampo44 =
MiTabla4.CreateField("ID_Cliente", dbText, 8) MiTabla4.CreateField("ID_Cinta", dbText, 13) MiTabla4.CreateField("Fecha_Alq", dbDate) MiTabla4.CreateField("Fecha_Dev", dbDate)
‘Una vez creados los campos se les ponen las peopiedades que se estime oportuno ‘En este caso, se ha puesto la propiedad AllowZeroLength (Permitir valores nulos en ese ‘campo) a lo que interesa en cada uno de los campos. Nota Tenga presente que por defecto le ‘va a dejar el campo que NO permite valores nulos, circunstancia que le va a crear problemas.
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 17
MiCampo12.AllowZeroLength = True MiCampo13.AllowZeroLength = False MiCampo14.AllowZeroLength = True MiCampo15.AllowZeroLength = True ‘Se añaden los campos a la colección Fields de las tablas MiTabla1.Fields.Append MiCampo11 MiTabla1.Fields.Append MiCampo12 MiTabla1.Fields.Append MiCampo13 MiTabla1.Fields.Append MiCampo14 MiTabla1.Fields.Append MiCampo15 MiTabla2.Fields.Append MiCampo21 MiTabla2.Fields.Append MiCampo22 MiTabla2.Fields.Append MiCampo23 MiTabla2.Fields.Append MiCampo24 MiTabla2.Fields.Append MiCampo25 MiTabla3.Fields.Append MiCampo31 MiTabla3.Fields.Append MiCampo32 MiTabla3.Fields.Append MiCampo33 MiTabla3.Fields.Append MiCampo34 MiTabla3.Fields.Append MiCampo35 MiTabla3.Fields.Append MiCampo36 MiTabla4.Fields.Append MiCampo41 MiTabla4.Fields.Append MiCampo42 MiTabla4.Fields.Append MiCampo43 MiTabla4.Fields.Append MiCampo44 ‘Se añaden las tablas a la colección Tabledefs del objeto Database MiBaseDatos.TableDefs.Append MiTabla1 MiBaseDatos.TableDefs.Append MiTabla2 MiBaseDatos.TableDefs.Append MiTabla3 MiBaseDatos.TableDefs.Append MiTabla4 ‘Se declaran las variables tipo objeto Index y tipo objeto Field para crear los índices Dim MiIndice1 As Index, MiIndice2 As Index, MiIndice3 As Index Dim CampoIndiceA As Field Dim CampoIndiceB As Field ‘Se crea el primer objeto Index Set MiIndice1 = MiTabla1.CreateIndex("IndiceCliente") ‘Este objeto Index crea el campo que va a ser índice, con los mismos datos que el ‘campo MiCampo11 Set CampoIndiceA = MiIndice1.CreateField("ID_Cliente", dbText, 8) ‘Se añade el campo a la colección Fields del Index MiIndice1.Fields.Append CampoIndiceA ‘Se le dice que es un índice primario (Clave primaria) MiIndice1.Primary = True ‘Se añade el objeto Index recién creado a la colección Index del objeto tabledef MiTabla1.Indexes.Append MiIndice1 ‘Se procede de igual forma con el segundo objeto Index Set MiIndice2 = MiTabla2.CreateIndex("IndicePeliculas") ‘Aquí se meten dos campos en el mismo índice Set CampoIndiceA = MiIndice2.CreateField("ID_Pelicula", dbText, 10) Set CampoIndiceB = MiIndice2.CreateField("Idioma", dbText, 1) MiIndice2.Fields.Append CampoIndiceA MiIndice2.Fields.Append CampoIndiceB MiIndice2.Primary = True
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 18
MiTabla2.Indexes.Append MiIndice2 Set MiIndice3 = MiTabla3.CreateIndex("IndiceCintas") Set CampoIndiceA = MiIndice3.CreateField("ID_Cinta", dbText, 13) MiIndice3.Fields.Append CampoIndiceA MiIndice3.Primary = True MiTabla3.Indexes.Append MiIndice3
‘Se declaran los objetos Relation y un par de objetos Field para crear las relaciones. Por ‘claridad se han declarado objetos Field distintos para la creación de los índices y de las ‘relaciones, pero podrían haber sido los mismos Dim MiRelacion1 As Relation, MiRelacion2 As Relation, MiRelacion3 As Relation Dim CampoRelacionA As Field Dim CampoRelacionB As Field ‘Se crea la primera relación entre el campo ID_Clientes de la tabla Clientes (Tabla ‘primaria) y el campo ID_Cliente de la tabla Alquileres (Tabla relacionada) Set MiRelacion1 = MiBaseDatos.CreateRelation("RelClientes", "Clientes", "Alquileres") Set CampoRelacionA = MiRelacion1.CreateField("ID_Cliente", dbText, 8) CampoRelacionA.ForeignName = "ID_Cliente" MiRelacion1.Fields.Append CampoRelacionA MiBaseDatos.Relations.Append MiRelacion1 ‘Se crea la segunda relación Set MiRelacion2 = MiBaseDatos.CreateRelation("RelCintas", "Cintas", "Alquileres") MiRelacion2.Attributes = dbRelationUnique Set CampoRelacionA = MiRelacion1.CreateField("ID_Cinta", dbText, 13) CampoRelacionA.ForeignName = "ID_Cinta" MiRelacion2.Fields.Append CampoRelacionA MiBaseDatos.Relations.Append MiRelacion2 ‘La tercera relación “relaciona” dos campos, Película e Idioma Set MiRelacion3 = MiBaseDatos.CreateRelation("RelCintasPelis", "Peliculas", "Cintas") Set CampoRelacionA = MiRelacion1.CreateField("ID_Pelicula", dbText, 8) Set CampoRelacionB = MiRelacion1.CreateField("Idioma", dbText, 8) CampoRelacionA.ForeignName = "Pelicula" CampoRelacionB.ForeignName = "Idioma" MiRelacion3.Fields.Append CampoRelacionA MiRelacion3.Fields.Append CampoRelacionB MiBaseDatos.Relations.Append MiRelacion3 ‘Se comienza a crear una consulta (La sentencia SQL está cortada dado que no cabe en ‘una línea. El signo indica que esa línea continúa en la siguiente Dim MiConsulta1 As QueryDef Set MiConsulta1 = MiBaseDatos.CreateQueryDef("Pelis", "SELECT Clientes.Nombre, Clientes.Apellidos, Clientes.Telefono, Peliculas.Titulo, Alquileres.ID_Cinta " & _ " FROM Peliculas INNER JOIN (Clientes INNER JOIN (Cintas INNER JOIN Alquileres ON Cintas.ID_Cinta = Alquileres.ID_Cinta) " & _ " ON Clientes.ID_Cliente = Alquileres.ID_Cliente) ON (Peliculas.Idioma = Cintas.Idioma) AND (Peliculas.ID_Pelicula = Cintas.Pelicula)" & _ " WHERE (((Alquileres.ID_Cinta)='0000000000001'));") ‘Se cierra la el objeto database. En este momento es cuando se crea el fichero. MiBaseDatos.Close ‘se comunica al usuario la buena nueva de que la base ha sido creada. MsgBox "La base de datos se ha creado con éxito" ‘Aquí comienza la rutina de error LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 19
Exit Sub RutErr: MsgBox "Ha ocurrido el error " & Err & ". " & Err.Description End Sub Nota acerca de la sentencia SQL para crear la consulta. Se puede ser muy experto en SQL y tener miedo a crear una consulta SQL debido a la complejidad que puede tener. No crea que el autor ha escrito la sentencia SQL que ha visto. Ha utilizado el truco de crear primero la consulta con Access, de la forma gráfica que seguro que Vd. conoce, ha comprobado que era eso lo que quería, pasó a Vista SQL y la copió. Es muy bueno hacer prácticas con SQL, para que no se olvide. Pero en algunos casos es preferible acudir a los trucos que nos proporcionan nuestras herramientas. El resultado de todo esto podemos verlo si abrimos la base de datos con Access
Fig. 20.4 Tablas de la BD de Videoclub creada con el código descrito
Fig. 20.5 Y la consulta
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 20
Fig. 20.6 Clave Primaria formada con los dos campos de la tabla Películas
Fig. 20.7 Propiedades de esa clave primaria
Fig. 20.8 Las relaciones creadas
Crear bases de datos con contraseña Imagínese que quiere que su base de datos no se pueda abrir mediante Access. Lo que debe hacer es crearla con una contraseña (Password) de forma que cuando la quiera abrir, le pida el Password. Si no lo sabe, no se puede abrir. Si crea una base de datos con estas características, y no comunica el Password a nadie, solamente la podrá abrir mediante el programa. (Eso sí, el programa para abrirla, deberá introducir en su instrucción de apertura el Password con el que se creó.) Si volvemos a la línea donde creabamos la base de datos del videoclub: Set MiBaseDatos = Workspaces(0).CreateDatabase(TBNombreBase, dbLangGeneral)
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 21
Basta con añadirle ";pwd=PaswordElegido" concatenándolo tras el parámetro que especifica la lengua. En el ejemplo siguiente hemos usado como Password las iniciales LSB Set MiBaseDatos = = Workspaces(0).CreateDatabase(TBNombreBase, dbLangGeneral & ";pwd=LSB") Cuando lleguemos a la parte de abrir bases de datos, explicaremos cómo se abre una base de datos con contraseña. Y no crea que ha conseguido la confidencialidad total de sus datos. Existen programas que puede bajarse de Internet que le leen la contraseña con la que ha protegido su base. No es una protección total, pero sí suficiente para que un usuario “normal” no se la pueda abrir.
Crear bases de datos encriptada Puede encriptar el fichero de su base de datos. Solamente le será útil para que no puedan ver el contenido del fichero .MDB, ya que si se abre con Access, le presentará los datos de forma correcta. Para encriptar una base de datos basta con añadir la palabra dbEncrypt en la instrucción donde ha creado la BD Set MiBaseDatos = Workspaces(0).CreateDatabase(TBNombreBase,dbLangGeneral & ";pwd=LSB", dbEncrypt)
Ha merecido la pena el trabajo. Hemos creado la base de datos haciendo click en un botón de la aplicación. No ha sido necesario venderle al cliente Access, ni enviar una base de datos vacía con los discos de distribución. Además hemos controlado todos los parámetros de los campos de nuestra BD. Merece la pena crearse las bases de datos por programa.
Anexo1 Propiedades de los campos Ha visto mas atrás que puede ser necesario cambiar las propiedades de los campos una vez creados (Por ejemplo, MiCampo13.AllowZeroLength = False) Alargaríamos demasiado este ya largo capítulo si se explican todos los las propiedades que puede tener un campo. Añada un poco de esfuerzo a su estudio y vea las propiedades de los objetos Field en la ayuda. Le reseño aquí las que he considerado mas importantes AllowZeroLength Si/NO Si es Si permite que ese campo tenga valores nulos DataUpdatable Si/No Si es Si permite modificar el dato de ese campo. No tiene aplicación a la hora de crear un campo. Sí puede cambiar el valor de esta propiedad por ejemplo, cuando crea un recordset. DefaultValue Es el valor que le pone a ese campo si no introduce ninguno. Puede indicar un valor a la hora de crear el campo: Campo14.DefaultValue = “Madrid” Required Si/No Indica si el dato es requerido. En caso de que tome el valor SI (True) es necesario introducir un dato en ese campo Value Es justamente el dato que almacena en ese campo. Es la propiedad por defecto del objeto Field. Propiedades Type, Attributes y Size referidas a los campos En el método CreateField debe introducir el tipo del campo que desea crear. (Como puede ver mas atrás, con esta sintaxis p.e.: CreateField("Fecha_Alq", dbDate) Ese tipo coincide con la propiedad Type aplicada a los campos. La propiedad Size solamente tendrá que aplicarla cuando vaya a crear un campo tipo Texto. LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 22
Puede ver la ayuda de VB para mas detalles. Le enumero los mas usuales
Propiedad Type Constante
Descripción
Constante
Descripción
dbBoolean dbCurrency dbDouble dbLong dbLongBinary dbSingle
Campo SI/NO dbByte Campo tipo Byte Tipo moneda dbDate Tipo Date/Time Numérico Doble dbInteger Numérico Integer Entero Long dbMemo Campo Memo Binario largo (Objeto OLE) Numérico Single dbText CampoTexto
Propiedad Attributes Constante
Descripción
dbFixedField dbVariableField dbAutoIncrField
El tamaño del campo es fijo (predeterminado en campos numéricos). El tamaño del campo es variable (Sólo campos de texto). El valor del campo en los registros nuevos es incrementado automáticamente a un valor Long integer único que no puede ser modificado. Sólo aceptado en tablas bases de datos Jet. El valor del campo no puede ser modificado. El campo está ordenado de forma descendente (Z-A o 100-0) (sólo se aplica a objetos Field de una colección Fields de un objeto Index). Si se omite esta constante, el campo se ordena ascendentemente (A-Z o 0-100) (predeterminado).
dbUpdatableField dbDescending
dbAutoIncrField es la constante a utilizar cuando queremos crear un campo que se vaya incrementando cada vez que se introduce un nuevo registro (Campo Contador). Por ejemplo, si hemos creado un campo denominado ID que será el contador de una serie de registros, y queremos que se incremente en 1 cada vez que añadamos un registro, debemos usar la propiedad Attributes ANTES de añadir ese campo al objeto TableDef correspondiente, de la siguiente forma : Set micampo1N = mitabla01.CreateField("ID", 4) micampo1N.Required = True micampo1N.Attributes = dbAutoIncrField mitabla01.Fields.Append micampo11
‘ Creamos el campo ID ‘ La propiedad Required la veremos ‘ Le damos atributo de contador ‘ Añadimos el campo a la tabla
Propiedad Size Devuelve o establece un valor que indica el tamaño máximo, en bytes, de un objeto Field que contiene texto o el tamaño fijo de un objeto Field que contiene texto o valores numéricos Esta propiedad se le debe suministrar en la sintaxis de CreateField solamente cuando creamos un campo tipo texto - CreateField("ID_Cinta", dbText, 13). El tamaño de un campo texto puede ser desde 1 a 255 caracteres. Para el resto de los tipos de datos, el tamaño va implícito en el tipo de dato. Puede consultar el tamaño ocupado por cualquier campo, leyendo la propiedad Size de un campo: Variable = Micampo11.Size
Abrir una Base de Datos ya existente mediante DAO.
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 23
Método OpenDatabase Hasta ahora hemos visto como crear una base de datos. No es lo más usual. Lo normal es tener la base de datos creada y abrirla cuando queremos extraer datos o introducir datos. Vamos a ver como se abre una base de datos mediante DAO, usando el espacio de trabajo Microsoft JET. Veremos luego como se puede crear un Recordset, que es en realidad sobre el que se leen y escriben datos, como se pueden añadir registros, borrarlos etc. Para abrir una base de datos existente deberemos usar el método OpenDatabase. Pero previamente deberemos declarar el nombre que se le va a dar a ese objeto Database mediante la instrucción Dim si queremos que el ámbito de ese Database sea un formulario, o Global o Public, (en la sección de declaraciones de un Módulo o Formulario) si queremos que el ámbito sea toda la aplicación. Por ejemplo, si queremos abrir una base de datos y poder referirnos a ella en toda la aplicación, debemos declararla de esta forma en la sección de declaraciones de un módulo : Public MiBaseDatos as Database
Método OpenDatabase Abre la base de datos existente. La base de datos abierta se agrega automáticamente a la colección Databases. Sintaxis Recuerde la expresión general: Set DAOInferior = DAOSuperior.Método ( - - - - - -) Set MiBaseDatos = Misesion.OpenDatabase(nombre_bd[, exclusivo[, sólo-lectura[, origen]]]) La sintaxis del método OpenDatabase consta de las siguientes partes: MiBaseDatos Variable de tipo de dato objeto Database que representa el objeto DAO Database que se va a abrir. Misesion Variable de tipo de dato objeto Workspace que representa el objeto Workspace existente que va a contener a la base de datos. nombre_bd Expresión de cadena con el nombre de un archivo (y su Path) de una base de datos existente. Si el nombre de archivo tiene extensión, es necesario especificarla. Si la red lo admite, también puede especificar una ruta de red, como por ejemplo "\\MISERVID\MICOMP\MIDIR\MIBD.MDB". nombre_bd también puede ser un origen de datos OBDC. Lo veremos en otro capítulo. Al especificar nombre_bd hay que tener en cuenta algunas consideraciones: Si se refiere a una base de datos ya abierta por otro usuario con acceso exclusivo, se producirá un error. Si no se refiere a una base de datos existente o a un origen de datos ODBC válido, se producirá un error. Si es una cadena de longitud cero ("") y origen es "ODBC;", aparecerá un cuadro de diálogo con todos los nombres de orígenes de datos ODBC registrados, en el que el usuario podrá elegir una base de datos. exclusivo sólo_lectura
origen
LSB
Valor de tipo Boolean que es True si la base de datos se va a abrir con acceso exclusivo (no compartido) o False si se va a abrir con acceso compartido. Si se omite este argumento, la base se abrirá con acceso compartido. Valor de tipo Boolean que es True si la base de datos se va a abrir con acceso de sólo lectura o False si se va a abrir con acceso de lectura/escritura. Si se omite este argumento, la base se abrirá para lectura/escritura. Expresión de cadena utilizada para abrir la base de datos. Esta cadena
Visual Basic - Guía del Estudiante
Capítulo 12
Página 24
constituye los argumentos de conexión ODBC. Para especificar una cadena de origen deberá especificar también los argumentos exclusivo y sólo_lectura. Consulte la sintaxis en la propiedad Connect. p.e.
Set MiBaseDatos = Misesion.OpenDatabase (“C:\Guia_Est\Videoclub.MDB”)
abre la base de datos cuyo fichero está en C :\Guia_Est y se llama Videoclub.MDB. Al no expresarle mas parámetros la abre de modo no exclusivo, y de lectura y escritura. Al no especificar nada en el parámetro origen entiende que la base es ACCESS Nota para todo este capítulo. No es necesario cambiar el nombre del Workspace. Si Misesion = Workspaces (0), la sentencia anterior podemos ponerla también : Set MiBaseDatos = Workspaces(0).OpenDatabase (“C:\Guia_Est\Videoclub.MDB”) El hecho de poner siempre un nombre al Workspace es solamente a efectos didácticos Ya tenemos la base de datos abierta. Pero no crea que nuestro programa ha hecho trabajo. Se ha limitado a ver si existía el fichero indicado y a “apuntar” el nombre y path de ese fichero que contiene la base de datos. El trabajo comienza cuando cree el recordset.
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 25
EL OBJETO RECORDSET
(O la mitad de lo que Vd. necesita saber de Bases de Datos) Un objeto Recordset contiene los registros de una tabla o de una consulta. Puede ser que no los contenga todos, si al crear ese recordset le hemos impuesto que los registros cumplan una determinada condición. En resumen, un recordset es un objeto de acceso a datos que contiene una colección de registros tomados, bien de una tabla, bien de un conjunto de tablas (a través de una consulta) También hay una colección Recordsets. La colección Recordsets contiene todos los objetos Recordset abiertos de un objeto Database. Al utilizar objetos de acceso a datos, casi toda la interacción con los datos se produce a través de objetos Recordset. Todos los objetos Recordset están formados por registros (filas) y campos (columnas). Existen tres tipos de objetos Recordset: Recordset de tipo tabla: Representación en código de una tabla base de datos que puede utilizarse para agregar, modificar o eliminar registros de una sola tabla de base de datos. Un Recordset tipo Tabla contiene todos los campos de una tabla y no puede contener campos que no pertenezcan a esa tabla. Recordset de tipo hoja de respuestas dinámica: Resultado de una consulta que puede tener registros actualizables. Un Recordset de tipo hoja de respuestas dinámica es un conjunto dinámico de registros que puede utilizarse para agregar, modificar o eliminar registros de una o más tablas de una base de datos subyacente. Este tipo de objeto Recordset puede contener campos de una o más tablas de una base de datos. Recordset de tipo instantánea: Copia estática de un conjunto de registros que puede utilizarse para buscar datos o generar informes. Los objetos Recordset de tipo instantánea pueden contener campos de una o más tablas de una base de datos, pero no pueden actualizarse. Un recordset de tipo instantánea (Snapshot) es una fotografía que se hace a la tabla o tablas que lo componen. Los datos que tiene el recordset son los que existían cuando se realizó la fotografía. Cualquier actualización posterior no se puede ver. Resumiendo, un Recordset es un conjunto de registros. Recuerde cuando explicábamos en control Data se decía que este control creaba un Recordset a partir de sus propiedades DatabaseName y RecordSource. De esta forma, el conjunto de registros que tiene ese control Data es la totalidad de los registros de la tabla (o consulta) que poníamos en la propiedad RecordSource. Pero siempre podemos asignar a la propiedad Recordset de ese control Data un Recordset ya creado mediante código. Y en ese caso, solamente contendrá los campos que a nosotros nos interese, incluso campos de distintas tablas, cosa que viene muy bien algunas veces. Un Recordset lo crearemos con el método OpenRecordset que estamos estudiando. El objeto Recordset se abre desde un objeto DataBase (que es lo normal). Pero si acude a la información de VB verá que también se puede abrir desde un TableDef, un QueryDef y desde otro Recordset. No se complique la vida. Abra directamente los Recordsets desde la base de datos. A lo mejor, tenemos oportunidad de ver que también se puede abrir desde otro recordset, pero que en este caso solamente podemos abrirlo para cambiar alguna de sus propiedades. Como cualquier objeto DAO, debemos declararlo como variable tipo objeto. Public Mirecordset As Recordset Una vez declarado, para abrirlo basta con ejecutar la sentencia : Set Mirecordset = base_datos.OpenRecordset (origen[, tipo[, opciones]]) Al crear un nuevo objeto Recordset se agrega automáticamente a la colección Recordsets.
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 26
base_datos es el nombre del objeto Database que va a crear el recordset. (La base de datos que acaba de abrir) Origen en la primera expresión es una variable de tipo String que especifica el origen de los registros del nuevo objeto Recordset. El origen puede ser un nombre de tabla, un nombre de consulta o una instrucción SQL que devuelva registros. En el caso de los objetos Recordset de tipo tabla, el origen sólo puede ser un nombre de tabla. Tipo es el tipo de Recordset que se quiere crear. Si no se especifica un tipo, OpenRecordset creará un objeto Recordset de tipo tabla cuando sea posible. (Cuando especifica como Origen el nombre de una Tabla) Si se especifica una consulta o una tabla adjunta, OpenRecordset creará un objeto Recordset de tipo hoja de respuestas dinámica. El tipo del nuevo objeto Recordset se define mediante una de las siguientes constantes : dbOpenTable para abrir un objeto Recordset de tipo tabla. dbOpenDynaset para abrir un objeto Recordset de tipo hoja de respuestas dinámica. dbOpenSnapshot para abrir un objeto Recordset de tipo instantánea. El parámetro opciones permite especificar las características del nuevo objeto Recordset tales como las restricciones de edición y consulta para otros usuarios. Vea la Ayuda de VB para mayor detalle. Los objetos Recordset se eliminan automáticamente de la colección Recordsets al cerrarlos con el método Close. También se eliminan automáticamente cuando creamos otro recordset con el mismo nombre. Ejemplo de creación de un Objeto Recordset Decíamos que se puede crear un Recordset con la sentencia : Recuerde la Fórmula general Set DAOinf = DAOsup.Método ( - - - - - - - ) Set Mirecordset = base_datos.OpenRecordset (origen[, tipo[, opciones]]) Si tenemos abierta una base de datos llamada MiBaseDatos, podemos crear el objeto MiRecordset eligiendo de la tabla MiTabla de esa base de datos los campos Campo1, Campo2 y Campo3, y que sea del tipo de hoja de respuestas dinámica, de la siguiente forma : Set Mirecordset = MiBaseDatos.OpenRecordset (“SELECT Campo1, Campo2, Campo3 _ FROM MiTabla”, dbOpenDynaset) Si deseamos que el Recordset contenga todos los campos de esa misma tabla : Set Mirecordset=MiBaseDatos.OpenRecordset (“SELECT * FROM MiTabla”, dbOpenDynaset) o simplemente sin utilizar la sentencia SQL : Set Mirecordset = MiBaseDatos.OpenRecordset (“MiTabla”, dbOpenDynaset) En los ejemplos anteriores no se ha establecido ningún parámetro en Opciones. Veamos lo que decíamos antes. Crear un Recordset desde otro recordset. Se puede usar solamente para variar sus propiedades. Si desde el Recordset anterior, queremos crear un nuevo Recordset denominado MiRecordset1, que tenga la condición de que sea solo lectura, usaremos la sentencia : Set Mirecordset1 = Mirecordset.OpenRecordset (dbReadOnly) Este nuevo Recordset contendrá los mismos campos que el Recordset origen, pero no podremos cambiar datos en él.
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 27
Pueden crearse tantos Recordsets como se necesiten. Estos Recordsets pueden tener campos comunes. Es más, podríamos crear dos Recordsets exactamente iguales. Pero en la mayoría de los casos, necesitaremos crear un Recordset donde se elijan varios campos de una o varias tablas, seleccionando de esos campos unos determinados valores. Por ejemplo, en una base con las direcciones de los clientes, a lo mejor queremos seleccionar todas aquellas direcciones en las cuales el código postal sea el 28700 (San Sebastián de los Reyes). Imaginemos que hemos abierto la base de datos con el nombre CLIENTES (Recuerde que este es el nombre del objeto DAO usado para abrir la B.D., no el nombre que pueda tener esa B.D. en el disco), y esta base de datos tiene una tabla llamada DIRECCIONES (Este sí es el nombre real de la tabla dentro de la B.D.) Vamos a abrir un Recordset con todos los clientes de San Sebastián de los Reyes : Set Mirecordset2 = CLIENTES.OpenRecordset (“SELECT * FROM DIRECCIONES WHERE COD_POSTAL = 28700“, dbOpenDynaset) El Recordset Mirecordset2 contiene todos los campos de todos los registros de la tabla DIRECCIONES que cumplan la condición de que el código postal (campo COD_POSTAL en el ejemplo) sea igual a 28700. Observe en esta y anteriores expresiones, que la sentencia SQL está entre doble comilla. Podemos introducir cualquier sentencia SQL para determinar qué registros introducimos en el Recordset. Por ejemplo, si queremos seleccionar todos los clientes de Madrid (su código postal comenzará necesariamente por 28 y le seguirán tres cifras) : Set Mirecordset2 = CLIENTES.OpenRecordset (“SELECT * FROM DIRECCIONES WHERE COD_POSTAL LIKE 28???“, dbOpenDynaset)
Tipo de recordset más práctico ¿Dynaset, Table? A la hora de crear un recordset podemos pensar que tipo es el más adecuado. Todo dependerá de lo que necesitemos de nuestro recordset y de cómo nos queramos mover por él. Cuando decimos movernos por él queremos decir cambiar de un registro a otro, buscar registros, etc. Vamos a prescindir del recordset tipo Snapshot si lo que queremos es leer y escribir datos. Un recordset Snapshot solamente sirve para realizar informes (leer) de los datos en un instante determinado. Veamos la elección entre Dynaset y Table Si queremos seleccionar parte de los registros de una tabla, o ver registros de varias tablas al mismo tiempo (lo que podemos ver en Access en una consulta), debemos elegir directamente el tipo Dynaset, ya que el tipo Table debe contener TODOS los registros de una UNICA TABLA. Si estamos en ese caso es el único en el que tendremos dudas respecto al tipo elegido. Si creamos un recordset tipo Table se nos puede complicar un poco el código a la hora de movernos a lo largo del recordset, ya que no podemos usar ciertos métodos como los Find (FindFirst, FindLast, etc.) debiendo utilizar para realizar la misma función los métodos Move (MoveFirst, MoveLast, MovePrevious, MoveNext), o el método Seek, un poco más complicado y que exige un índice. (Lo que ganamos con la complicación del Seek es velocidad). Los desplazamientos a lo largo de un recordset tipo tabla son mucho más rápidos que sobre un recordset tipo Dynaset. Esa rapidez se nota fundamentalmente cuando va a manejar miles de registros, en cuyo caso es indispensable usar recordsets tipo tabla y moverse sobre un índice. Si no va a manejar miles de registros, no apreciará mucha diferencia entre uno y otro. Y si usa Dynaset dispone de más recursos, sobre todo de búsqueda. Veremos casos de uno y otro tipo.
Métodos y Propiedades del recordset que va a necesitar inmediatamente Propiedad RecordCount
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 28
Devuelve el número de registros accedidos en un objeto Recordset. El valor de esta propiedad es de sólo lectura Sintaxis
NombredeMiRecordset.RecordCount
Donde NombredeMiRecordset es un objeto Recordset. El valor devuelto por la propiedad RecordCount es un dato numérico Long (NombredeMiRecordset puede ser del tipo Dynaset, Snapshot Table. Pero en el caso de que sea Dynaset se va a encontrar con una sorpresa. Si le pide ese dato a un recordset tipo Dynaset recién creado, le devolverá el valor 1. ¡Parece que solamente tiene un registro! No es así. Un recordset tipo Dynaset tiene la ventaja (y el inconveniente) de que solamente guarda en memoria un registro. Por lo tanto no sabe cuantos registros tiene realmente hasta que no los recorre todos. Para que el método RecordCount le devuelva el número de registros existentes, tiene que acceder previamente al primero y al último. Para ello basta con ejecutar estas dos líneas de código: NombredeMiRecordset.MoveLast NombredeMiRecordset.MoveFirst A partir de ese momento, ya le indicará el número correcto de registros existentes. Método AddNew Crea un nuevo registro en un objeto Recordset de tipo Table o Dynaset. Sintaxis
MiRecordset.AddNew
El método AddNew crea un nuevo registro donde puede introducir nuevos datos, y posteriormente agregarlo al conjunto de registros del objeto Recordset. Este método establece en los campos el valor Null (predeterminado para los objetos Recordset de tipo tabla) o los valores predeterminados, si existen. El registro creado queda en la memoria, y ahí se puede modificar simplemente asignando a cada campo el valor deseado. Para asignar un valor a un campo simplemente tenemos que poner la expresión : MiRecordset ! MiCampo1 = “Dato tipo string” MiRecordset ! MiCampo2 = Dato tipo numérico Una vez que se hayan introducido los datos en el nuevo registro, debe utilizar el método Update para guardar los cambios y agregarlo al conjunto de registros. No se modificará la base de datos hasta que se utilice el método Update. La posición del nuevo registro depende del tipo de objeto Recordset: En un objeto Recordset de tipo hoja de respuestas dinámica, (Dynaset) los registros se insertan al final del conjunto, independientemente de las reglas de ordenación que pueda haber en vigor al abrir el conjunto de registros. En un objeto Recordset de tipo tabla cuya propiedad Index esté definida, los registros se agregan en el lugar correspondiente al orden. Si no se ha establecido la propiedad Index, los nuevos registros se agregan al final del conjunto. El registro que era actual antes de utilizar el método AddNew continúa siéndolo después. Esto puede comprobarlo asignando a un Label el contenido de un campo, añadir un registro con un valor para ese campo distinto al que está presente en el Label y comprobar que el contenido del Label no se ve afectado por haber introducido un registro nuevo. Si desea hacer que el nuevo registro sea el actual, puede establecer en la propiedad Bookmark el marcador identificado por el valor de la propiedad LastModified. En la práctica anterior observará tras este proceso que se cambia el contenido del Label al nuevo valor. Vea la propiedad LastModified un poco mas adelante.
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 29
Método Edit Copia el registro actual de un objeto Recordset de tipo hoja de respuestas dinámica o tabla en el búfer de copia para su edición. Sintaxis
MiRecordset.Edit
Donde MiRecordset representa el nombre de un objeto Recordset abierto y actualizable que contiene el registro a editar. Una vez invocado el método Edit, los cambios efectuados en los campos del registro actual se copian en el búfer de copia. Al terminar de realizar los cambios deseados, utilice el método Update para guardarlos. Como en el caso del método AddNew este registro modificado está en la memoria y es necesario introducirlo en la BD. El registro actual después de utilizar Edit es precisamente el registro que acabamos de editar. Para poder usar Edit debe existir un registro actual. Si no es así o si MiRecordset no se refiere a un objeto Recordset de tipo tabla u hoja de respuestas dinámica, a un objeto Table o a un objeto Dynaset abierto, se producirá un error. El uso de Edit producirá un error en las condiciones siguientes: No hay registro actual. La base de datos o el conjunto de registros es de sólo lectura. Ningún campo del registro es actualizable. Otro usuario ha abierto la base de datos o el conjunto de registros para uso exclusivo. Otro usuario ha bloqueado la página que contiene el registro. Una vez añadido el registro, o cambiados los datos de un registro, debemos utilizar el Método Update para guardar los datos en la BD.
Método Update Guarda el contenido del búfer de copia en un objeto Recordset de tipo hoja de respuestas dinámica o tabla especificado. Es decir, mete en la Base de Datos el contenido del registro que estaba en la memoria, bien por haber utilizado el método Update, bien por haber utilizado el método Edit. Sintaxis
MiRecordset.Update
Donde MiRecordset representa el nombre de un objeto Recordset de tipo hoja de respuestas dinámica o tabla, abierto y actualizable. Los cambios en el registro actual se perderán en las siguientes situaciones: Uso del método Edit o AddNew y desplazamiento a otro registro sin utilizar antes Update. Uso de Edit o AddNew y utilización de nuevo de Edit o AddNew sin especificar antes Update. Establecimiento de otro registro en la propiedad Bookmark. Cierre del conjunto de registros indicado en MiRecordset sin utilizar antes Update. Cada vez que se crea o edita un registro se cambia el valor de la propiedad LastModified, que tomará el marcador de ese registro creado o editado. Método CancelUpdate Cancela todas las actualizaciones pendientes del objeto Recordset.
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 30
Sintaxis
recordset.CancelUpdateTipo
Tipo puede tomar los siguientes valores dbUpdateRegular dbUpdateBatch Comentarios
Cancela los cambios pendientes que no están en la memoria caché. Cancela los cambios pendientes en la memoria cache actualizada.
El método CancelUpdate cancela todas las actualizaciones pendientes a causa de una operación Edit o AddNew. La utilización del método CancelUpdate tiene el mismo efecto que moverse a otro registro sin utilizar el método Update, salvo que el registro activo no cambia y algunas propiedades, como BOF y EOF, no se actualizan. Método Delete Este método elimina el registro actual de un objeto Recordset de tipo hoja de respuestas dinámica o tabla. Para eliminar un registro, debe haber un registro actual en el Recordset antes de utilizar Delete, pues de lo contrario se producirá un error interceptable. Una vez eliminado, este registro sigue siendo el registro actual. Puede observar, que si a continuación de Delete utiliza AbsolutePosition para conocer en que registro está, la respuesta será -1, prueba de que está sobre un registro inexistente. Propiedad Bookmark Devuelve o establece un marcador que identifica de forma única el registro actual de un objeto Recordset o define el registro actual de un Recordset como marcador válido. Esto merece una pequeña aclaración. Bookmark en inglés significa ese papel que introducimos en un libro para saber en qué página hemos dejado la lectura. En Visual Basic, significa el registro en el que estamos actualmente (registro actual) Podemos conocer en que registro estamos mediante la siguiente expresión: Variable = MiRecordset.Bookmark Pero tenemos que tener en cuenta que Variable es una variable tipo String (Sí, string, aunque parezca que para conocer la posición de un registro debería ser un numérico, pero es así). Por lo tanto deberíamos haber declarado la variable previamente como una variable tipo String Dim Variable As String Pero esta propiedad sirve para colocarnos en el registro que deseemos. Eso sí, previamente deberíamos haber obtenido el Bookmark de ese registro. Imagínese que se está moviendo a lo largo del recordset y hemos visto un registro donde tenemos un dato importante (por ejemplo, un máximo del valor de un campo) No sabemos si habrá otro registro que tenga un valor mayor que este. Deberemos seguir buscando, pero antes anotamos el Bookmark de ese registro Variable = MiRecordset.Bookmark Seguimos buscando moviéndonos por todo el recordset, y comprobamos que no hay otro registro con un valor mayor. Queremos volver a aquel registro. Para ello forzamos a que el registro cuyo Bookmark sea igual a Variable se convierta en registro actual: MiRecordset.BookMark = Variable Y se colocará en el registro deseado. Solamente se puede ver la propiedad Bookmark en aquellos recordsets que tengan la propiedad Bookmarkable a True. En un Recordset basado completamente en tablas del motor de base de datos Microsoft Jet, el valor de la propiedad Bookmarkable es True y
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 31
pueden usarse marcadores. Sin embargo, otros productos de bases de datos pueden no aceptar los marcadores. Por ejemplo, no se pueden usar marcadores en un Recordset basado en una tabla anexa Paradox que no tiene clave principal. La propiedad Bookmark se almacena internamente como matriz de Byte. Por esta razón, si se intenta usar la propiedad Bookmark en una operación de comparación, se producirá un error interceptable. Antes de tener acceso a la propiedad Bookmark, copie los valores de los marcadores a variables cadena y efectúe las comparaciones usando dichas variables cadena. Por ejemplo, el siguiente código compara marcadores en dos objetos Recordset: Dim Marca1 as String, Marca2 as String Dim Rs1 as Recordset, Rs2 as Recordset Set Rs1 = Db.OpenRecordset("Títulos") Set Rs2 = Rs1.Clone() Marca1 = Rs1.Bookmark Marca2 = Rs2.Bookmark If Marca1 = Marca2 Then Print "Esta comparación es válida " No intente realizar la siguiente comparación, aunque a primera vista parezca igual : If Rs1.Bookmark = Rs2.Bookmark Then ..... Porque dará error. No hay límite en el número de marcadores que pueden establecerse. Para crear un marcador para otro registro distinto del registro actual, muévase al registro deseado y asigne el valor de la propiedad Bookmark a una variable String que identificará el registro. Para asegurarse de que el Recordset acepta marcadores, inspeccione el valor de su propiedad Bookmarkable antes de usar la propiedad Bookmark. Si Bookmarkable es False, el Recordset no acepta marcadores, y el uso de la propiedad Bookmark produce un error interceptable. Si la propiedad Bookmark se establece a un valor que corresponda a un registro eliminado, se produce un error interceptable. Propiedad LastModified Devuelve un marcador que indica el registro más recientemente agregado o modificado. Sintaxis
NombreRecordset.LastModified
El valor devuelto por esta propiedad es un tipo de datos Variant o String. (Similar al devuelto por Bookmark) LastModified se puede usar para colocarse en el registro más recientemente agregado o actualizado. Esta propiedad puede usarse para volver al último registro que ha sido modificado. Basta para ello igualar la propiedad Bookmark a la propiedad LastModified : NombreRecordset.Bookmark = NombreRecordset.LastModified
Métodos MoveFirst, MoveLast, MoveNext, MovePrevious Estos métodos son aplicables a todos los tipos de recordset. Se sitúan en el primer, el último, el siguiente o el anterior registro del objeto Recordset especificado y lo convierten en el registro actual. Sintaxis
MiRecordset.{MoveFirst | MoveLast | MoveNext | MovePrevious}
Puede utilizar los métodos Move para desplazarse de un registro a otro sin aplicar una condición.
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 32
Al abrir el conjunto de registros indicado en Recordset, el primer registro pasa a ser el registro actual y en la propiedad BOF se establece False. Si el conjunto no contiene ningún registro, se establecerá en BOF el valor True y no habrá registro actual. Si el primer o el último registro ya es el actual al utilizar MoveFirst o MoveLast, el registro actual no varía. Si utiliza MovePrevious cuando el registro actual sea el primero, en la propiedad BOF se establecerá True y no habrá registro actual. Si utiliza de nuevo MovePrevious, se producirá un error y BOF continuará con el valor True. Si utiliza MoveNext cuando el registro actual sea el último, en la propiedad EOF se establecerá True y no habrá registro actual. Si utiliza de nuevo MoveNext, se producirá un error y EOF continuará con el valor True. Si Recordset hace referencia a un objeto Recordset de tipo tabla o a un objeto Table, el movimiento se hará según el índice actual de la tabla. Para establecer el índice actual puede usar la propiedad Index. Si no establece un índice actual, el orden de los registros devueltos no estará definido. Si utiliza MoveLast en un objeto Recordset basado en una consulta SQL o QueryDef, se forzará la terminación de la consulta, poblando completamente el objeto Recordset. No es posible utilizar los métodos MoveFirst ni MovePrevious en los Recordset tipo snapshot de desplazamiento hacia delante. Para desplazar la posición del registro actual en un objeto Recordset un número de registros determinado hacia adelante o hacia atrás, utilice el método Move.
Método Move Desplaza la posición del registro actual en un objeto Recordset. Sintaxis
MiRecordset.Move filas[, inicio]
Donde : filas es un valor de tipo Long con signo que especifica el número de filas (de registros) que se desplaza la posición. Si filas es mayor que 0, la posición se desplaza hacia adelante (hacia el final del archivo). Si es menor que 0, la posición se desplaza hacia atrás (hacia el principio del archivo). Inicio (opcional) es un valor de tipo String que identifica un marcador. Si se especifica inicio, el desplazamiento será relativo al marcador indicado. Si se omite, Move comenzará por el registro actual. El marcador que debe utilizarse para definir el registro Inicio debe ser un Bookmark o similar (LastModified, por ejemplo) Si se especifica una posición anterior al primer registro, la posición del registro actual se situará al principio del archivo (BOF). Si se especifica una posición posterior al último registro, la posición del registro actual se situará al final del archivo (EOF). Si el objeto Recordset no contiene registros y el valor de su propiedad BOF es True, el uso de este método para desplazarse hacia atrás producirá un error interceptable en tiempo de ejecución. Lo mismo ocurrirá si el valor de la propiedad EOF es True y pretende desplazarse
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 33
hacia adelante. Si las propiedades BOF o EOF tienen el valor True y se intenta usar el método Move sin un marcador válido, se generará un error interceptable. Si el objeto Recordset está basado en una consulta, la operación forzará la ejecución de la consulta en el número de filas especificado..
Métodos FindFirst, FindLast, FindNext, FindPrevious Estos métodos no se pueden aplicar a un recordset tipo Tabla Buscan el primer, el último, el siguiente o el anterior registro de un objeto Recordset de tipo instantánea u hoja de respuestas dinámica, que satisfaga el criterio especificado y lo convierte en el registro actual. Sintaxis
MiRecordset.{FindFirst | FindLast | FindNext | FindPrevious} criterio
MiRecordset es el nombre de un objeto Recordset. Criterio es una expresión de cadena (como la cláusula WHERE de una instrucción SQL, sin la palabra WHERE) que se utiliza para buscar un registro. Si no se encuentra ningún registro que satisfaga el criterio, el puntero de registro actual se situará en el primer registro del objeto Recordset y se establecerá en la propiedad NoMatch el valor True. Si Recordset contiene más de un registro que satisfaga el criterio, FindFirst hallará el primero de ellos, FindNext el siguiente y así sucesivamente. La propiedad NoMatch tomará en este caso el valor False. Compruebe siempre el valor de la propiedad NoMatch para determinar si la operación de búsqueda ha tenido éxito. Si la búsqueda ha tenido éxito, NoMatch se establece a False. Si ha fracasado, NoMatch se establece a True y el registro actual pasa a ser el primero del objeto Recordset. Por ejemplo: Dim Estabaaqui as String Estabaaqui = Recordset.Bookmark Recordset.FindFirst "Nombre = 'Luis' "
If Recordset.NoMatch = True Then Recordset.Bookmark = Estabaaqui Else . .
' Busca un nombre. Recuerde siempre las comillas dobles para la expresión de búsqueda y las comillas simples si se trata de un dato string. 'Si no se ha encontrado ' Si no se ha encontrado, vuelve al que era el ‘registro actual. ' Sí se ha encontrado.
Aquí las instrucciones adecuadas End If
No es posible utilizar estos métodos en un objeto Recordset de tipo snapshot de desplazamiento hacia delante. Al buscar campos que contengan fechas, deberá utilizar el formato de fecha de los Estados Unidos (mes-día-año), incluso cuando no utilice la versión para este país del motor de base de datos Jet, pues de lo contrario es posible que no se encuentren los datos buscados. Puede utilizar la función Format para convertir la fecha. Por ejemplo: Mirecordset.FindFirst "fecha > #" & Format(mifecha, "mm/dd/yyyy" ) & "#"
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 34
Observe que las fechas, aparte de ponerlas en americano, hay que presentarlas entre almohadillas (#). Observe lo dicho mas atrás para las comillas dobles en la expresión de búsqueda.
Método Seek Este método solo se puede usar con recordsets tipo Tabla Si no podemos usar los métodos Find en un recordset tipo Tabla, ¿Qué podemos hacer para buscar un dato en un recordset de este tipo? Usar el método Seek El método Seek busca el primer registro de un objeto Recordset indexado de tipo Table que cumple el criterio especificado para el índice activo y lo convierte en el registro activo. Sólo funciona en espacios de trabajo Microsoft Jet. Sintaxis
MiRecordset.Seek comparación, clave1, clave2...clave13
Donde MiRecordset es un recordset de tipo Table que tiene definido un índice en el campo por el que se va a realizar la búsqueda. Como podemos tener varios índices en una tabla, deberemos indicarle cual es el índice de búsqueda. Una vez que se lo indiquemos, ese índice será el Indice activo. comparación Es una de esta expresiones de cadena: <, <=, =, >=, >. clave1, clave2...clave13 Son uno o más valores que corresponden a los campos en el índice activo del objeto Recordset. Puede utilizar un argumento de hasta 13 claves. Antes de usar Seek se debe establecer el índice activo. Todo índice tiene un nombre. Habíamos visto cuando creábamos un índice que debía tener un nombre. Recuerde el ejemplo: Set MiIndice2 = MiTabla2.CreateIndex("IndicePeliculas") Puede ver el nombre de ese índice en la Fig. 20.7 En este caso habíamos creado el índice mediante código y hemos podido controlar su nombre. Si lo hubiésemos creado directamente en Access, el nombre que le pone por defecto es el mismo que el nombre del campo. Ese nombre del índice es el que debemos usar para crear el índice activo. Por ejemplo, si quisiéramos que el índice activo fuese el IndicePeliculas lo haríamos índice activo mediante la siguiente instrucción: MiRecordset.Index = "IndicePelículas" A partir de ahora, el campo (o campos) de ese índice será sobre el que realizaremos la búsqueda mediante Seek. Para encontrar el registro que tenga por valor 00000012 usaremos la expresión MiRecordset.Seek "=", "00000012" Ese registro será ahora el registro actual. Si hubiese mas de un registro con ese valor, el registro actual será el primero que cumpla esa condición En el ejemplo hemos utilizado el comparador = para buscar un registro cuyo valor en el campo indicado por el índice activo sea igual al indicado en el siguiente parámetro (00000012). Si quisiésemos encontrar un registro cuyo valor sea superior a 00000012 usaríamos la expresión MiRecordset.Seek ">", "00000012" Pero observe ahora que el 0000012 no es un número, es una cadena de caracteres. No se preocupe. Seek puede comparar el contenido de un campo numérico, de un campo texto, de
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 35
un campo Fecha/Hora, etc. Eso sí, debe compararlo con un valor del mismo tipo, es decir, si el contenido del campo es numérico, en el parámetro Clave1 deberemos pasarle un campo numérico, si el campo es texto, deberemos pasarle un dato tipo texto, etc. El método Seek busca en los campos clave especificados y localiza el primer registro que cumpla el criterio especificado por comparación y clave1. Cuando lo encuentra, convierte ese registro en activo y la propiedad NoMatch se establece en False. Si el método Seek no consigue localizar ninguna coincidencia, la propiedad NoMatch se establece en True y el registro activo es indefinido. Si comparación es igual (=),mayor o igual (>=) o mayor que (>), Seek empezará al principio del índice y buscará hacia adelante. Si comparación es menor que (<) o mayor o igual que (<=), Seek empezará al final del índice y buscará hacia atrás, a menos que haya entradas de índice duplicadas al final. En tal caso, Seek empezará en una entrada cualquiera entre las entradas duplicadas existentes al final del índice. Debe especificar valores para todos los campos definidos en el índice. Si utiliza Seek con un índice de múltiples columnas y no especifica un valor de comparación para cada campo del índice, no podrá usar el operador de igual (=) en la comparación. Esto se debe a que algunos de los campos de criterio(clave2, clave3, etc) estarán predeterminados en Null, lo que posiblemente no concordará. Por tanto, el operador de igual sólo funcionará correctamente si tiene un registro que sea Null en su totalidad, excepto la clave que está buscando. Es aconsejable usar el operador mayor que o igual en su lugar. En el ejemplo siguiente se toman los nombres de las calles y otros datos de una tabla llamada Calles_Nombre, cuyo campo NombreVia esta indexado. El índice tiene el mismo nombre que el campo porque se creó directamente con Access. El procedimiento BBuscaCalle_Click busca la primera calle cuyo nombre coincida con las letras tecleadas en el TextBox TBBuscaCalle Set BaseDatos = OpenDatabase("C:\Callejero\Calles.mdb") Set RsCalles = BaseDatos.OpenRecordset("Calles_Nombre", dbOpenTable) Private Sub BBuscaCalle_Click() RsCalles.Index = "NombreVia" RsCalles.Seek ">=", TBBuscaCalle If RsCalles.NoMatch = False Then TBNombreCalle = RsCalles!nombrevia Else TBNombreCalle = "No se encontró la calle" End If End Sub
Método Clone En muchas ocasiones es necesario crear un Recordset que sea copia exacta de otro. Las ocasiones en las que es necesario hacer esto pueden ser variadas, pero vamos a destacar una : crear un Recordset idéntico al Recordset de un control Data, para trabajarlo con código durante una parte de la ejecución del programa. Otras aplicaciones pueden ser copiar el Recordset de otra máquina a través de la Red de Area Local, ... y donde podamos llegar con nuestra imaginación. La sintaxis de Clone es la siguiente : Sintaxis
Set Duplicado = Original.Clone
Donde Duplicado es una variable tipo Recordset, y Original es el Recordset que se va a duplicar.
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 36
Con el método Clone puede crear múltiples Recordsets. Cada uno de ellos puede tener su propio registro actual. El uso de Clone no modifica los datos de los registros. Puede modificar un registro desde cualquier Recordset, bien desde el que sirvió de original, bien desde cualquiera de sus copias, pero debe hacerlo invocando los métodos Edit - Update. Puede compartir marcadores entre dos o más Recordsets creados de esta forma. Puede utilizar el método Clone cuando desee realizar en un conjunto de registros una operación que requiera varios registros actuales. Este método es más rápido y eficiente que crear un nuevo Recordset. Inicialmente, un Recordset creado con Clone carece de registro actual. Para hacer que un registro sea el actual antes de utilizar el Recordset copia, puede utilizar cualquiera de los métodos Move, Find o Seek (solo para Recordsets tipo Tabla), o establecer su propiedad Bookmark El hecho de cerrar el Recordset original no afecta al duplicado y viceversa. Nota No es posible utilizar este método con snapshots de desplazamiento hacia delante (objetos Recordset de tipo instantánea con la opción dbForwardOnly activada).
Método Requery El método Requery actualiza los datos de un objeto Recordset, volviendo a ejecutar la consulta con la que se ha creado ese Recordset. Este método debe usarse cada vez que se sospeche que los datos de la Base de datos han cambiado, y se quieran presentar los datos actualizados. Es un método típico de una BD que se está usando desde varios puestos a través de una Red de Area Local. Sintaxis
NombreRecordset.Requery [NuevoQueryDef]
Donde NombreRecordset es el nombre del Recordset, y NuevoQueryDef (opcional) es una consulta almacenada No es posible utilizar el método Requery en objetos Recordset tipo Snapshot o en los Dynaset que tengan la propiedad Restartable a False, ni tampoco en los objetos Recordset de tipo Tabla. Si los valores de las propiedades BOF y EOF del objeto Recordset son ambos True después de utilizar el método Requery, la consulta no habrá devuelto ningún registro y el objeto Recordset no contendrá datos.
Transacciones Métodos BeginTrans, CommitTrans y Rollback Estos métodos son métodos del Objeto Workspace Veamos estos tres métodos que, dadas sus funciones, deben estudiarse conjuntamente. Supongamos un Banco. Debe hacer una transferencia entre dos cuentas corrientes que están en la misma base de datos. La operación es sencilla : Busca la cuenta origen y crea un nuevo registro. Apunta en el campo OPERACIÓN una T de Transferencia, en el campo IMPORTE apunta el valor del dinero a transferir, y en el campo SALDO pone la diferencia entre lo que había en ese campo en la última operación, menos el importe del dinero transferido. A continuación hace un proceso similar con la cuenta destino, pero en este caso, sumándole el importe de la transferencia. No hay problemas. Pero que pasa si, una vez sacado el dinero de
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 37
la cuenta origen, no se puede ingresar en la cuenta destino, por la razón que sea (cuenta bloqueada, no existe esa cuenta, fallo de la red de área local) Obviamente la operación no se ha completado, y hay que devolver el dinero a la cuenta origen. Podría hacerse un apunte, metiendo la misma cantidad de dinero que se ha extraído anteriormente, y su saldo no se verá afectado. Pero no sé lo que pensaría el cliente cuando vea un estadillo de su cuenta, en la que le han sacado una cantidad de dinero, aunque en el siguiente apunte se lo hayan vuelto a introducir. Para evitar estas situaciones usamos lo que se denomina una Transacción, que es una combinación de estos tres métodos. Con el método BeginTrans iniciamos la Transacción. Con CommitTrans terminamos la transacción y se guardan los cambios realizados (En ambas cuentas a la vez, en el caso del ejemplo). Con Rollback se termina la transacción sin llegar a guardar los cambios, quedando el Objeto Workspace afectado por las operaciones internas a esa transacción tal y como estaba antes de comenzar dicha operación. Dado que una transacción pertenece a un Workspace, deberemos aplicar estos métodos al Workspace que ese usuario tenga abierto. Es decir, en un sistema con varios usuarios que están trabajando simultáneamente sobre una Base de Datos, un determinado usuario deberá entrar con un Workspace propio (una sesión de trabajo solo para él). En estas condiciones podemos crear una transacción. Y aquí comenzamos a ver la necesidad de crear Workspaces distintos para distintos usuarios. Veremos un poco más adelante como se crean los Workspaces. Sintaxis
MiSesión.BeginTrans MiSesión.CommitTrans MiSesión.Rollback
Dentro de un objeto Workspace, las transacciones son siempre globales y no se limitan sólo a la base de datos o al conjunto de registros. Si realiza operaciones en más de una base de datos o conjunto de registros durante una transacción en un objeto Workspace, el método Rollback deshará todas las operaciones en todos ellos. Quiere esto decir que una transacción debe iniciarse al comenzar una determinada operación, realizar esa operación sin realizar ninguna otra durante ese tiempo, terminar la operación y finalizar la transacción, bien con CommitTrans o con Rollback. Si desea tener transacciones simultáneas, lo mas indicado es crear varios objetos Workspace para usar uno con cada transacción. Puede anidar transacciones. Es posible tener hasta cinco niveles de transacciones abiertos a un tiempo en un mismo objeto Workspace utilizando múltiples combinaciones anidadas de BeginTrans y CommitTrans o Rollback. En este caso, el orden de finalización de una transacción debe ser siempre de menor a mayor nivel jerárquico, es decir, se deberá cerrar primero la transacción que esté mas interior dentro del anidamiento, y así sucesivamente. Si cierra una transacción anidada mediante CommitTrans, y posteriormente cierra una transacción que abarque a esta última con Rollback, los cambios de la primera transacción NO quedarán guardados. (Cuando se utilizan bases de datos SQL ODBC externas no es posible anidar las transacciones). Si cierra un objeto Workspace sin guardar o deshacer las transacciones pendientes, éstas se desharán automáticamente. Algunas bases de datos pueden no admitir las transacciones. En este caso la propiedad Transactions del objeto Database o Recordset tendrá el valor False. Lea detenidamente la Ayuda de estos métodos antes de trabajar con ellos. El hecho de usar transacciones, aparte de lo que significa para asegurar la integridad de los datos, ahorra accesos al disco (Importantísimo en algunas redes LAN y WAN), ya que los cambios a introducir se van almacenando en un búfer en la memoria, y se vuelcan al disco solamente en el momento de terminar la transacción de modo afirmativo con CommitTrans.
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 38
La protección de accesos a una base de datos Access. Usuarios, Workspaces, Grupos de Trabajo, Grupos de Usuarios. La base de datos de sistema. Acabamos de ver que es necesario poder crear Workspaces para cada usuario. Hasta ahora, y para no complicar el estudio de las bases de datos, habíamos usado solamente el Workspaces(0) que VB crea automáticamente. Vamos a entrar ahora en la creación de Usuarios (Users) y Workspaces. El método para crear Workspaces es el CreateWorkspace, método del dbEngine. Comencemos con una mirada a este objeto desde el punto de vista de DAO. No olvide que el dbEngine es el motor de bases de datos Jet.
El dbEngine. Visión desde DAO El objeto dbEngine es el objeto de nivel más alto en el modelo de objeto DAO. Es el encargado de contener y controlar todos los objetos DAO. Hay un solo objeto dbEngine, inherente a la aplicación, y no se pueden crear más. Tiene como cualquier objeto DAO sus propiedades y métodos. Vamos a ver alguna propiedad, en orden ascendente de importancia práctica. Propiedad Version Devuelve la versión actual de DAO en uso. El tipo de datos es String (p.e., "3.5" ) Propiedad DefaultType Establece o devuelve un valor que indica qué tipo de espacio de trabajo (Microsoft Jet u ODBCDirect) utilizará el próximo objeto Workspace que se cree. Puede tomar los valores: dbUseJet dbUseODBC
Crea objetos Workspace conectados al motor de base de datos Microsoft Jet. Crea objetos Workspace conectados a un origen de datos ODBC.
Propiedad DefaultUser. Establece el nombre de usuario utilizado para crear el Workspace predeterminado cuando se inicializa. Propiedad DefaultPassword. Establece la contraseña utilizada para crear el Workspace predeterminado cuando se inicializa. El DefaultUser es un tipo de datos String, que puede tener entre 1 y 20 caracteres de longitud en espacios de trabajo Microsoft Jet y cualquier longitud en espacios de trabajo ODBCDirect. El DefaultPassword es un tipo de datos String que puede tener hasta 14 caracteres de longitud en bases de datos Microsoft Jet y cualquier longitud en conexiones ODBCDirect. Puede contener cualquier carácter excepto 0 ASCII. De modo predeterminado, la propiedad DefaultUser se establece a "administrador" y la propiedad DefaultPassword se establece a una cadena de longitud cero (""). Para que estas propiedades tenga efecto, debe establecerla antes de llamar a cualquier método DAO. Propiedad LoginTimeout. Establece o devuelve el número de segundos que se esperará antes de que se genere un error cuando se intenta conectar a una base de datos de ODBC. Tiene como valor predeterminado, 20 segundos. Propiedad IniPath. Devuelve la ubicación de la información de Registro de Windows de Microsoft Jet. El método SetOption le permite sobrescribir los valores del Registro de Windows para el motor de base de datos Microsoft Jet. (Recuerde que cualquier operación indebida sobre el registro de Windows puede hacerle perder toda la información de su PC)
Propiedad SystemDB
LSB
(Muy importante)
Visual Basic - Guía del Estudiante
Capítulo 12
Página 39
Establece o devuelve la ruta de acceso del archivo de información del grupo de trabajo (sólo espacios de trabajo Microsoft Jet). Este archivo es una base de datos Access, que normalmente se llama System.Mdw (Observe que la extensión es distinta a la .Mdb a la que nos tiene acostumbrados Access) Access usa este fichero y lo guarda generalmente en C:\Windows\System. Pero puede copiar esta base con otro nombre, y colocarla en el directorio que quiera. Lo único que tendrá que hacer es indicarle al dbEngine su nombre y ubicación mediante esta propiedad SystemDB En esa base de datos se guarda la información de los usuarios. Se guarda su nombre y su contraseña. Lógicamente no se puede leer con Access, aunque la abre como si se tratase de una base de datos ordinaria, pero cuando pretende abrir una tabla, no la visualiza. (NOTA. - La ayuda dice que el nombre predeterminado es System.Mda. Ese era el nombre para versiones antiguas del Motor Jet. En mi PC, con W98 y VB6, y con Access98 solamente encuentro System.Mdw)
Creación de nuevos usuarios Método CreateUser (Sólo espacios de trabajo Microsoft Jet). Crea un nuevo Usuario, e introduce sus datos en el archivo de información del grupo de trabajo (Base de Datos System.Mdw o la especificada en la propiedad SystemDB) El método CreateUser corresponde al Workspace o al Objeto Group. Para crear un nuevo usuario deberemos suministrar la siguiente información: Nombre del usuario, PID y contraseña. La sintaxis de CreateUser es: Set User = Objeto.CreateUser (Nombre, PID, contraseña) Donde User es la variable tipo objeto User que desea crear. Debe declararse como User) Objeto El nombre del Workspace (o del objeto objeto Group) que quiere utilizar para crear el nuevo objeto User. Nombre Nombre del nuevo usuario PID. Identificador de Usuario. Debe contener de 4 a 20 caracteres alfanuméricos Contraseñal. Contraseña para el nuevo usuario. La contraseña puede tener hasta 14 caracteres de longitud y puede incluir cualquier carácter excepto el carácter ASCII 0 Esto merece algún comentario. El nuevo usuario debe crearlo un Workspace (o un Group, pero de eso no nos vamos a ocupar por ahora) Lo normal es crear el nuevo usuario con el Workspaces(0) que como sabe, lo crea automáticamente Visual Basic. Recuerde que antes de crear un nuevo usuario, debe indicar donde está la base de datos con la información del systema, mediante la propiedad SystemDB DBEngine.SystemDB = "C:\Windows\System\System.Mdw") Con las siguientes instrucciones se crea un nuevo usuario Dim NuevoUser As User Set NuevoUser = Workspaces(0).CreateUser("Luis", "EsteesmiPID", "MiContraseña") Workspaces(0).Users.Append NuevoUser Podemos saber cuantos usuarios tiene el Workspaces(0) y su nombre. Con las siguientes instrucciones vamos a introducir los nombres de los usuarios en la lista ListUsers ListUsers.Clear For I = 0 To Workspaces(0).Users.Count - 1 NomUsuario = Workspaces(0).Users(I).Name ListUsers.AddItem NomUsuario Next I
Creación de nuevos Workspaces. Método CreateWorkspace
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 40
Podemos crear cuantos Workspaces necesitemos. Recuerde que un Workspace es una sesión de trabajo. Y una sesión de trabajo se abre para que trabaje un usuario. Por eso, a la hora de crear un Workspace debemos indicarle para que usuario, y la contraseña de ese usuario. Como el objeto Workspace pertenece al dbEngine, es este objeto el que debe crearlo. La sintaxis es la siguiente: Set NuevoWorkSpace = DBEngine.CreateWorkspace (Nombre, Usuario, Contraseña, Tipo) NuevoWorkSpace es el Workspace que queremos crear, que habremos declarado como variable tipo objeto Workspace. Nombre es el nombre del Workspace (P.e., MiSesion) Usuario Nombre de un usuario registrado en la base de datos del sistema (Que lo habremos creado con CreateUser) que será el propietario del nuevo objeto Workspace. Contraseña La contraseña del Usuario propietario del Workspace. Tipo (Opcional). Indica el tipo de espacio de trabajo. Puede tomar los valores dbUseJet para crear un espacio de trabajo Microsoft Jet, o dbUseODBC para crear un espacio de trabajo ODBCDirect. Si omite tipo, la propiedad DefaultType del objeto DBEngine determinará a qué tipo de origen de datos está conectado el Workspace No es necesario añadir el nuevo Workspace a la colección Workspaces Veamos un ejemplo de cómo crear un Workspace para el usuario Luis creado anteriormente: Dim NewWS as Workspace Set NewWS = DBEngine.CreateWorkspace("SesiondeLuis", "Luis", "MiContraseña") DBEngine.Workspaces.Append NewWS Podemos ver todos los Workspaces existentes. En las siguientes instrucciones podemos ver el código para listarlos en ListWS ListWS.Clear For I = 0 To DBEngine.Workspaces.Count - 1 NomWS = DBEngine.Workspaces(I).Name ListWS.AddItem NomWS Next I Debe tenerse en cuenta que el objeto Workspace es un objeto que solamente existe mientras está ejecutándose la aplicación. Cuando salimos de la aplicación, ese Workspace desaparece. No ocurre lo mismo con el Usuario, que queda en la base de datos del sistema, es decir, es un objeto persistente. Las partes de código expuestas se han sacado de un ejemplo creado para ver el número y nombre de los usuarios existentes, Workspaces y DataBases. Es un ejemplo para explicar estos conceptos. No tiene otra finalidad.
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 41
Fig. 20.9 Aspecto de la interface gráfica del ejercicio para crear Users y Workspaces Código de este ejercicio General/Declaraciones Option Explicit Dim VarDrag As String Dim NuevoWS() As Workspace Private Sub BAnadirUser_Click() On Error GoTo RutErr Dim NomUsuario As String, I As Integer If BAnadirUser.Caption = "Añadir" Then TBNuevoUserName.Visible = True TBNuvoUsePID.Visible = True TBNuevoUserPw.Visible = True LNUserName.Visible = True LNuevoUserPID.Visible = True LNuevoUserPw.Visible = True BAnularUser.Left = 3480 BAnularUser.Visible = True BBorrarUser.Visible = False BAnadirUser.Caption = "O.K." Exit Sub End If If BAnadirUser.Caption = "O.K." Then Dim NuevoUser As User Set NuevoUser = Workspaces(0).CreateUser(TBNuevoUserName, TBNuevoUserPw) Workspaces(0).Users.Append NuevoUser TBNuevoUserName.Visible = False TBNuvoUsePID.Visible = False TBNuevoUserPw.Visible = False LNUserName.Visible = False LNuevoUserPID.Visible = False LNuevoUserPw.Visible = False BAnularUser.Visible = False BBorrarUser.Visible = True
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
TBNuvoUsePID,
Página 42
BAnadirUser.Caption = "Añadir" ListUsers.Clear For I = 0 To Workspaces(0).Users.Count - 1 NomUsuario = Workspaces(0).Users(I).Name ListUsers.AddItem NomUsuario Next I BAnularUser.Visible = False BAnadirUser.Visible = True End If RutErr: If Err = 3304 Then MsgBox "Debe introducir el PID (Mínimo 5 caracteres, máximo 20 caracteres)" Exit Sub Else If Err > 0 Then MsgBox "Ha ocurrido el error " & Err & "." End If End Sub Private Sub BAnadirWs_Click() On Error GoTo RutErr If BAnadirWs.Caption = "Añadir" Then TBNuevoWorkspace.Visible = True TBNombreUser.Visible = True TBNuevoWSPw.Visible = True LNWSName.Visible = True LNombreUsuario.Visible = True LUserPw.Visible = True BAnularWS.Left = 5520 BAnularWS.Visible = True BEliminarWs.Visible = False BAnadirWs.Caption = "O.K." Exit Sub End If If BAnadirWs.Caption = "O.K." Then Dim NumWS As Integer NumWS = Workspaces.Count ReDim Preserve NuevoWS(NumWS) Dim I As Integer, NomWS As String Set NuevoWS(NumWS) = DBEngine.CreateWorkspace(TBNuevoWorkspace, TBNombreUser, TBNuevoWSPw) DBEngine.Workspaces.Append NuevoWS(NumWS) ListWS.Clear For I = 0 To DBEngine.Workspaces.Count - 1 NomWS = DBEngine.Workspaces(I).Name ListWS.AddItem NomWS Next I BAnadirWs.Caption = "Añadir" TBNuevoWorkspace = "" TBNombreUser = "" TBNuevoWSPw = "" TBNuevoWorkspace.Visible = False TBNombreUser.Visible = False TBNuevoWSPw.Visible = False LNWSName.Visible = False LNombreUsuario.Visible = False LUserPw.Visible = False BAnularWS.Visible = False BEliminarWs.Visible = True End If RutErr:
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 43
If Err = 3029 Then MsgBox "El Password usado no coincide con el de ese Usuario (User)" Exit Sub End If End Sub Private Sub BAnularUser_Click() If BAnadirUser.Caption = "O.K." Then TBNuevoUserName.Visible = False TBNuvoUsePID.Visible = False TBNuevoUserPw.Visible = False LNUserName.Visible = False LNuevoUserPID.Visible = False LNuevoUserPw.Visible = False BAnularUser.Visible = False BBorrarUser.Visible = True BAnadirUser.Caption = "Añadir" End If If BBorrarUser.Enabled = True Then BBorrarUser.Enabled = False BAnadirUser.Visible = True BAnularUser.Visible = False ListUsers.ListIndex = -1 End If End Sub Private Sub BAnularWS_Click() If BAnadirWs.Caption = "O.K." Then BAnadirWs.Caption = "Añadir" TBNuevoWorkspace = "" TBNombreUser = "" TBNuevoWSPw = "" TBNuevoWorkspace.Visible = False TBNombreUser.Visible = False TBNuevoWSPw.Visible = False LNWSName.Visible = False LNombreUsuario.Visible = False LUserPw.Visible = False BAnularWS.Visible = False BEliminarWs.Visible = True End If If BEliminarWs.Enabled = True Then BEliminarWs.Enabled = False BAnadirWs.Visible = True BAnularWS.Visible = False End If End Sub Private Sub BBorrarUser_Click() Dim NomUsuario As String, I As Integer Workspaces(0).Users.Delete ListUsers.Text ListUsers.Clear For I = 0 To Workspaces(0).Users.Count - 1 NomUsuario = Workspaces(0).Users(I).Name ListUsers.AddItem NomUsuario Next I BAnularUser.Visible = False BAnadirUser.Visible = True End Sub
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 44
Private Sub BEliminarWs_Click() Dim I As Integer, NomWS As String, NumWS As Integer For I = 0 To DBEngine.Workspaces.Count - 1 NomWS = DBEngine.Workspaces(I).Name If NomWS = ListWS.Text Then NumWS = I Next I Workspaces(NumWS).Close ListWS.Clear For I = 0 To DBEngine.Workspaces.Count - 1 NomWS = DBEngine.Workspaces(I).Name ListWS.AddItem NomWS Next I BEliminarWs.Enabled = False BAnularWS.Visible = False BAnadirWs.Visible = True End Sub Private Sub BSalir_Click() End End Sub Private Sub BVerIni_Click() Shell "Notepad.exe " & App.Path & "\Cap20Usr.INI", vbNormalFocus End Sub Private Sub Form_Activate() On Error GoTo RutErr Dim LineaEntr As String Dim LineaEntr8 As String Dim I As Integer, NomUsuario As String Dim PathFichero As String Open App.Path & "\Cap20Usr.INI" For Input As #1 Do Until EOF(1) Line Input #1, LineaEntr LineaEntr8 = "" LineaEntr8 = Left(LineaEntr, 8) If UCase(LineaEntr8) = "PROYCAPT" Then Me.Caption = Right(LineaEntr, Len(LineaEntr) - 9) If UCase(LineaEntr8) = "DBENGINI" Then PathFichero = Trim(Right(LineaEntr, Len(LineaEntr) - 9)) Loop Close #1 LBDSys = PathFichero TBTextINI = PathFichero DBEngine.SystemDB = PathFichero For I = 0 To Workspaces(0).Users.Count - 1 NomUsuario = Workspaces(0).Users(I).Name ListUsers.AddItem NomUsuario Next I For I = 0 To DBEngine.Workspaces.Count - 1 NomUsuario = Workspaces(I).Name ListWS.AddItem NomUsuario Next I RutErr: If Err = 3028 Then MsgBox "No se puede abrir la base de datos del sistema. Compruebe que su Path y nombre son correctos. Vealo en Ver Ini" End If End Sub
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 45
Private Sub ListUsers_Click() BAnadirUser.Visible = False BBorrarUser.Enabled = True BAnularUser.Left = 4560 BAnularUser.Visible = True End Sub Private Sub ListWS_Click() BEliminarWs.Enabled = True BAnadirWs.Visible = False BAnularWS.Left = 6600 BAnularWS.Visible = True End Sub Private Sub Text1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single) If Shift = 1 And Button = 1 Then VarDrag = Text1.Text Text1.Drag End If End Sub El fichero Cap20Usr.INI que debe estar necesariamente en la misma carpeta que el programa, en el caso del PC del autor tiene esta forma REM Visual Basic - Guía del Estudiante. Cap. 20. Creación de Usuarios y WorkSpaces DBEngINI=C:\WinNT\System32\System.mdw (Deberá cambiar el Path del fichero System.Mdw de acuerdo a como lo tenga en su ordenador.)
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 46
Mantenimiento y Copia de Bases de Datos. Vamos a ver dos métodos del Objeto DBEngine para el mantenimiento y copia de Bases de Datos ACCESS. En una Base de Datos ACCESS, cuando borramos un dato en realidad no lo estamos borrando, sino marcándolo como borrado. (No intente recuperar un dato marcado y no borrado porque no se puede.) Por lo tanto, verá que tras sucesivas operaciones de escritura / borrado en una BD, esta va aumentando su tamaño. Se necesita un método que limpie todos los datos inservibles de la BD para disminuir su tamaño. Este método también deberá reorganizar los índices y marcadores internos a esa BD. El objeto DAO que debe hacer estas cosas es el Motor de Bases de Datos. Es decir, el Objeto DBEngine. Los métodos son CompactDatabase, que hace una copia de la base de datos (no borra la BD original) sin copiar los datos inútiles, y RepairDatabase, que intenta (no siempre lo consigue) reparar los datos internos de una BD que presente datos corruptos (Se generan con bastante facilidad cuando apagamos el ordenador con la base abierta)
Método CompactDatabase Copia, compacta y da la opción de modificar la versión, el orden de intercalado y la codificación de una base de datos cerrada. Sintaxis DBEngine.CompactDatabase BaseDatosAnt, BaseDatosNva [, inf_local [, opciones]] BaseDatosAnt es el nombre del fichero de la base de datos a compactar. Debe expresar el Path completo y el nombre del fichero (C :\MiCarpeta\MiBase.MDB) Si el nombre de archivo tiene extensión, deberá especificarla. Si la red lo admite, también puede especificar una ruta de red, como por ejemplo "\\MISERVID\MIDIR\MiBase.MDB". BaseDatosNva es el nombre del fichero (con su Path completo) de la base de datos nueva, creada al copiar la BaseDatosAnt, ya compactada. No es posible especificar en el argumento BaseDatosNva el mismo archivo de base de datos que en BaseDatosAnt. inf_local es una expresión de cadena utilizada para especificar el alfabeto usado a la hora de ordenar datos de esa Base de Datos. El parámetro a introducir es el mismo que para el argumento similar usado en la creación de la Base de Datos (dbLangGeneral para el caso de España). Este argumento es opcional. Si se omite, la información local de BaseDatosNva será la misma que la de BaseDatosAnt. Opciones nos permite cambiar alguna característica de la Base de Datos. Puede elegirse entre cifrarla o no cifrarla y cambiar la versión del motor de bases de datos que va a usar la nueva Base de Datos. dbEncrypt dbDecrypt dbVersion10 dbVersion11 dbVersion25 dbVersion30
Codifica la base de datos durante la compactación. Descodifica la base de datos durante la compactación. Crea una base de datos que utiliza la versión 1.0 del motor Jet Crea una base de datos que utiliza la versión 1.1 del motor Jet Crea una base de datos que utiliza la versión 2.5 del motor Jet Crea una base de datos que utiliza la versión 3.0 del motor Jet
Se puede usar una constante (solamente) de encriptación y una (solamente) de Versión. Sólo se puede compactar BaseDatosNva con una versión igual o posterior a la de BaseDatosAnt. Lea detenidamente la ayuda en línea de este Método.
Método RepairDatabase
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 47
Intenta reparar una base de datos dañada que accede al motor de base de datos Microsoft Jet. Sintaxis
DBEngine.RepairDatabase NombreBase
Donde NombreBase es el nombre (Y path) del fichero que contiene la Base de Datos a reparar. Puede especificar una ruta de red. P.e. : "\\MISERVID\ MIDIR\NombreBase.MDB". Para poder reparar la base debe estar Cerrada. Recuerde, si está en un entorno multiusuario, que los demás usuarios tampoco pueden tenerla abierta mientras la repara. El método RepairDatabase también intenta validar todas las tablas del sistema y todos los índices. Los datos que no puedan repararse se pierden. Si la base de datos no puede repararse, se producirá un error interceptable. Sugerencia Después de reparar una base de datos, es aconsejable compactarla con el método CompactDatabase para desfragmentar el archivo y recuperar espacio en disco.
METODOS DEL OBJETO DataBase Método Execute Este Método es para el Objeto DataBase y para el Objeto QueryDef. Ejecuta una consulta de acciones o una instrucción SQL en el objeto Database especificado. Sintaxis
Para un objeto DataBase
NombreBD.Execute origen[, opciones]
Donde NombreBD es el nombre del objeto DataBase Origen es una instrucción SQL Opciones es un entero o constante que determina las características de integridad de datos de la consulta, según se especifica mas adelante. Para un objeto QueryDef
NombreQuerydef.Execute [opciones]
Donde NombreQuerydef es el nombre del objeto QueryDef cuya propiedad SQL especifica la instrucción SQL a ejecutar. Opciones igual que para el Objeto Database. En opciones puede utilizar las siguientes constantes: dbDenyWrite dbInconsistent dbConsistent dbSQLPassThrough dbFailOnError dbSeeChanges
Deniega el permiso de escritura a los demás usuarios. (Predeterminado) Actualizaciones inconsistentes. Actualizaciones consistentes. Paso a través de SQL. Hace que se pase la instrucción SQL a una base de datos ODBC para su procesamiento. Deshace las actualizaciones en caso de error. Genera un error en tiempo de ejecución si otro usuario modifica los datos que se están editando.
El método Execute sólo es válido para las consultas de acciones. Si utiliza Execute con otro tipo de consultas, se producirá un error. Debido a que las consultas de acciones no devuelven registros, Execute no devuelve un conjunto de registros. Ejemplo. En el siguiente ejemplo, usamos EXECUTE para cambiar el campo Nombre en una tabla llamada CLIENTES de una Base de Datos abierta, cuyo Objeto DataBase se llama BaseDatos. Para poder “jugar” con el nombre a cambiar y el nombre cambiado, se introduce el nombre que queremos cambiar en TBNombre2 y el nuevo nombre en TBNombre1 Dim RegistrosCambiados As Long
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 48
Dim MiSQL As String MiSQL = "UPDATE CLIENTES SET NOMBRE = '" & TBNombre2 & "' WHERE NOMBRE= '" & TBNombre1 & "'" BaseDatos.Execute MiSQL, dbFailOnError RegistrosCambiados = BaseDatos.RecordsAffected MsgBox RegistrosCambiados Dada una instrucción SQL sintácticamente correcta y teniendo los permisos adecuados, el método Execute no fallará, aún cuando no pueda modificarse ni eliminarse una línea. Por lo tanto, debe especificar siempre la opción dbFailOnError cuando utilice el método Execute para ejecutar una consulta de actualización o eliminación. Esta opción generará un error interceptable y deshará todos los cambios realizados con éxito cuando alguno de los registros afectados se encuentre bloqueado y no pueda actualizarse o eliminarse.
Propiedad RecordsAffected Para determinar el número de registros afectado por el último método Execute, puede utilizar la propiedad RecordsAffected del objeto Database o Querydef. Por ejemplo, RecordsAffected contienen el número de registros eliminados, actualizados o insertados al ejecutar una consulta de acciones. Al utilizar el método Execute para ejecutar un objeto Querydef, en la propiedad RecordsAffected del Querydef se establece el número de registros afectados. Para obtener el mejor rendimiento, especialmente en un entorno multiusuario, puede anidar el método Execute dentro de una transacción: Utilice el método BeginTrans en el objeto Workspace actual, use luego el método Execute y complete la transacción con el método CommitTrans en el objeto Workspace. De esta forma se guardarán los cambios en el disco y se liberarán los bloqueos que se hayan podido producir durante la ejecución de la consulta. IMÁGENES EN UNA BASE DE DATOS ACCESS Una imagen (la fotografía de una persona por ejemplo) puede guardarse en una base de datos tipo ACCESS y presentarse en un control Picture. ¡¡ Imagine una aplicación que sea una agenda de teléfonos y pueda insertar la foto de la persona !! Para introducir una imagen en una BD, el campo de esa BD donde se va a introducir la imagen debe ser LongBinary ( si esa versión de ACCESS lo tiene) u Objeto OLE. Introducir y presentar un bit-map en una base de datos es necesario hacerlo mediante un Control Data. Un bit-map puede presentarse en un control Picture o en un control Image. Ambos son controles enlazados a datos. Si introducimos un Control Data y un Control Picture o Control Image en el Formulario, asociamos el Control Data a la Base de Datos, y el campo que contiene el gráfico a el Control Picture (o Control Image) mediante sus propiedades DataSource = Nombre del Control data, DataField = Nombre del Campo, tenemos el problema resuelto. Para meter un gráfico en la BD basta con introducir ese gráfico en el Picture (o Image) mediante LoadPicture, por ejemplo, y guardar los datos en la BD, bien cambiando el registro actual del Control data, bien mediante el método UpdateRecord de dicho Control Data. La asociación de la Base de datos al Control Data puede hacerse, bien mediante sus propiedades DatabaseName y RecordSource, bien creando un Recordset con código e igualando la propiedad Recordset del Control Data a ese Recordset. Es posible que se pueda introducir y presentar un bit-map en un control Picture o Image de otra forma, sin usar el control Data. Eso sí, complicando el código. No merece la pena liarse con esto. Lo mismo que decíamos que necesitamos un Control Data cuando vamos a usar un DBGrid, debemos usar un Control Data cuando vayamos a presentar una imagen.
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 49
LSB
Visual Basic - Guía del Estudiante
Capítulo 12
Página 50