apalopohapa
apalopohapa

Reputation: 185

How to merge two database tables when only some fields are common?

In MS Access I have two tables (A and B), and the task is to insert B into A. However, there are some special conditions:

Example:

Table A:
key  a       b       c
--- ------- ------- -------
k0  hello   dear    world
k1  bye     cruel   world

Table B:
key  a       d       e
--- ------- ------- -------
k2  welcome john    doe
k3  turulu  ann     harp

Table C (the new A):
key  a       b       c       d       e
--- ------- ------- ------- ------- -------
k0  hello   dear    world
k1  bye     cruel   world
k2  welcome                 john    doe
k3  turulu                  ann     harp

Upvotes: 0

Views: 2672

Answers (2)

Barranka
Barranka

Reputation: 21047

The easiest way I can think to solve this is to use VBA to create the query definition.

I will assume that there's a column named key which is common to both tables.

I found here that you can use collections to make a dictionary-like structure. I'll use that to build the field list.

So, here we go:

public function contains(col as Collection, key as variant) as boolean
    dim obj as variant
    on error goto err
        contains = True
        obj = col(key)
        exit function
    err:
        contains = false
end function

public sub create_this_query(tbl1 as String, tbl2 as String, keyField as String)
    ' tbl1 and tbl2 are the names of the tables you'll use
    dim db as DAO.database, rs1 as DAO.recordset, rs2 as DAO.recordset
    dim columns as Collection
    dim strSQL as String
    dim i as integer
    dim obj as variant, colName as String

    set db = currentdb()
    set tbl1 = db.openrecordset(tbl1, dbopendynaset, dbreadonly)
    set tbl2 = db.openrecordset(tbl2, dbopendynaset, dbreadonly)
    set columns = new Collection

    ' Let's create the field list (ommiting the keyField)
    for i = 1 to tbl1.fields.count
        if not contains(columns, tbl1.fields(i).Name) _
           and tbl1.fields(i).Name <> keyField then
            columns.add tbl1.fields(i).Name, tbl1.fields(i).Name
        end if
    next i
    for i = 1 to tbl2.fields.count
        if not contains(columns, tbl2.fields(i).Name) _
           and tbl2.fields(i).Name <> keyField then
            columns.add tbl1.fields(i).Name, 1 ' The value is just a placeholder
        end if
    next i
    ' Now let's build the SQL instruction
    strSQL = "select [a].[" & keyField & "]"
    for colName in columns
        strSQL = strSQL & ", [" & colName & "]"
    next obj
    strSQL = strSQL & " " & _
             "from " & _
             "    (" & _
             "        select [" & keyField & "] from [" & tbl1 & "] " & _
             "        union " & _
             "        select [" & keyField & "] from [" & tbl2 & "] " & _
             "    ) as a " & _
             "left join [" & tbl1 & "] as t1 " & _
             "    on a.[" & keyField & "] = t1.[" & keyField & "] " & _
             "left join [" & tbl2 & "] as t2 " & _
             "    on a.[" & keyField & "] = t2.[" & keyField & "] "
   ' Finally, let's create the query object
   db.createQueryDef("myNewQuery", strSQL)
end sub

Hope this helps

Upvotes: 1

Laurence
Laurence

Reputation: 10976

Create an Access Module and use the following code. Replace the values in the test sub with your table and destination names

Option Compare Database
Option Explicit

Function SplatTablesSql(pT1 As String, pT2 As String, pDest As String)
    Dim lDb As Database
    Dim lTd1 As TableDef, lTd2 As TableDef
    Dim lField As Field, lF2 As Field
    Dim lS1 As String, lS2 As String, lSep As String

    SplatTablesSql = "Select "
    lS1 = "Select "
    lS2 = "Select "

    Set lDb = CurrentDb
    Set lTd1 = lDb.TableDefs(pT1)
    Set lTd2 = lDb.TableDefs(pT2)

    For Each lField In lTd1.Fields
        SplatTablesSql = SplatTablesSql & lSep & "x.[" & lField.Name & "]"
        lS1 = lS1 & lSep & "a.[" & lField.Name & "]"
        Set lF2 = Nothing
        On Error Resume Next
        Set lF2 = lTd2.Fields(lField.Name)
        On Error GoTo 0
        If lF2 Is Nothing Then
            lS2 = lS2 & lSep & "Null"
        Else
            lS2 = lS2 & lSep & "b.[" & lField.Name & "]"
        End If
        lSep = ", "
    Next

    For Each lField In lTd2.Fields
        Set lF2 = Nothing
        On Error Resume Next
        Set lF2 = lTd1.Fields(lField.Name)
        On Error GoTo 0
        If lF2 Is Nothing Then
            SplatTablesSql = SplatTablesSql & lSep & "x.[" & lField.Name & "]"
            lS1 = lS1 & lSep & "Null as [" & lField.Name & "]"
            lS2 = lS2 & lSep & "b.[" & lField.Name & "]"
        End If
        lSep = ", "
    Next

    SplatTablesSql = SplatTablesSql & " Into [" & pDest & "] From ( " & lS1 & " From [" & pT1 & "] a Union All " & lS2 & " From [" & pT2 & "] b ) x"
End Function

Sub Test()

    CurrentDb.Execute SplatTablesSql("a", "b", "c")

End Sub

Upvotes: 1

Related Questions