Exporting Access Query data into MS word Table - vba

I am trying to find a way to move the data from a query to a table in MS Word. I have attached a picture of the document
Here's the situation: When we close a case out, we need to create a document that includes several pieces of demographic data from that case and list of important dates to that case. The table needs to have some borders (underline on the date), and it needs to be inserted midway through the document (I am thinking bookmarks are the way to go). The document may be sent to other providers off of our network. (I am really hoping the pic attached...)
I have tried using Power Query (which does not allow the user to set parameters or prompt for criteria).
My initial thoughts are to create a recordset from the query and then create a loop to insert the data into the table. However, all the posts I could find seem to only deal with creating the table in word as the sole object. I also can't find how to point the recordset to a bookmark or particular table. The user will generate the document from Access (Right now, I have it where it will put certain dates, like open and close, into the corresponding Form Field in Word template, but I'm stuck at this juncture).
I have minor programming knowledge, just enough to be known as the local expert, when I am merely the only programming fish in the small pond. I would be happy to pointed in the right direction or given some code snippets (I would like to understand why/how they work).

With gratitude, I want to post the code for the solution. Of course, there is probably a better way to do it, but the solution works, and best of all I know why it works.
Public Function concatData() As String
Dim retVal As String
Dim rsHeader As Long, rsCounter As Long
Dim rs As DAO.Recordset
Dim Val As String
Dim strSQL As String
'This code puts the query into a recordset, which is then formatted into a table later
Val = [Forms]![FrmAllTracker]![CaseID]
strSQL = "Select * From QryTrackerInitRecRecv WHERE [CaseID] = " & Val
Set rs = CurrentDb.OpenRecordset(strSQL)
'Get headers
'For rsHeader = 0 To rs.Fields.Count - 1
' retVal = retVal & rs.Fields(rsHeader).Name & vbTab
'Next
'Replace last TAb with a carriage return
'retVal = Left(retVal, Len(retVal) - 1) & vbCr
Do While Not rs.EOF
'Get all records
For rsCounter = 0 To rs.Fields.Count - 1
retVal = retVal & rs.Fields(rsCounter).Value & vbTab
Next
retVal = Left(retVal, Len(retVal) - 1) & vbCr
rs.MoveNext
Loop
concatData = retVal
End Function
Private Sub BtnGenTracker_Click()
If IsNull(Me.CaseClosed) Then
MsgBox "Please Enter a Close Date", _
vbOKOnly + vbInformation
Exit Sub
End If
' Create pointers to Word Document
Dim wd As Word.Application
Dim doc As Word.Document 'doc As Word.Document
Dim bolOpenedWord As Boolean
Dim rng As Range
Dim Tbl As Word.Table
Dim MDate As String
MDate = Format([CaseOpen], "mm-dd-yyyy")
' Get pointer to Word Document
On Error Resume Next
Set wd = GetObject(, "Word.Application")
If Err.Number = 429 Then
' If Word is not opened, open it
Set wd = CreateObject("Word.Application")
bolOpenedWord = True
End If
wd.Visible = True ' Set this to true if you want to see the document open
On Error GoTo 0
Set doc = wd.Documents.Add("\\gsmstore2\COE\Testing Database\TFT1.docx")
DoCmd.OpenForm FormName:="FrmRelRecSenAll"
With doc
On Error Resume Next
'sends particular fields to corresponding FormFields in Word
.FormFields("PtName").Result = [Forms]![FrmAllTracker]![FrmSubTherapyRef].[Form].[Text62]
.FormFields("COENum").Result = Me.COEMR
.FormFields("RefRec").Result = Me.CaseOpen
.FormFields("FirstCont").Result = Me.CaseOpen
.FormFields("InitRecsRecv").Result = DLookup("FirstOfRecordsRec", "QryTrackerInitRecRecvCFFirst")
.FormFields("SuffRecs").Result = Me.SuffRecDate
.FormFields("Init2").Result = Me.InitCaseDate
.FormFields("TeamRev").Result = DLookup("ContactDate", "QryTrackerTeamRev", "[ContactType] = 14")
.FormFields("MCRMeet").Result = DLookup("ContactDate", "QryTrackerTeamRev", "[ContactType] = 6")
.FormFields("MCRMeetAct").Result = DLookup("ContactDate", "QryTrackerTeamRev", "[ContactType] = 6")
.FormFields("FTDate").Result = InputBox("Please enter Date of FT Release", "FT Release", Default)
.FormFields("FirstAppt").Result = InputBox("Please enter Date of 1st offered appt", "1st Offered Date", Default)
.FormFields("AssessDebrief").Result = DLookup("ContactDate", "QryTrackerTeamRev", "[ContactType] = 15")
.FormFields("RptSent").Result = DLookup("ContactDate", "QryTrackerTeamRev", "[ContactType] = 11")
.FormFields("FFollow").Result = DLookup("ContactDate", "QryTrackerTeamRev", "[ContactType] = 12")
.FormFields("LFollow").Result = DLookup("ContactDate", "QryTrackerLFollow")
.FormFields("CaseClosed").Result = Me.CaseClosed
If Not IsNull(DLookup("ContactDate", "QryTrackerTeamRev", "[ContactType] = 4")) Then
.FormFields("Bill").Result = "Yes"
Else
.FormFields("Bill").Result = "No"
End If
.Application.Activate
Set rng = ActiveDocument.Bookmarks("Releases").Range
rng.Text = concatData()
Set Tbl = rng.ConvertToTable
End With
'This foramats the table
With Tbl
.Columns(1).Borders(wdBorderBottom).LineStyle = wdLineStyleSingle
.Columns(1).Borders(wdBorderBottom).LineWidth = wdLineWidth050pt
.Columns(1).Borders.InsideLineStyle = wdLineStyleSingle
.Columns(1).Borders.InsideLineWidth = wdLineWidth050pt
.Columns(1).Width = 125
.Columns(2).Width = 450
.Columns(3).Delete
End With
wd.ActiveDocument.SaveAs2 ("\\Filelocation\COE\Case Files\" & COEMR & "\Tracking Sheet" & " " & MDate & ".docx")
Set doc = Nothing
Set wd = Nothing
Set rg = Nothing
Set Tbl = Nothing
End Sub

Related

Why does this line of code work half the time, and the other half gives me Data Type Conversion Error 3421

Here is the full code:
Dim db As DAO.Database
Dim rs As DAO.Recordset
Dim frm As Access.Form
Dim i As Long
'For readability
Set frm = Forms!Frm_JobTicket
'Open Tbl_Schedule for adding Schedule Dates
Set db = CurrentDb
Set rs = db.OpenRecordset("Tbl_Schedule", dbOpenDynaset, dbAppendOnly)
'Creates loop for fields 1-14. Sets Date_ScheduledX = Forms!Frm_JobTicket!Txt_DateScheduledX. Runs through Loop then closes recordset
rs.AddNew
For i = 1 To 14
If (Not IsNull(frm("Txt_DateScheduled" & i & "_JobTicket"))) Then
rs("Date_Scheduled" & i) = frm("Txt_DateScheduled" & i & "_JobTicket")
End If
Next i
'Adds in Sales Order Number to Tbl_Schedule
rs!Sales_Order_Number = frm("Sales_Order_Number")
'Adds in Part Number to Tbl_Schedule
rs!Part_Number = frm("Part_Number")
'Adds updates and closes table
rs.Update
rs.Close
'Shows message box to inform the User if item was Scheduled
MsgBox "Item Scheduled."
'Runs Private Sub above. Clears all values from DateScheduled1-14 on Frm_JobTicket to null
ClearFields
'Clears DB and RS to null
Set db = Nothing
Set rs = Nothing
The line that doesn't work is this rs("Date_Scheduled" & i) = frm("Txt_DateScheduled" & i & "_JobTicket"). Sometimes it will run perfectly fine, and other times it gives me an endless flow of 3421 Data type conversion errors. I do not know what could be going wrong, none of the fields have default values, all of the fields in the table side are Date/Time with this same format, and now I am checking for nulls.
Any help would be greatly appreciated!!
Maybe something like
If Len(Me.Txt_DateScheduled & vbNullString) > 0 Then
rs("Date_Scheduled" & i) = frm("Txt_DateScheduled" & i & "_JobTicket")
Else
rs("Date_Scheduled" & i) = ""
End If
This is completely untested, but I think you should get the concept.

Writing Excel data to Word content controls without error messages

This question is about using content controls to move data values from Excel to Word in VBA. Please note I have enabled the "Microsoft Word 16.0 Object Library" under references in the MSExcel VBA environment.
My project needs to send Excel data to specific places in a Word document.
PROBLEM: It seems I am not using the contentcontrols properly and keep getting runtime errors I'm not finding much information about. Either RTE-438
Object doesen't support this method
or RTE-424
Object Required
Description of what the code does: There are two baseline workbooks with multiple worksheets. Another analysis workbook uses each of these is programmed with VLOOKUP(INDIRECT...),) to generate tables for reports put into a word document. A Variant is used to change the tabs being sourced in the baseline workbook. The analysis is basically CATS-DOGS=PETS. on each cycle through, tables that are not informational (no difference between two baseline workbooks) are skipped and the next tab is analyzed. If a table is informative, then a PDF is produced. The report (a Word document) is updated. Table is added to the report. Upon completion, the next tab or evaluation table is considered.
Sub CommandButton1_Click()
Dim Tabs(0 To 18) As Variant
Tabs(0) = "01"
Tabs(1) = "02"
Tabs(2) = "03"
Tabs(3) = "03"
Tabs(4) = "04"
Tabs(5) = "05"
Tabs(6) = "06"
Tabs(7) = "07"
Tabs(8) = "08"
Tabs(9) = "09"
Tabs(10) = "10"
Tabs(11) = "11"
Tabs(12) = "12"
Tabs(13) = "13"
Tabs(14) = "14"
Tabs(15) = "15"
Tabs(16) = "16"
Tabs(17) = "17"
Tabs(18) = "18"
Dim xlApp As Object
On Error Resume Next
Set xlApp = GetObject("excel.applicaiton")
If Err.Number = 429 Then
Err.Clear
Set xlApp = CreateObject("excel.applicaiton")
End If
On Error GoTo 0
Dim controlThis As String ' the controlThis variable is to the address of the particular data unit that should be passed to a word.documents.contentcontrols to update the text in the word document based on the change in the actual data.
Dim NetworkLocation As String
NetworkLocation = "\\myServer\myFolder\mySubfolder\"
Dim CATS As String
CATS = "kittens.xlsx"
Excel.Application.Workbooks.Open FileName:=(NetworkLocation & "Other Subforder\ThisWway\" & CATS)
Dim DOGS As String
DOGS = "puppies.xlsx"
Excel.Application.Workbooks.Open FileName:=(NetworkLocation & "differentSubfolder\ThatWay\" & DOGS)
'Populates the array with analysis tables
Dim Temples As Object
Dim Template(3 To 9) As Variant
Template(3) = "\3\EVAL Table 3.xlsx"
Template(4) = "\4\EVAL Table 4.xlsx"
Template(5) = "\5\EVAL Table 5.xlsx"
Template(6) = "\6\EVAL Table 6.xlsx"
Template(7) = "\7\EVAL Table 7.xlsx"
Template(8) = "\8\EVAL Table 8.xlsx"
Template(9) = "\9\EVAL Table 9.xlsx"
Dim strXLname As String
Dim opener As Variant
For Each opener In Template
strXLname = NetworkLocation & "Other Subfolder\EVAL Tables\WonderPets" & opener
Excel.Application.Workbooks.Open FileName:=strXLname
Dim currentDiffernce As Long
currentDifference = ActiveSheet.Cells(5, 6).Value
'This code cycles through the different EVAL Table templates
ActiveSheet.Cells(1, 1).Value = CATS
ActiveSheet.Cells(2, 1).Value = DOGS
Dim k As Variant
For Each k In Tabs
controlThis = k & "-" & eval 'passes a string to the wdApp.contentcontrol
ActiveSheet.Rows.Hidden = False
ActiveSheet.Cells(1, 4).Value = k 'initialize k
ActiveSheet.Calculate
DoEvents
currentDifference = ActiveSheet.Cells(5, 6).Value 'stop blank tables from being produced using the total delta in the preprogrammed spreadsheet
If currentDifference = 0 Then 'since the total difference in the current analysis is 0 this bit of code skips to the next WonderPet
Else
controlThis = k & "-" & opener '(Was eval as variant used with thisTable array)passes a string to the wdApp.contentcontrol
Call PDFcrate 'Print the Table to a PDF file. Worked well and was made a subroutine.
Dim objWord As Object
Dim ws As Worksheet
'Dim cc As Word.Application.ContentControls
Set ws = ActiveWorkbook.Sheets("Sheet1")
Set objWord = CreateObject("Word.Application")
objWord.Visible = True
objWord.Documents.Open FileName:="myFilePath\Myfile.docx", noencodingdialog:=True ' change as needed
With objWord.ActiveDocument
.ContentControls(controlThis & " cats").Range.Text = eval.ActiveSheet.Cells(5, 4) 'These are the updates to the report for each content control with the title. Substituting SelectContentControlsByTitle() gives RTE-424 'Object Required'
.ContentControls(controlThis & " dogs").Range.Text = eval.ActiveSheet.Cells(5, 5)
.ContentControls(controlThis & " pets").Range.Text = eval.ActiveSheet.Cells(5, 6)
.ContentControls(controlThis & " Table).range. = 'Need to add the PDF to the report, perhaps using an RichTextConentConrols...additional suggestions welcomed (haven't researched it yet).
End With
Set objWord = Nothing
Word.Application.Documents.Close SaveChanges:=True 'Saves and Closes the document
Word.Application.Quit 'quits MS Word
End If
Next 'repeats for each tab with name "k" in the workbooks
Excel.Application.Workbooks(strXLname).Close
Next 'repeat for each evalTable
Excel.Application.Workbooks(CATS).Close
Excel.Application.Workbooks(DOGS).Close
End Sub
Word's content controls can't be picked up using a string as the index value the way other things can. The following line from the code sample in the question can't work:
.ContentControls(controlThis & " cats").Range.Text = eval.ActiveSheet.Cells(5, 4)
The only valid index value for a ContentControl is ID, which is a long number (GUID) assigned by the Word application when a ContentControl is generated.
The reason for this is that more than one content control can have the same Title (name) and/or Tag. Since this information is not unique it can't be used to pick up a single content control.
Instead, code needs to use either Document.SelectContentControlsByTitle or Document.SelectContentControlsByTag. These return an collection of content controls that meet the specified criterium. For example:
Dim cc as Word.ContentControls ' As Object if late-binding is used
With objWord.ActiveDocument
Set cc = .SelectContentControlsByTitle(controlThis & " cats")
'Now loop all the content controls in the collection to work with individual ones
End With
If it's certain there's only one content control with the Title, or only the first one is wanted, then it's possible to do this:
Dim cc as Word.ContentControl ' As Object if late-binding is used
With objWord.ActiveDocument
Set cc = .SelectContentControlsByTitle(controlThis & " cats").Item(1)
cc.Range.Text = eval.ActiveSheet.Cells(5, 4)
End With
Tip 1: Using ActiveDocument is not considered good practice for Word. As with ActiveCell (or anything else) in Excel, it's not certain that the "active" thing is the one that should be manipulated. More reliable is to use an object, which in this case can be assigned directly to the document being opened. Based on the code in the question:
Dim wdDoc as Object 'Word.Document
Set wdDoc = objWord.Documents.Open(FileName:="myFilePath\Myfile.docx", noencodingdialog:=True)
With wdDoc 'instead of objWord.ActiveDocument
Tip 2: Since the code in the question targets multiple content controls, rather than declaring multiple content control objects it might be more efficient to put the titles and values in an array and loop that.
This fixed it... looping through may have been the thing that got me unstuck.
The use of the plural ContentControls or singular ContentControl didn't seem to matter. My next trick is to get the tables into the word document... any thoughts?
Set wdDoc = Word.Application.Documents(wdDocReport)
Dim evalData(0 To 2) As Variant
evalData(0) = " CATS"
evalData(1) = " DOGS"
evalData(2) = " PETS"
Dim j As Variant
Dim i As Integer
i = 4
For Each j In evalData
Dim cc As Word.ContentControls
With Word.Application.Documents(wdDocReport)
.SelectContentControlsByTitle(controlThis & j).Item (1).Range.Text = ActiveWorkbook.ActiveSheet.Cells(5, i).Value
i = i + 1
End With
Next
Word.Application.Documents.Close SaveChanges:= True
Word.Application.Quit
Only one worksheet ever takes focus so the ActiveWorkbook and ActiveWorksheet didn't hurt me here

ActiveDocument.Printout issue

I have two word documents, source and target. My target document is pulling values from a table inside source document and putting those concatenated values stored inside a variable named ReportHeader into it's page header. I have used two For-Next loops, the outer loops takes care of designated rows inside source document and this loop begins at 6th row till count of total rows inside that table.
My inner loop named "For xPages = 1 To numPages" takes care of number of parges required for each Annexure that it finds inside the table and loops through found number of pages for a particular Annexure reference.
Below is reference screenshot of my table. Please ignore the fact the few preceding lines of my tableare not being shown here. My business begins with 6th row that shows Annexure A.
Everything works fine as long as I test values in Debug.print or Msgbox but when I sent these values for printing using ActiveDocument.PrintOut, I notice an abnormal printing behaviour. First few rows starting at row 6 of table are ignored and later not all the pages are sent to printing.
Any feedback from you guys would be much appreciated.
Thanks
Syed
Sub PrintMyHeaders()
Dim r As Range
Dim sourceDoc, jobNumber, AnnexureRaw, Annexure, ReportHeader As String
Dim numPages As Integer
Application.ScreenUpdating = False
jobNumber = InputBox("Enter job number")
sourceDoc = ActiveDocument.Name
ActiveDocument.Tables(3).Range.ListFormat.RemoveNumbers
Set r = ActiveDocument.Tables(3).Range
For Each doc In Documents
If doc.Name = "Template.doc" Then Found = True
Next doc
If Found <> True Then
Documents.Open FileName:="C:\Users\smi\Documents\Template.doc"
Else
Documents("Template.doc").Activate
End If
Documents(sourceDoc).Activate
For i = 6 To r.Rows.Count
AnnexureRaw = Replace(r.Rows(i).Cells(2).Range.Text, "", "")
Annexure = Replace(AnnexureRaw, Chr(13), "")
numPages = Val(r.Rows(i).Cells(3).Range.Text)
For xPages = 1 To numPages
counter = counter + 1
ReportHeader = "PAGE " & xPages & " OF " & numPages & vbCrLf _
& "OUR REF: TKU-" & jobNumber & "/2018" & vbCrLf _
& "ANNEXURE : " & Chr(34) & Annexure & Chr(34)
Documents("Template.doc").Activate
If ActiveWindow.View.SplitSpecial <> wdPaneNone Then
ActiveWindow.Panes(2).Close
End If
If ActiveWindow.ActivePane.View.Type = wdNormalView Or ActiveWindow. _
ActivePane.View.Type = wdOutlineView Then
ActiveWindow.ActivePane.View.Type = wdPrintView
End If
ActiveWindow.ActivePane.View.SeekView = wdSeekCurrentPageHeader
Selection.Font.Name = "Arial"
Selection.Font.Size = 8
Selection.Font.Bold = True
Selection.Text = ReportHeader
Selection.ParagraphFormat.LineSpacingRule = wdLineSpaceExactly
Selection.ParagraphFormat.LineSpacing = 6
Selection.ParagraphFormat.Alignment = wdAlignParagraphRight
ActiveDocument.PrintOut
'Debug.Print ReportHeader & vbCrLf
Documents(sourceDoc).Activate
Next xPages
Next
Documents("template.doc").Activate
ActiveDocument.Close SaveChanges:=wdDoNotSaveChanges
Documents(sourceDoc).Activate
Application.ScreenUpdating = True
End Sub
When printing out documents in relatively quick succession, things can get "mixed up" if background printing is turned on. This is an option in Word:
File/Options/Advanced, section "Print"
In the object model, to turn it off programmatically you can use the following. If the user likes having it turned on, save the current setting, turn it off, then restore the setting at the end of the code:
Options.PrintBackground = False
Since speed of execution is a concern, your code can be optimized. The following suggestions can also help to make it more self-documenting. Note that I'm not testing, just writing from the top of my head, so I may make a typo or two...
Put Option Explicit at the top of your code modules. This will save you from frustrating error messages if you mistype a variable name.
Use declared objects throughout. Referring back to ActiveDocument each time is less efficient (VBA has to figure out which it is, every time. Also, the active document could change during code execution, which would mess things up.) Ditto for repeatedly requesting Tables(3).
If your concern when looping a collection is to ensure a certain member is present (a particular document, in your case) you can use Exit For to break off the loop before all members have been queried.
It's not necessary to activate a document in order to work with it in code. Once you have Document objects, that's all you need.
It's also not necessary to fiddle with the Views in order to work with a header or footer. I've deleted those lines.
I hope I've kept "everything straight"!
Option Explicit
Sub PrintMyHeaders()
Dim r As Range
Dim sourceDoc, jobNumber, AnnexureRaw, Annexure, ReportHeader As String
Dim numPages As Integer
Dim sourceTable as Word.Table, doc as Word.Document
Dim tDoc as Word.Document
Application.ScreenUpdating = False
jobNumber = InputBox("Enter job number")
Set sourceDoc = ActiveDocument
Set sourceTable = sourceDoc.Tables(3)
sourceTable.Range.ListFormat.RemoveNumbers
Set r = sourceTable.Range
For Each doc In Documents
If doc.Name = "Template.doc" Then
Found = True
Exit For
End If
Next doc
If Found <> True Then
Set tDoc = Documents.Open(FileName:="C:\Users\smi\Documents\Template.doc")
Else
Set tDoc = Documents("Template.doc")
End If
For i = 6 To r.Rows.Count
AnnexureRaw = Replace(r.Rows(i).Cells(2).Range.Text, "", "")
Annexure = Replace(AnnexureRaw, Chr(13), "")
numPages = Val(r.Rows(i).Cells(3).Range.Text)
For xPages = 1 To numPages
counter = counter + 1
ReportHeader = "PAGE " & xPages & " OF " & numPages & vbCrLf _
& "OUR REF: TKU-" & jobNumber & "/2018" & vbCrLf _
& "ANNEXURE : " & Chr(34) & Annexure & Chr(34)
Dim rngHeader as Word.Range
Set rngHeader = tDoc.Sections(1).Headers(wdHeaderFooterPrimary).Range
rngHeader.Font.Name = "Arial"
rngHeader.Font.Size = 8
rngHeader.Font.Bold = True
rngHeader.Text = ReportHeader
rngHeader.ParagraphFormat.LineSpacingRule = wdLineSpaceExactly
rngHeader.ParagraphFormat.LineSpacing = 6
rngHeader.ParagraphFormat.Alignment = wdAlignParagraphRight
tDoc.PrintOut
'Debug.Print ReportHeader & vbCrLf
Next xPages
Next
tDoc.Close SaveChanges:=wdDoNotSaveChanges
Application.ScreenUpdating = True
End Sub

How get Cells's value and check before open excel file use VBA in outlook

I tried to search value of cell from outlook script and check whether Does it exist in excel file, if yes, open excel file, else do nothing. I can open file and search where is that value in range. But my problem is I don't know how to search that value in range and get it's position without open excel file.
EDIT 1:
Here is my detail issue: EX:
I have a phone number at column "Phone Number". I would like to find where Column of "Phone Number" (because sometime it will change to another column). After I find position of column, I would like to search whether number "123876" is existed in that column (until this time excel file still close). Now, if number "123876" is existed, open that excel file, else do nothing.
Here is my code to search if file open
Sub test()
Dim objExcel As Object
Dim WB As Object
Dim WS As Object
'Open excel file
Set objExcel = CreateObject("Excel.Application")
objExcel.Visible = True
Set WB = objExcel.Workbooks.Open("D:\Book1.xlsm")
WB.Activate
Set WS = WB.Worksheets("Sheet1")
'Search position of column "Phone Number"
Phone_Number_Col = Chr(WS.Range("A:Z").Find("Phone Number", LookIn:=xlValues).Column + 64) 'It will return 5 and change to "E" for this column
'Search whether does my number is exist in this file
Dim range_1 As Range
Set Found_Nprod = WS.Range(Phone_Number_Col & ":" & Phone_Number_Col).Find("123876", LookIn:=xlValues)
If Not Found_Nprod Is Nothing Then 'found my number
MsgBox ("This value is existed")
Else ' not find my number
MsgBox ("This value is not existed in this file")
End If
End Sub
Above code just can find when excel file is opened. But my problem is how to find like that without open file, It just open file when that file have my number "123876"
EDIT 2:
I found a peace of code which can get value of cell without open. It's run ok But I don't know how to use find function with it.
This is my function I found
Sub ReadClosed()
'
' Credit this To Bob Umlas
'
Dim strPath As String
Dim strFile As String
Dim strInfoCell As String
strPath = "D:\"
strFile = "Book1.xlsm"
i = 3
strInfoCell = "'" & strPath & "[" & strFile & "]Sheet1'!R" & i & "C1"
MsgBox "In Cell A1 = " & ExecuteExcel4Macro(strInfoCell), vbInformation, strFile
Ok, this is how I can help you with - this is a code, that I am using, and it gives you the row of the wanted string, if you give it in which column to search. If it is not the wanted string, it returns -1. If you want the second repetitable of the string, you should give in the optional parameter l_more_values_found a value of 2. If your string is Phone and in the sheet it is Phones, you should set look_for_part as True. Prety much this is how it works. Lets imagine you have this:
If you run the MyTest Sub, you would get 4 as a result. 4 is the phone number 155, which is given as a parameter to l_locate_value_row. In your case, you can check once you know the column that it has to search, whether it returns -1.
Here comes the code:
Public Function l_locate_value_row(target As String, ByRef target_sheet As Worksheet, _
Optional l_col As Long = 2, _
Optional l_more_values_found As Long = 1, _
Optional b_look_for_part = False) As Long
Dim l_values_found As Long
Dim r_local_range As Range
Dim my_cell As Range
l_values_found = l_more_values_found
Set r_local_range = Nothing
target_sheet.Activate
Set r_local_range = target_sheet.Range(target_sheet.Cells(1, l_col), target_sheet.Cells(Rows.Count, l_col))
For Each my_cell In r_local_range
'The b_look_for_part is for the vertriebscase
If b_look_for_part Then
If target = Left(my_cell, Len(target)) Then
If l_values_found = 1 Then
l_locate_value_row = my_cell.Row
Exit Function
Else
l_values_found = l_values_found - 1
End If
End If
Else
If target = Trim(my_cell) Then
If l_values_found = 1 Then
l_locate_value_row = my_cell.Row
Exit Function
Else
l_values_found = l_values_found - 1
End If
End If
End If
Next my_cell
l_locate_value_row = -1
End Function
Public Sub MyTest()
Dim l_col As Long
l_col = l_locate_value_row("155", ActiveSheet, 3, 1, False)
Debug.Print l_col
End Sub
An ADODB Query is ideal for retrieving data from a closed workbook.
Phone Numbers
Test
Code
Function hasPhoneNumber(FilePath As String, PhoneNumber As Variant) As Boolean
Const adOpenStatic = 3, adLockOptimistic = 3, adCmdText = 1
Dim conn As Object, rs As Object
Set conn = CreateObject("ADODB.Connection")
Set rs = CreateObject("ADODB.Recordset")
conn.Open "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=" & FilePath & _
";Extended Properties=Excel 12.0;"
rs.Open "SELECT (Count([Phone Number]) > 0) AS hasPhoneNumber FROM [Sheet1$]" & _
" WHERE Cstr([Phone Number])='" & PhoneNumber & "';", conn, adOpenStatic, adLockOptimistic, adCmdText
hasPhoneNumber = CBool(rs!hasPhoneNumber)
On Error Resume Next
rs.Close
Set rs = Nothing
conn.Close
Set conn = Nothing
End Function

VBA Access: Import CSV with additonal header data

I am new to coding VBA. Was wondering if you all could help me? I have a CSV file which is structured as the following:
- First 22 rows cover the specfic header data(this all loads in one column in excel)
- column headers for table are in Row 23
- the data is actually located from row 24 onward.
What the code needs to do is insert this data in new table with the right column titles. Also while inserting it needs to input the file name and header data in the first few columns of the table.
So far I have imported the entire CSV into an array I believe:
See what I have so far:
Sub readCSV()
Dim fs As Object
Dim fso As New FileSystemObject
Dim tsIn As Object
Dim sFileIn, filename As String
Dim aryFile, aryHeader, aryBody As Variant
sFileIn = "C:\doc\test.csv"
Set filename = fso.GetFileName(sFileIn)
Set fs = CreateObject("Scripting.FileSystemObject")
Set tsIn = fs.OpenTextFile(sFileIn, 1)
sTmp = tsIn.ReadAll
aryFile = Split(sTmp, vbCrLf)
For i = 1 To 22
aryHeader(1, i) = aryFile(i)
Next i
For i = 23 To UBound(aryFile)
aryBody(i) = Split(aryFile(i), ",")
DoCmd.RunSQL "INSERT INTO MAINS VALUES (filename,aryHeader(1),aryBody(i))"
Next i
End Sub
is this correct? Can anyone see of i am taking the right approach
UPDATE - recoded this a bit
Use DoCmd.TransferText instead of rolling out your own code:
http://msdn.microsoft.com/en-us/library/office/ff835958%28v=office.15%29.aspx
In your Import Specification, you can set the starting row.
See Skip first three lines of CSV file (using DoCmd?) in MS Access for more information!
Edit: The import specification can be changed to rename the fields etc. See http://www.access-programmers.com/creating-an-import-specification-in-access-2003.aspx (the Import wizard exists in Access 2007 as well) and the Advanced dialog specifically.
I was a bit irked by the use of multiple arrays in your code (which is super confusing, to me, anyway, because you are looking at counters everywhere) so I thought I would post an alternative for you. If you can do it your way, more power to you, but if you run into problems, you can try this. Code below is much more verbose, but may save you time in the future if you hand it off or even have to come back to it yourself and have no idea what is going on (lol):
Sub ReadCSV()
On Error GoTo ErrorHandler
Dim db As DAO.Database
Dim rst As DAO.Recordset
Dim fso As Scripting.FileSystemObject
Dim tst As Scripting.TextStream
Dim strFileName As String
Dim intCurrentLine As Integer
Dim strCurrentLine As String
Dim intHeaderRows As Integer
Dim strHeader As String
Dim strHeaderDelimInField As String
'Consider these your 'constants', so you don't come back to this code in a month
'and wonder what the random numbers mean.
intHeaderRows = 22 'Number of header rows in CSV.
strHeaderDelimInField = "~" 'The character(s) you want to separate each
'header line, in field.
strFileName = "C:\IrregularCSV.csv"
intCurrentLine = 1 'Keep track of which line in the file we are currently on.
'Next two lines get a reference to your table; will add data via DAO and not SQL,
'to avoid messy dynamic SQL.
Set db = CurrentDb()
Set rst = db.OpenRecordset("Mains", dbOpenDynaset)
Set fso = New Scripting.FileSystemObject
Set tst = fso.OpenTextFile(strFileName, ForReading)
'Instead of storing data in arrays, let's go through the file line by line
'and do the work we need to do.
With tst
Do Until .AtEndOfStream
strCurrentLine = .ReadLine
If intCurrentLine <= intHeaderRows Then
strHeader = strHeader & strHeaderDelimInField & strCurrentLine
Else
'Add the records via DAO here.
rst.AddNew
'In DAO, rst.Fields("FieldName") are the columns in your table.
rst.Fields("FileName") = strFileName
'Remove leading delimiter with Right.
rst.Fields("HeaderInfo") = Right(strHeader, Len(strHeader) - 1)
'Note that Split always returns a zero-based array
'and is unaffected by the Option Base statement.
'The way below is less efficient than storing
'the return of Split, but also less confusing, imo.
rst.Fields("Field1") = Split(strCurrentLine, ",")(0)
rst.Fields("Field2") = Split(strCurrentLine, ",")(1)
rst.Fields("Field3") = Split(strCurrentLine, ",")(2)
rst.Update
End If
intCurrentLine = intCurrentLine + 1
Loop
End With
tst.Close
rst.Close
ExitMe:
Set tst = Nothing
Set fso = Nothing
Set rst = Nothing
Set db = Nothing
Exit Sub
ErrorHandler:
Debug.Print Err.Number & ": " & Err.Description
GoTo ExitMe
End Sub
To be honest, I think there are a lot of gotchas to the way you are going about it. Not saying it won't work, because I think it can, but this method is more robust. An unexpected single quote won't ruin your work and using a data object to do the inserts is not prone (well, less, at least) to SQL injection issues. And I've done it with no persisted arrays. Anyway, some food for thought. Good luck.
this is what i ended up:
Sub ReadCSV2()
Dim fs As Object
Dim filename As String
Dim tsIn As Object
Dim sFileIn As String
Dim aryHeader, aryBody As Variant
Dim Text As String
Dim sqlcre As String
Dim sqlsta As String
sFileIn = "C:\test\test.csv"
filename = GetFilenameFromPath(sFileIn) 'function to get the file name
Set fs = CreateObject("Scripting.FileSystemObject")
Set tsIn = fs.OpenTextFile(sFileIn, 1)
For i = 1 To 23
Tmps = tsIn.ReadLine
Next i
aryHeader = Split(Tmps, ",")
On Error Resume Next
DoCmd.RunSQL "DROP TABLE tempdata"
On Error GoTo 0
sqlcre = "CREATE TABLE tempdata ([Filename] Text,"
For k = LBound(aryHeader) To UBound(aryHeader)
sqlcre = sqlcre & "[" & aryHeader(k) & " " & k + 1 & "] Text,"
Next k
k = k - 1
sqlcre = Left(sqlcre, Len(sqlcre) - 13) & ")"
'Debug.Print k
'Debug.Print sqlcre
DoCmd.RunSQL sqlcre
DoCmd.SetWarnings False
While Not tsIn.AtEndOfStream
Tmps = tsIn.ReadLine
aryBody = Split(Tmps, ",")
sqlsta = "INSERT INTO tempdata VALUES ('" & filename & "','"
For M = LBound(aryBody) To UBound(aryBody)
sqlsta = sqlsta & Replace(aryBody(M), "'", "`") & "', '"
Next M
M = M - 1
Debug.Print M
If M < k Then
Text = ""
For i = 1 To (k - M)
Text = Text & "', '"
Next i
sqlsta = sqlsta & Text
End If
sqlsta = Left(sqlsta, Len(sqlsta) - 7) & ")"
'Debug.Print sqlsta
'Debug.Print k
DoCmd.RunSQL sqlsta
Wend
DoCmd.SetWarnings True
End Sub