Vincular tablas en Access con Visual Basic

Vincular tablas

Vamos a seguir con nuestra serie de artículos sobre Administración de aplicaciones Access con otra de las utilidades que más trabajo nos puede ahorrar a los administradores de bases de datos Access, la vinculación automática de tablas.

Suele ser bastante habitual que desde sistemas se realicen cambios en las rutas a los servidores o que simplemente tengamos que cambiar nuestro BackEnd de sitio. En estos casos, revincular nuestras aplicaciones manualmente puede ser además de laborioso, un posible punto de error ya que primero hay que quitar los vínculos viejos, añadir nuevos, tener cuidado de no borrar tablas locales, etc.

Para no tener que seguir todos estos pasos he decidido escribir este artículo sobre vinculación de tablas con Visual Basic además de añadir la funcionalidad al Administrador de aplicaciones Access. En principio puede parecer sencillo, pero como siempre que empiezo a añadir nuevas funcionalidades, surgen cuestiones que me parecen suficientemente interesantes como para escribir un artículo, así que como siempre, explicaré el desarrollo de la funcionalidad y para los que no os interese, al final del artículo adjuntaré las funciones para que las reutilicéis.

Empezaré siguiendo los pasos que he dado para el desarrollo de la funcionalidad y en primer lugar creé una función para simplemente cambiar la propiedad connect del tabledef de nuestro FrontEnd. En un principio pensaba que con eso iba a ser suficiente, buscar las tablas vinculadas en el FE, cambiar la ruta y listo. Como veremos unas líneas más abajo, no ha sido tan sencillo.

Empecemos con la función para modificar la propiedad connect de las tablas. Como siempre le pasamos la ruta del BackEnd, la contraseña y en este caso la ruta del FE (queremos que sea una herramienta externa:

Function VinculaTablas(ByVal BaseDatos As String, ByVal BackEnd As String, ByVal pass As String)

Declaramos las variables necesarias:

Dim trabajo As DAO.Workspace
Dim Base, Back As DAO.Database
Dim TablaDef, TablaBack As TableDef
Dim sConexion As String

Y recorremos el tabledef modificando la propiedad connect (nos aseguramos de que sea una tabla vinculada comprobando que en la propiedad actual exista la cadena «DATABASE=»):

Set Base = trabajo.OpenDatabase(BaseDatos, False, False, "MS Access;PWD=" & pass & "") 'Abrimos base de datos remota
Set Back = trabajo.OpenDatabase(BackEnd, False, False, "MS Access;PWD=" & pass & "") 'Abrimos base de datos remota en modo normal 'como véis le paso el mismo pass, si tenemos el FE con password diferente necesitaríamos otro parámetro

For Each TablaDef In Base.TableDefs

        sConexion = Nz(TablaDef.Connect, "")
        If InStr(1, sConexion, "DATABASE=") > 0 Then 'Controlamos que sea una tabla vinculada
            If InStr(1, sConexion, "PWD=") > 0 Then 'Si tiene pass en la conexión vieja
                If Len(pass & "") > 0 Then 'Si le pasamos pass
                    TablaDef.Connect = "MS Access;PWD=" & pass & ";DATABASE=" & BackEnd
                Else 'Creamos la cadena sin pass
                    TablaDef.Connect = ";DATABASE=" & BackEnd
                    'Base.TableDefs(TablaDef.name).Connect = ";DATABASE=" & BackEnd
                End If
            Else 'Esta no tiene pass
                TablaDef.Connect = ";DATABASE=" & BackEnd
            End If
        TablaDef.RefreshLink
        End If

    Next

En el código de esta función podemos ver varias cosas interesantes. Mi idea inicial era, como se puede ver en la función, dejar la tabla vinculada sin guardar la contraseña (ver artículo sobre Dividir base de datos Access) en el caso de que no estuviera ya guardada. Además, dar la opción de borrarla si no le pasábamos el parámetro pass.

Como siempre, estas cosas no son tan sencillas, y aunque intenté hacerlo de varias maneras no funcionaba:

TablaDef.Connect = ";DATABASE=" & BackEnd
Base.TableDefs(TablaDef.name).Connect = ";DATABASE=" & BackEnd

Parece ser que Access guarda la contraseña de la cadena de conexión sí o sí. Tenía que pensar un método alternativo para dar la opción de no guardar la contraseña haciendo mucho más segura la aplicación. Para ello se me ocurrió una manera (a lo mejor encontráis otra más sencilla) que puede ser un poco rebuscada, pero mi neurona no da para más:

  • Abrir la base de datos en modo exclusivo
  • Quitar la contraseña
  • Vincular las tablas
  • Volver a poner la contraseña

Aquí es donde empezamos a ver sentido a mi artículo sobre encriptar y modificar la contraseña de Access en tiempo de ejecución. Modificamos la función anterior para ello. Pasamos un nuevo parámetro booleano «Guarda» que nos indica si queremos guarda la contraseña de la cadena de conexión o no. La función quedaría así:

Function VinculaTablas(ByVal BaseDatos As String, ByVal BackEnd As String, ByVal pass As String, ByVal Guarda As Boolean)

Dim trabajo As DAO.Workspace
Dim Base, Back As DAO.Database
Dim TablaDef, TablaBack As TableDef
Dim sConexion As String

On Error GoTo mal

Set trabajo = DBEngine.Workspaces(0)

Set Base = trabajo.OpenDatabase(BaseDatos, False, False, "MS Access;PWD=" & pass & "") 'Abrimos base de datos remota

If Guarda = False Then 'Si queremos que no guarde la contraseña en el tabledef
    Set Back = trabajo.OpenDatabase(BackEnd, True, False, "MS Access;PWD=" & pass & "") 'Abrimos base de datos remota en modo exclusivo
    Back.NewPassword pass, "" 'Quitamos el password para que no se almacene en local
Else
    Set Back = trabajo.OpenDatabase(BackEnd, False, False, "MS Access;PWD=" & pass & "") 'Abrimos base de datos remota en modo normal
End If

For Each TablaDef In Base.TableDefs

        sConexion = Nz(TablaDef.Connect, "")
        If InStr(1, sConexion, "DATABASE=") > 0 Then 'Controlamos que sea una tabla vinculada
            If InStr(1, sConexion, "PWD=") > 0 Then 'Si tiene pass en la conexión vieja
                If Len(pass & "") > 0 Then 'Si le pasamos pass
                    TablaDef.Connect = "MS Access;PWD=" & pass & ";DATABASE=" & BackEnd
                Else 'Creamos la cadena sin pass
                    TablaDef.Connect = ";DATABASE=" & BackEnd
                    'Base.TableDefs(TablaDef.name).Connect = ";DATABASE=" & BackEnd
                End If
            Else 'Esta no tiene pass
                TablaDef.Connect = ";DATABASE=" & BackEnd
            End If
        TablaDef.RefreshLink
        End If

    Next

If Guarda = False Then
    Back.NewPassword "", pass 'Volvemos a poner el password
End If

'Cerramos
Back.Close
Set Back = Nothing

Exit Function
mal:
MsgBox ("Error al revincular tablas. " & Err.Description), vbCritical, "Error al revincular tablas"
End Function

Esta función si que funciona bien, pero me seguía sin gustar mucho… A veces modificamos el BE añadiendo o borrando (o cambiando de nombre) alguna tabla, con lo que la función nos daría un error y quedarían tablas sin vincular. Para ello la mejor manera que se me ocurrió fue la de borrar todas las tablas vinculadas y volverlas a crear. Y aquí es donde entendemos mi artículo sobre los bucles For Each. Me salto el paso del bucle mal creado y os paso la función final ya creada. Como veréis le paso otro parámetro nuevo «Todas» para controlar si borramos las tablas (todas=1) o simplemente las revinculamos (todas=0):

Function VinculaTablas(ByVal BaseDatos As String, ByVal BackEnd As String, ByVal pass As String, ByVal Guarda As Boolean, ByVal Todas As Integer)

Dim trabajo As DAO.Workspace
Dim Base, Back As DAO.Database
Dim TablaDef, TablaBack As TableDef
Dim sConexion As String
Dim flagDesconexion, flagConexion As Boolean
Dim n, i As Integer

On Error GoTo mal

Set trabajo = DBEngine.Workspaces(0)

Set Base = trabajo.OpenDatabase(BaseDatos, False, False, "MS Access;PWD=" & pass & "") 'Abrimos base de datos remota

If Guarda = False Then 'Si queremos que no guarde la contraseña en el tabledef
    Set Back = trabajo.OpenDatabase(BackEnd, True, False, "MS Access;PWD=" & pass & "") 'Abrimos base de datos remota en modo exclusivo
    Back.NewPassword pass, "" 'Quitamos el password para que no se almacene en local
Else
    Set Back = trabajo.OpenDatabase(BackEnd, False, False, "MS Access;PWD=" & pass & "") 'Abrimos base de datos remota en modo normal
End If

If Todas = 1 Then 'Borramos todas las tablas vinculadas y creamos todas las del BE

For i = Base.TableDefs.count - 1 To 0 Step -1 'Así que borramos con un bucle externo

    Set TablaDef = Base.TableDefs(i)
    sConexion = Nz(TablaDef.Connect, "")
        If InStr(1, sConexion, "DATABASE=") > 0 Then 'Controlamos que sea una tabla vinculada
            Base.TableDefs.Delete TablaDef.name
            Base.TableDefs.Refresh
        End If
Next

    For Each TablaBack In Back.TableDefs
        If Left(TablaBack.name, 4) <> "MSys" Then 'Comprobamos que no sea una tabla de sistema
            Set TablaDef = Base.CreateTableDef(TablaBack.name)
            TablaDef.Connect = ";DATABASE=" & BackEnd
            TablaDef.SourceTableName = TablaBack.name 'la que se desea vincular
            Base.TableDefs.Append TablaDef ' y por ultimo agregamos la nueva
            Base.TableDefs.Refresh
        End If

    Next
Else 'Recorremos todo el tabledef del FE para modificar los vínvulos
    For Each TablaDef In Base.TableDefs

        sConexion = Nz(TablaDef.Connect, "")
        If InStr(1, sConexion, "DATABASE=") > 0 Then 'Controlamos que sea una tabla vinculada
            If InStr(1, sConexion, "PWD=") > 0 Then 'Si tiene pass en la conexión vieja
                If Len(pass & "") > 0 Then 'Si le pasamos pass
                    TablaDef.Connect = "MS Access;PWD=" & pass & ";DATABASE=" & BackEnd
                Else 'Creamos la cadena sin pass
                    TablaDef.Connect = ";DATABASE=" & BackEnd
                    'Base.TableDefs(TablaDef.name).Connect = ";DATABASE=" & BackEnd
                End If
            Else 'Esta no tiene pass
                TablaDef.Connect = ";DATABASE=" & BackEnd
            End If
        TablaDef.RefreshLink
        End If

    Next

End If

If Guarda = False Then
    Back.NewPassword "", pass 'Volvemos a poner el password
End If

'Cerramos
Back.Close
Set Back = Nothing

Exit Function
mal:
MsgBox ("Error al revincular tablas. " & Err.Description), vbCritical, "Error al revincular tablas"
End Function

Espero que os sirva.

The following two tabs change content below.
Llevo más de 10 años programando, sobre todo en Visual Basic y con bases de datos Access. Para mí, VBA y Access siguen siendo herramientas muy potentes. He desarrollado varios proyectos con PHP y MySql. Si sumo las webs que he tenido, probablemente pasaría de 100. Ahora prefiero dedicar todo mi esfuerzo a este blog (aunque sigo manteniendo unas cuantas...). Trabajo en la administración pública (si, soy funcionario), pero he trabajado en pequeñas empresas e incluso en una "grande" de las telecomunicaciones. Ultimamente estoy bastante metido en abrirme nuevos horizontes con C# y .NET. Renovarse o morir!

10 Respuestas a Vincular tablas en Access con Visual Basic

  1. Como estas me llamo emmanuel, quiero comenzar a trabajar con Access, como veo que sos un experto en el tema, quiero hacerte una consulta acerca de manuales de acces necesarios, en particular los relacionados con contabilidad, y mas aun con consultas pues me causan muchos problemas al crearlas. gracias.-

  2. Juanl Silva dice:

    Que tal Akaitz, tengo una consulta, tengo una aplicacion con vb.6 y access y me trabaja bien, ahora me veo en la necesidad de usar Sql Azure vincular la base de datos y trabajar en linea, mi pregunta es puedo vincular access para que mi aplicacion de vb.6 siga funcionando y que por medio de una controlador ODBC vincule Access con SQL Azure?. Gracias por tu tiempo y tu respuesta

  3. Toni dice:

    En la función de Vinculación, ¿Qué datos son los que se han de pasar? ¿2 Bases de datos? ¿Base de datos y ruta?
    Me provoca duda las siguientes sentencias:

    Set Base = trabajo.OpenDatabase(BaseDatos, False, False, «MS Access;PWD=» & pass & «») ‘Abrimos base de datos remota

    Set Back = trabajo.OpenDatabase(BackEnd, True, False, «MS Access;PWD=» & pass & «») ‘Abrimos base de datos remota en modo exclusivo

    ¿BaseDatos y BackEnd son las mismas? ¿Se abren 2 veces?

    Muchas gracias por compartir tu tiempo y conocimientos.

    • Base de datos es el archivo local en el que quieres vincular las tablas de la base de datos remota o Backend. Así que tenemos que pasarle la ruta del archivo en el que queremos las tablas vinculadas y la ruta del archivo donde están las tablas.

      Un saludo.

      • Toni dice:

        Tengo que vincular una Base de datos (archivo local) con 2 Backend, una mdb y otra accdb.
        ¿Es posible que no funcione? me indica en ambas «Contraseña inválida».
        Por otra parte entiendo que si estás ejecutando la función desde el archivo local, no es necesario abrir esta.
        Gracias y un saludo.

  4. Arkaitz, muchas gracias por tu post.
    Tengo una pregunta. Cuando se vincula una tabla access con un motor sql, quien mantiene el vínculo, SQL o Access. Me explico, si uso tu funcionalidad es seguramente porque no tengo el access instalado como tal, pero mi aplicación si está escribiendo en una bd access. Si viculo esta tabla con sql, sql monitorea el evento insert de la tabla access de tal forma que pueda disparar un trigger on create?

    Nuevamente Gracias.

  5. Javi Er LoEn dice:

    Excelente trabajo, Arkaitz Arteaga.
    Muchas gracias

  6. Arkaitz, muy interesante y explicativo el post pero ahora tengo una duda si se puede realizar lo mismo pero con un origen de datos ODBC, ¿crees que esto sea posible?

  7. Angel Egi dice:

    Hola Arkaitz, creo que hice la pregunta en otro tema donde no correspondía, vuel a escribir la misma pregunta. Soy un fanático principiante de access, siguiendo tu curso he logrado hacer una pequeña aplicación front-end back-end, pero me surge un problema y no se como resolverlo, solo puedo conectar un usuario a la vez, cuando intento conectar otro usuario me da un error indicando: “Error al vincular tablas. No se puede cambia la contraseña de una base de datos compartida”.
    Te agradecería alguna recomendación, gracias de antemano.

Deja una respuesta