Microsoft Access - Merging content with identical keys into a new table - ms-access

In Microsoft Access, I would like to merge a specific column from tables having an identical key (=being duplicates) into a new table.
The source table would look like this
Key Content
Apple X
Banana B
Banana D
Banana E
Banana G
Lemon A
Lemon X
Orange A
I would like to create a new table where there is only one entry per key, each key having a field consisting of all corresponding “content” fields accumulated into one field. Each “content” value should be delimited with something, example:
Apple X
Banana B<x>D<x>E<x>G
Lemon A<x>X
Orange A
I would prefer to have it like above, but it could also work if they are in different fields/columns like below:
Apple X
Banana B D E G
Lemon A X
Orange A
I would really appreciate help with this. When googling on this I have found a quit a few of third party add-ons (like this one http://www.tucows.com/preview/1586663/MS-Access-Join-Two-Tables-Software) that seems to be solving this, but surely this can be done with MS Access itself….or…?

Another approach would be to create the table with the two columns (Key, Content) then run the function below to copy the data into the new table. You'll have to replace "ExistingTableName" and "NewTableName" with your table names.
Sub CreateNewTable()
Dim rs As Recordset
Dim rsContent As Recordset
Dim strContent As String
'Select and loop through all keys
Set rs = CurrentDb.OpenRecordset("SELECT DISTINCT Key FROM [ExistingTableName]")
Do Until rs.EOF
'Select all content records for this key and combine into a string
Set rsContent = CurrentDb.OpenRecordset("SELECT Content FROM [ExistingTableName] WHERE Key = " & Chr(34) & Nz(rs!Key) & Chr(34))
strContent = ""
Do Until rsContent.EOF
strContent = strContent & rsContent!Content & ","
rsContent.MoveNext
Loop
If Len(strContent) > 0 Then
strContent = Mid(strContent, 1, Len(strContent) - 1)
End If
CurrentDb.Execute "INSERT INTO [NewTableName] (Key, Content) VALUES (" & Chr(34) & Nz(rs!Key) & Chr(34) & ", " & Chr(34) & Nz(strContent) & Chr(34) & ")"
rs.MoveNext
Loop
Set rs = Nothing
Set rsContent = Nothing
End Sub
I don't think there is a way to do this without VBA.
Seeing as you are using a relational database you should consider using one table to store keys and another table to store content (one content per row/record), then link them up either by using a third table or by adding the 'key' as a foreign key in the content table. I would also always use an autonumber as the primary key in all MS Access tables, if not for every other reason this is a good idea, simply to avoid corruption and enable you to change a spelling mistake like 'aple' to 'apple' without breaking your relationships.

One version would be to use a UDF and this query:
SELECT Distinct Fruit.Key,
ConcatADO("SELECT Content FROM Fruit WHERE Key='" & [Key] & "'","<x>","<x>")
AS AllRows
INTO NewFruit
FROM Fruit
User Defined Function (UDF)
Function ConcatADO(strSQL As String, strColDelim, strRowDelim)
Dim rs As New ADODB.Recordset
Dim strList As String
On Error GoTo Proc_Err
If strSQL <> "" Then
rs.Open strSQL, CurrentProject.Connection
strList = rs.GetString(, , strColDelim, strRowDelim)
strList = Mid(strList, 1, Len(strList) - Len(strRowDelim))
End If
ConcatADO = strList
Exit Function
Proc_Err:
ConcatADO = "***" & UCase(Err.Description)
End Function
Working within the query design window, you can create a crosstab
TRANSFORM Min(Fruit.Content) AS MinOfContent
SELECT Fruit.Key
FROM Fruit
GROUP BY Fruit.Key
PIVOT Fruit.Content;
Which would return
Key A B D E G X
Apple X
Banana B D E G
Lemon A X
Orange A
You could then save the crosstab and create a new query based on the cross tab. This query could be a Make Table query to get the new table, but as you can see, you have several columns.
If you have a predetermined number of possible rows for each key, there are other approaches.
Finally, you must ask yourself, is de-normalizing really the way to go?

Related

MS Access add/update query result to an existing table base on its ID

I followed the tips by others to produce an access query.
I have two tables. See figure1. And the result is figure2.
Figure1
http://img.libk.info/f1.png http://img.libk.info/f1.png
Figure2
http://img.libk.info/f2.png http://img.libk.info/f2.png
The method to generate the result query is solved in another question.
The query script :
TRANSFORM Nz(Count([number]),0) AS CountValue
SELECT Table1.ID
FROM Table1, Table2
WHERE (((Table2.number) Between [table1].[start] And [table1].[end]))
GROUP BY Table1.ID
PIVOT DatePart("yyyy",[ndate]);
My question is:
Is there anyway to write back the query result to table 1?
I want to add two new columns in table 1. And be able to add or update the query value to the field base on its "ID".
I'm not sure my description is clear or not. Hope you may understand and thanks for your help!
You won't be able to do it directly. However, here are two ways it could be done indirectly.
Method 1: Temp Table
This method is best for a quick-and-dirty one-time solution.
Create a Make-Table query based on your query and use it to make a temporary table.
Use the temporary table joined to [Table 1] to update your two new fields.
Delete the temporary table
Method 2: VBA Routine
This method is best when you want a repeatable method. It's overkill if you're only going to do it once. However, if you want calculated values for every year, you'll need to run it again.
Read the query into a recordset
Loop through the Recordset and for each ID, either
Run a sql statement to update table 1, or
open a second recordset querying by the ID and Edit/Update
Here's a simple version that updates the value for a single year.
Public Sub UpdateAnnualTotal(ByVal nYear As Long)
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim sSQL As String
Dim sId As String
Dim nTotal As Long
Set db = CurrentDb
sSQL = "SELECT [ID],[" & nYear & "_count"] FROM AnnualTotalsQuery"
Set rs = db.OpenRecordset(sSQL)
With rs
Do Until .EOF
sId = .Fields("ID").Value
nTotal = .Fields(nYear & "_count").Value
sSQL = "UPDATE [Table 1] SET [" & nYear & "_count"] = " & nTotal _
& " WHERE [ID] = '" & sId & "'"
db.Execute sSQL
.MoveNext
Loop
.Close
End With
End Sub

How to compare two access databases to compare database records

How can i compare two MS ACCESS 2007 databases.Both databases contain same tables with same feilds ad structure.i need to compare the record values between two databases to detect any difference in record values.
ACCESS 2007 Database1
serial no. | NAME | ADDRESS
1 smith street 1
2 john street 4
3 alix street 8
ACCESS 2007 Database2
serial no.| NAME | ADDRESS
1 smith street 1
2 jhn stret 4
3 alix street 8
I need a VBA code for ms access that can detect the differece of records,just as the records at serial number two.
First thing you should do is link in one of the tables to the other database, e.g link the Database 2 table into database one (this allows both to be queried together) then you could use this simple example with concatenation to determine if all the fields strung together match based on the serial number:
SELECT T1.*, T2.*
FROM Table1 As T1, Table2 As T2
WHERE T2.[serial no.] = T1.[serial no.]
AND T2.[NAME] & T2.[ADDRESS] <> T1.[NAME] & T1.[ADDRESS]
You could also specify the columns with each of their own condition if you prefer.
NOTE: This is assuming you are only looking for differences where the serial no matches, if you also need to identify records that may appear in one table but not the other then you will need to use an "Un-matched" query, the query designer can help you with this or post back and I can update my answer.
Option Compare Database
Private Sub Command4_Click()
Dim tablename1, tablename2 As String
tablename1 = Text0.Value
tablename2 = Text2.Value
'On Error GoTo Err_cmdValidateGeneralInfo_Click
Dim F As DAO.Field
Dim rs As DAO.Recordset
Dim rs1 As DAO.Recordset
Set curDB = CurrentDb()
'If Me.DateModified = Date Then
'Adds new employees to the TT_GeneralInfo table in the FTEI_PhoneBook.mdb - which is used thru out the AP databases.
' DoCmd.OpenQuery "qryEmpData_TT_General"
strsql = "Select * from " & tablename1
Set rs = curDB.OpenRecordset(strsql)
strsql1 = "Select * from " & tablename2
DoCmd.CopyObject , "Unmatched_records", acTable, tablename1
curDB.Execute "DELETE FROM Unmatched_records"
Set rs1 = curDB.OpenRecordset(strsql1)
Do Until rs.EOF
For Each F In rs.Fields
If rs.Fields(F.Name) <> rs1.Fields(F.Name) Then
'rs.Edit
strsql = "Select * into test from " & tablename1 & " where " & F.Name & " = """ & rs.Fields(F.Name) & """"
DoCmd.RunSQL strsql
If DCount(F.Name, "test") <> 0 Then
GoTo append_unmatch
'appending unmacthed records
append_unmatch:
strsql2 = "insert into Unmatched_records Select * from test"
DoCmd.RunSQL strsql2
'if record doesnt match move to next one
GoTo Nextrecord
End If
' rs.Fields(F.Name) = rs1.Fields(F.Name)
' rs.Update
End If
Next F
Nextrecord:
rs.MoveNext
rs1.MoveNext
Loop
'To check whether tables matched or not
Dim rs2 As DAO.Recordset
strsql3 = "select * from Unmatched_records"
Set rs2 = curDB.OpenRecordset(strsql3)
For Each F In rs2.Fields
If DCount(F.Name, "Unmatched_records") <> 0 Then
MsgBox ("The two tables didnt match. Check table test for unmatching reocrds.")
Else
MsgBox ("Tables match!")
End If
Exit Sub
Next F
rs2.Close
End Sub

How to rename primary key value in Access DB

I have a MS Access DB with a primary key on the parent table and 82 other tables.
New data will be coming with new name for the primary key
Before New
RS182 X182RS
RS188 X188RS
RD301 X301RD
Is there a way to rename the primary key value in bulk on all the tables in the DB because I want to associate all previous historical data to the new name value.
Based on that sample, it seems there is a consistent pattern between the new and old primary key values.
? "X" & Right("RS182", 3) & Left("RS182", 2)
X182RS
If that is true, then use a series of UPDATE statements to replace the old values with the new. But first make a backup copy of your database for safekeeping.
For example, if the primary key field for YourTable is named ID:
UPDATE YourTable
Set ID = "X" & Right(ID, 3) & Left(ID, 2);
If YourTable is included in any defined relationships, you will first need to drop those relationships (or at least uncheck the "enforce referential integrity" option for them), then restore the relationships after updating the primary key values.
Also removing the primary key property from ID should allow the UPDATE to complete faster. Re-assign the primary key afterward.
Since you have 82 tables which require this conversion, you could create a VBA procedure to do it.
Public Sub ConvertPKeyValues(ByVal pTable As String, _
ByVal pField As String)
Dim db As DAO.Database
Dim strSql As String
strSql = "UPDATE [" & pTable & "]" & vbCrLf & _
"Set [" & pField & "] = 'X' & " & _
"Right([" & pField & "], 3) & " & _
"Left([" & pField & "], 2);"
Set db = CurrentDb
db.Execute strSql, dbFailOnError
Set db = Nothing
End Sub
Call the procedure with each table name and the name of the relevant field in that table. You should also add an error handler for any problems which dbFailOnError exposes.
It's not that hard to code, at least if I understand the following correct from your question:
the primary key column exists in every table
it has the same name in every table (I'll use ID in my example)
the existing values are all in the same format ("RS182" --> two letters and three numbers)
To get a list of all tables in your database, you can take a look at the hidden table MSysObjects.
And then you just have to loop through the tables and update the ID column.
A quick example (works on my machine):
Public Function Test()
Dim RS As DAO.Recordset
Dim SQL As String
SQL = "select name from msysobjects where type = 1 and name not like 'msys*'"
Set RS = CurrentDb.OpenRecordset(SQL)
Do While Not RS.EOF
SQL = "update " & RS("name") & " set ID = 'X' & Mid([ID],3) & Left([ID],2);"
CurrentDb.Execute SQL, dbFailOnError
RS.MoveNext
Loop
RS.Close
Set RS = Nothing
End Function
You cannot modify a PK if you have related records in the other tables.
So the trick here is to temporarily modify all those relationships (I suppose they exist and that Referential Integrity is enabled - otherwise I wouldn't even talk to you :), and enable the Cascade Update option.
Don't forget to turn that option off once your data goes back in prod !!

How to evaluate text of a Field Name in Access VBA

I'm looking to iterate through a set of fields as part of a table that follow a specific format. The table contains N pairs of these fields (Var_1, Value_1, Var_2, Value_2, etc).
I need to iterate through these fields and extract the data to write it out to a text file. I'd like to use a loop, I'd rather not hardcode each field into my VBA as the fields will likely grow in time and not every field will be populated for every record. I guess if there was a way to "evaluate" a field when its name is dynamically created in a string, that would solve my problem.
So, for example...
For i = 1 to FieldCount
' Write data to each field
Initially I thought of something like this
For i=1 to FieldCount
myStr = "!Var_"+CStr(i) + " = " + "!Value_" + CStr(i)
WriteLine(myStr)
but course, it prints the text literally... any way to get Access to evaluate that text first?
Perhaps there is a better way to approach this entirely?
You can use your record set's Fields collection, and refer to the value of any individual field by referencing the field's name.
rs.Fields("field_name").Value
You can generally drop the explicit .Value since that's the field's default property.
Public Sub printFieldPairs()
Dim strSql As String
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim N As Long
strSql = "SELECT * FROM tblJoeS;"
Set db = CurrentDb
Set rs = db.OpenRecordset(strSql)
With rs
Do While Not .EOF
For N = 1 To 3
Debug.Print .Fields("Var_" & N) & " = " & _
.Fields("Value_" & N)
Next N
.MoveNext
Loop
End With
rs.Close
Set rs = Nothing
Set db = Nothing
End Sub
That's what I think you're asking for. However, if the number of field pairs grows, you will have to revise the table structure. So my inclination would be to use a different table structure.
row_identifier fKey fValue
1 Var_1 foo
1 Var_2 bar
1 Var_3 baz
2 Var_1 abcd
As suggested improvement to HansUp answer:
....
Dim fld as DAO.Field
For Each fld in rs.fields
debug.? fld.value
Next fld

A "Count" Based off Filters set in a Pivot Table

So, we have imported data which we have queried and then created a pivot table off that query. It is essentially a list of files, each having unique ID numbers, and various attributes (file extension, type of document, hash, etc). In any case, this data is based off "hits" on keyword searches from a different program. This means that there might be multiple records for the same Unique ID since there are multiple hits.
The pivot table allows us to illustrate/manipulate via filtering out certain criteria (e.g. we don't want certain file extensions or we don't want records with FIELD X or FIELD Y0. The report is fine, but we want to make a form/query/report/whatever that will pull a "count" (based off unique ID) which ignores duplicates. For example, once all the filters are set in the pivot table, based on the filters/output of the pivot table, we want something like this:
.PDF Files: 200 | total for field x | total field y | etc
.DOCX files: 320 | total for field x | total for field y | etc
Obviously, we want to ignore duplicates of the same Unique ID in the counts.
What is the best way to do this considering we will be manipulating the pivot table dynamically and often? The ideal scenario would to have the pivot table and another object (form/report/etc) open, and as the pivot table is manipulated whatever is displaying counts changes as well.
Here are some very rough notes notes. They are only minimally tested, and using IN would be a disaster with a lot of values, however, it would be easy enough to switch this round and use an excluded list. Perhaps you can get some ideas.
Dim oPTable ''PivotTable
Dim oPM ''PivotMember
Dim oFUpd ''PivotFilterUpdate
Dim oChildren ''ChildMembers
Dim fset ''FieldSet
Dim sWhere As String
Dim sTemp As String
Dim sSQL As String
Dim sDelim As String
Dim aStates As Variant
Dim i As Integer
Dim rs As DAO.Recordset
sDelim = """"
aStates = Array("Cleared", "Checked") ''Possible states
Set oPTable = Forms(0).PivotTable.ActiveView
sWhere = vbNullString
For Each fset In oPTable.FieldSets
sTemp = vbNullString
Set oChildren = oPTable.FieldSets(fset).Member.ChildMembers
For i = 0 To oChildren.Count - 1
Set oPM = oChildren(i)
Set oFUpd = oPM.Field.FieldSet.CreateFilterUpdate
If aStates(oFUpd.StateOf(oPM) - 1) = "Checked" Then
Select Case fset.BoundField.DataType
Case adChar, adLongVarWChar
sTemp = sTemp & "," & sDelim & oPM.Caption & sDelim
Case adInteger
sTemp = sTemp & "," & oPM.Caption
Case adDate
sTemp = sTemp & ",#" & oPM.Caption & "#"
Case Else
'' The above is a very short list.
'' Stop
End Select
End If
Next
If sTemp > vbNullString Then
sWhere = sWhere _
& " AND [" & fset.Name & "] IN ( " & Mid(sTemp, 2) & ")"
End If
Next
sSQL = "SELECT DISTINCT ID FROM [" & oPTable.Control.DataMemberCaption & "] "
sSQL = sSQL & "WHERE 1=1" & sWhere
Set rs = CurrentDb.OpenRecordset(sSQL)
MsgBox "Unique: " & rs.RecordCount
if that helps:
http://lazyvba.blogspot.com/2010/11/improve-your-pivot-table-to-count.html
it will get you the unique count of ID numbers by numbers you want, and you can still manipulate the pivot