Reputation: 4566
I have an MS Access Database which has a Field called Field1
that contains multiple values delimited by commas. i.e.,
Value1,Value 2, Value3, Value 4,Value5
I am trying to split the values not into separate fields, but by duplicating the record and storing each value in another field. This will be such that a record containing a cell with three values will be duplicated three times, with each record varying in the value contained in the new field. For example,
Before query/running module:
+-----------+------------------------+
| App Code | Field1 |
+-----------+------------------------+
| AB23 | Value1, Value 2,Value3 |
+------------------------------------+
After query/running module:
+-----------------------------------------------+
| App Code | Field1 | Field2 |
+-----------+------------------------+----------+
| AB23 | Value1, Value 2,Value3 | Value1 |
+-----------+------------------------|----------+
| AB23 | Value1, Value 2,Value3 | Value 2 |
+-----------+------------------------+----------+
| AB23 | Value1, Value 2,Value3 | Value3 |
+-----------+------------------------+----------+
So far, I have found several questions about splitting a field into two or even several different fields, but I have not found any solution for splitting the record vertically. Of these solutions, some use queries and others use modules but I am also uncertain of which is most efficient, so I decided to go with a VBA module.
And so, here is the VBA module that I have found to be the most useful so far:
Function CountCSWords (ByVal S) As Integer
' Counts the words in a string that are separated by commas.
Dim WC As Integer, Pos As Integer
If VarType(S) <> 8 Or Len(S) = 0 Then
CountCSWords = 0
Exit Function
End If
WC = 1
Pos = InStr(S, ",")
Do While Pos > 0
WC = WC + 1
Pos = InStr(Pos + 1, S, ",")
Loop
CountCSWords = WC
End Function
Function GetCSWord (ByVal S, Indx As Integer)
' Returns the nth word in a specific field.
Dim WC As Integer, Count As Integer, SPos As Integer, EPos As Integer
WC = CountCSWords(S)
If Indx < 1 Or Indx > WC Then
GetCSWord = Null
Exit Function
End If
Count = 1
SPos = 1
For Count = 2 To Indx
SPos = InStr(SPos, S, ",") + 1
Next Count
EPos = InStr(SPos, S, ",") - 1
If EPos <= 0 Then EPos = Len(S)
GetCSWord = Trim(Mid(S, SPos, EPos - SPos + 1))
End Function
Yet how could I use this in an Access Query to achieve the aforementioned desired results? Otherwise, is there a better way to come to the same conclusion other than a Query (i.e. solely with a VBA module)?
EDIT
Note that the primary key in the Table is
Application Code
and not autonumber. This primary key is textual and distinct. In order for a record to be split, this will require the primary key to be duplicated, which is fine.
Upvotes: 4
Views: 11722
Reputation: 5386
Here's a sample piece of code using Field1, Field2 in your a Table1
Option Explicit
Public Sub ReformatTable()
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim rsADD As DAO.Recordset
Dim strSQL As String
Dim strField1 As String
Dim strField2 As String
Dim varData As Variant
Dim i As Integer
Set db = CurrentDb
' Select all eligible fields (have a comma) and unprocessed (Field2 is Null)
strSQL = "SELECT Field1, Field2 FROM Table1 WHERE ([Field1] Like ""*,*"") AND ([Field2] Is Null)"
Set rsADD = db.OpenRecordset("Table1", dbOpenDynaset, dbAppendOnly)
Set rs = db.OpenRecordset(strSQL, dbOpenDynaset)
With rs
While Not .EOF
strField1 = !Field1
varData = Split(strField1, ",") ' Get all comma delimited fields
' Update First Record
.Edit
!Field2 = Trim(varData(0)) ' remove spaces before writing new fields
.Update
' Add records with same first field
' and new fields for remaining data at end of string
For i = 1 To UBound(varData)
With rsADD
.AddNew
!Field1 = strField1
!Field2 = Trim(varData(i)) ' remove spaces before writing new fields
.Update
End With
Next
.MoveNext
Wend
.Close
rsADD.Close
End With
Set rsADD = Nothing
Set rs = Nothing
db.Close
Set db = Nothing
End Sub
EDIT
Updated example to generate a new primary key
If you have to generate a new AppCode based on previous Appcode (AND assumming AppCode is a text field), you can use this example to generate a unique primary key based on last appcode.
Option Explicit
Public Sub ReformatTable()
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim rsADD As DAO.Recordset
Dim strSQL As String
Dim strField1 As String
Dim strField2 As String
Dim varData As Variant
Dim strAppCode As String
Dim i As Integer
Set db = CurrentDb
' Select all eligible fields (have a comma) and unprocessed (Field2 is Null)
strSQL = "SELECT AppCode, Field1, Field2 FROM Table1 WHERE ([Field1] Like ""*,*"") AND ([Field2] Is Null)"
' This recordset is only used to Append New Records
Set rsADD = db.OpenRecordset("Table1", dbOpenDynaset, dbAppendOnly)
Set rs = db.OpenRecordset(strSQL, dbOpenDynaset)
With rs
While Not .EOF
' Do we need this for newly appended records?
strAppCode = !AppCode
strField1 = !Field1
varData = Split(strField1, ",") ' Get all comma delimited fields
' Update First Field
.Edit
!Field2 = Trim(varData(0)) ' remove spaces before writing new fields
.Update
' Add new fields for remaining data at end of string
For i = 1 To UBound(varData)
With rsADD
.AddNew
' ***If you need a NEW Primary Key based on current AppCode
!AppCode = strAppCode & "-" & i
' ***If you remove the Unique/PrimaryKey and just want the same code copied
!AppCode = strAppCode
' Copy previous Field 1
!Field1 = strField1
' Insert Field 2 based on extracted data from Field 1
!Field2 = Trim(varData(i)) ' remove spaces before writing new fields
.Update
End With
Next
.MoveNext
Wend
.Close
rsADD.Close
End With
Set rsADD = Nothing
Set rs = Nothing
db.Close
Set db = Nothing
End Sub
Upvotes: 2