1

I have very little experience with Access Databases however I have wrote a similar VBA macro in excel. I am trying to copy rows from one .mdb file into the exact same table on a different .mdb file. However I would like it to only import it if it does not already exsist. Could someone please advise me the best way to go about this and maybe some code I can use and modify? I have already looked around stack overflow and can't seem to find any examples that work.

There are 8 different tables and inside these a few hundred rows. with maybe 5-20 columns.

If the script could be made inside VBS this would be ideal, as it would allow me to run the updates without loading into access.

Thanks for any help or advice, Simon

EDIT -

Zev's answer seems to do the job however I am getting this error, also the MDB inside site2 is the one I am copying from and putting it into site1

Error: Expected end of statement
Code: 800A0401
Line: 17
Char: 13

Code (saved as "update.vbs"):

Dim eng
Set eng = CreateObject("DAO.DBEngine.120")
Set dest = eng.OpenDatabase("C:\Users\simon\Documents\garden games redesign\import script\Site1\ActinicCatalog.mdb")

Sub CopyTable()
    Dim rs
    Set rs = dest.OpenRecordset("Person")

    Dim sWhere
    For Each fld In rs.Fields
        sWhere = sWhere & " AND " & fld.Name & " <> t1." & fld.Name
    Next
    sWhere = Mid(sWhere, 6)

    Dim sql: sql= _
        "INSERT INTO Person " & _
        "SELECT * " & _
        "FROM Person AS t1 IN ""C:\Users\simon\Documents\garden games redesign\import script\Site2\ActinicCatalog.mdb"" " & _
        "WHERE " & sWhere
    dest.Execute(sql)
End Sub

Edit for more info:

\Site1\ActinicCatalog.mdb - is destination database \Site2\ActinicCatalog.mdb - is original database

These databases have about 20 columns

Simon Staton
  • 4,345
  • 4
  • 27
  • 49
  • Please define **doesn't exist**. Is there a primary key field, in which case how would you handle differences between the source and destination records? Or once a record has different data it is a new record? – Zev Spitz Jul 16 '13 at 11:43
  • Very valid point, thinking of this I may even need this more advanced. Lets say we have 1 database with rows of data and the second database with rows also, by doesn't exist I mean the row not being inside the database it is copying into. If it does exist (we can use the first column as an identifier for this) check that all column values match (to copy over edits made on existing data. – Simon Staton Jul 16 '13 at 13:10
  • The line `"FROM " & Person & " AS t1 IN "C:\Users\simon\Documents\garden games redesign\import script\Site2\ActinicCatalog.mdb" " & _` should read `"FROM " & Person & " AS t1 IN ""C:\Users\simon\Documents\garden games redesign\import script\Site2\ActinicCatalog.mdb"" " & _`. Note the `""` instead of `"` before and after the path. – Zev Spitz Jul 16 '13 at 13:10
  • Sorry dude, the edit was outdated from your reply underneath. Have just fixed it with the code I am now using throwing out the error. – Simon Staton Jul 16 '13 at 13:11
  • What error are you getting now? – Zev Spitz Jul 16 '13 at 13:16
  • Line: 15, Char: 13, Expected end of statement, Code: 800A0401, I have updated the above edit to show how the code is looking now while showing this error – Simon Staton Jul 16 '13 at 13:18
  • Sorry, I've become too used to VB.NET. In VBScript, the variable declaration (`Dim`) and the assignment have to be on different lines (or separated by colons): `Dim sql: sql="INSERT ...` – Zev Spitz Jul 16 '13 at 13:22
  • I see, you lost me a bit on that one though. I can't see Dim S:S=" in the code. Do I just make a new line after every Dim, so 'Dim rs' will become 'Dim' (new line) 'rs' Edit, just seen your edit – Simon Staton Jul 16 '13 at 13:25
  • `s` should have been `sql`. Can you update the code in the question? – Zev Spitz Jul 16 '13 at 13:26
  • Okay new error, code: 800A0400, line: 15, Char: 14 – Simon Staton Jul 16 '13 at 13:26
  • wait, edit! it went through lets see if it updated – Simon Staton Jul 16 '13 at 13:26
  • Okay its doing stuff, but its doing it in a strange way, in one db it duplicated itself. Can I just check Set Dest = database its copying to, Set rs = database its copying from, inside sql query = database its copying from? – Simon Staton Jul 16 '13 at 13:29
  • Edit: Okay my bad, its not even duplicating its just not updating. – Simon Staton Jul 16 '13 at 13:40
  • The `OpenRecordset` line should be the name of a table, not the database file. You're not getting an error there? – Zev Spitz Jul 16 '13 at 13:46
  • Can you show the actual SQL statement being executed? Put this line: `InputBox "","",sql` before this line: `dest.Execute(sql)`. Copy the text in the input box and add it to the question. Alternatively, use a [debugger](http://stackoverflow.com/a/13802548/111794) with VBScript. – Zev Spitz Jul 16 '13 at 13:49
  • Okay that is now reading "Person" which is the name of the table. please see edit above, it wasn't throwing out an error at all. However its still not updating. – Simon Staton Jul 16 '13 at 13:49

2 Answers2

3

Here is an example to get you started. It copies the content of [Table1] of the current database to [Table1] of a second database.

Option Compare Database
Option Explicit

Sub copyTables()

    'Open source database
    Dim dSource As Database
    Set dSource = CurrentDb

    'Open dest database
    Dim dDest As Database
    Set dDest = DAO.OpenDatabase("C:\Users\Admin\Desktop\DBdest.accdb")

    'Open source recordset
    Dim rSource As Recordset
    Set rSource = dSource.OpenRecordset("Table1", dbOpenForwardOnly)

    'Open dest recordset
    Dim rDest As Recordset
    Set rDest = dDest.OpenRecordset("Table1", dbOpenDynaset)

    'Loop through source recordset
    While Not rSource.EOF

        'Look for record in dest recordset
        rDest.FindFirst _
            "Field1 = '" & rSource.Fields("Field1") & "' AND " & _
            "Field2 = " & rSource.Fields("Field2")

        'If not found, copy record - Field1 is text / Field2 is numeric
        If rDest.NoMatch Then
            rDest.AddNew
            rDest.Fields("Field1") = rSource.Fields("Field1")
            rDest.Fields("Field2") = rSource.Fields("Field2")
            rDest.Update
        End If

        'Next source record
        rSource.MoveNext
    Wend

    'Close dest recordset
    rDest.Close
    Set rDest = Nothing

    'Close source recordset
    rSource.Close
    Set rSource = Nothing

    'Close dest database
    dDest.Close
    Set dDest = Nothing

    'Close source database
    dSource.Close
    Set dSource = Nothing
End Sub
Luke Wage
  • 693
  • 4
  • 13
  • Thanks for the reply Luke, this should be a good place for me to get started. Do you know if its possible to run this outside of access through something like vbs? – Simon Staton Jul 16 '13 at 10:22
  • Okay I seem to be getting an error when running this: Run-time error '3058': Index or primary key cannot contain a Null value. the debug points to "rDest.Update" – Simon Staton Jul 16 '13 at 10:44
  • I assume that you have fields that are not allowed to have NULL values. Make sure that you copy *ALL* fields from the source table to the destination table. – Luke Wage Jul 16 '13 at 10:49
  • I have one more question if thats okay :) how do I go about adding more than 1 column? doing multiples of these lines is causing errors: rDest.FindFirst "nCustomerID = '" & rSource.Fields("nCustomerID") & "'" – Simon Staton Jul 16 '13 at 11:49
  • Updated the example, so it now checks for 2 fields. – Luke Wage Jul 16 '13 at 12:17
  • Awesome, thanks Luke I am just looking at Zev's reply and if I can't get that working will go with this script. Thanks for the help – Simon Staton Jul 16 '13 at 13:00
2

I would suggest using an SQL statement if possible. From VBScript using DAO/ACE:

Dim eng
Set eng = CreateObject("DAO.DBEngine.120")
Set dest = eng.OpenDatabase("path\to\destination\database.accdb")

Using ADO:

Dim conn
Set conn = CreateObject("ADODB.Connection")
With conn
    .Provider = "Microsoft.ACE.OLEDB.12.0"
    .ConnectionString = "Data Source=""path\to\destination\database.accdb"";"
    .Open
End With

The SQL statement would be something like this:

INSERT INTO Table1
SELECT *
FROM Table1 AS t1 IN "path\to\source\database.accdb"
WHERE Table1.Field1 <> t1.Field1

and executed like this:

Dim sql = _
    "INSERT INTO Table1 " & _
    "SELECT * " & _
    "FROM Table1 AS t1 IN "path\to\source\database.accdb" " & _
    "WHERE Table1.Field1 <> t1.Field1"

'Using DAO or ADO
dest.Execute sql

Considering that each table has a variable number of columns, you might have to generate the WHERE expression dynamically:

Sub CopyTable(tablename)
    Dim rs
    Set rs = dest.OpenRecordset(tablename)
    'if using ADO:
    'Set rs = conn.Execute(tablename)

    Dim sWhere
    For Each fld In rs.Fields
        sWhere = sWhere & " AND " & fld.Name & " <> t1." & fld.Name
    Next
    sWhere = Mid(sWhere, 6)

    Dim sql
    sql = _
        "INSERT INTO " & tablename & " " & _
        "SELECT * " & _
        "FROM " & tablename & " AS t1 IN ""path\to\source\database.accdb"" " & _
        "WHERE " & sWhere
    dest.Execute(sql)
End Sub

Update
If you are only using one column to determine whether the record exists, the SQL statement should look like this:

INSERT INTO Table1
SELECT *
FROM Table1 AS t1 IN "path\to\source\database.accdb"
LEFT JOIN Table1 ON t1.FirstField = Table1.FirstField
WHERE Table1.FirstField IS NULL

and CopyTable like this:

Sub CopyTable(tablename)
    Dim rs
    Set rs = dest.OpenRecordset(tablename)
    'if using ADO:
    'Set rs = conn.Execute(tablename)

    Dim field0Name
    field0Name=rs.Fields(0).Name

    Dim sql
    sql = _
        "INSERT INTO " & tablename & " " & _
        "SELECT * " & _
        "FROM " & tablename & " AS t1 IN ""path\to\source\database.accdb"" " & _
        "LEFT JOIN " & tablename & " ON t1." & field0Name & "=" & tablename & "." & field0Name & " " & _
        "WHERE " & tablename & "." & field0Name & " IS NULL"
    dest.Execute(sql)
End Sub
Zev Spitz
  • 13,950
  • 6
  • 64
  • 136
  • Thankyou very much Zev this looks awesome, am going to give it a try now. – Simon Staton Jul 16 '13 at 12:51
  • Hi Zev, please could you see my update to the question and maybe point out where I am making a mistake? – Simon Staton Jul 16 '13 at 12:56
  • Not your mistake. The path has to have a double-quote in the SQL statement. The simplest way to represent that in VBScript is to use double double-quotes, otherwise VBScript thinks the string ends after the path, and doesn't know what to do with the rest. I've edited my post appropriately. – Zev Spitz Jul 16 '13 at 13:01
  • I have just tried this fix however it still seems to be throwing up this error, is it okay if you check over my edit and make sure I'm not being a numpty with the way I am using this – Simon Staton Jul 16 '13 at 13:04
  • Keep in mind that the purpose here of using a `Sub` that takes the tablename as a parameter, is only if you have multiple tables. If you're only doing this with a single table, it would be more concise to encode the table name inside the SQL statement -- `Dim sql = "INSERT INTO Persons SELECT * FROM Persons IN ...` – Zev Spitz Jul 16 '13 at 13:06
  • @SimonStaton I don't see your edit yet. Please excuse my ignorance, but what's a numpty? – Zev Spitz Jul 16 '13 at 13:08
  • I see, I would infact me doing this for multiple tables. Great advice. And check this out for the definition http://www.urbandictionary.com/define.php?term=numpty – Simon Staton Jul 16 '13 at 13:13
  • aha the person thing might be causing the error edit: okay changing the person to pure text is still throwing the error – Simon Staton Jul 16 '13 at 13:14