First of all I'm new to VB so all help is very much appreciated.
I'm trying to populate a ComboBox in Excel with data from an SQL server.
The error is 'Dim cnt As ADODB.Connection' - Complie Error: User-defined type not defined
Sub Populate_ComboBox_From_SQL()
Dim cnt As ADODB.Connection
Dim rst As ADODB.Recordset
Dim stDB As String, stConn As String, stSQL As String
Dim xlCalc As XlCalculation
Dim vaData As Variant
Dim k As Long
Set cnt = New ADODB.Connection
stConn = "DSN=Backup;Trusted_Connection=Yes;APP=Microsoft Office;DATABASE=<database>"
cnt.ConnectionString = stConn
stSQL = "SELECT 'project no' FROM 'project register"
With cnt
.CursorLocation = adUseClient
.Open stConn 'Open connection.
Set rst = .Execute(stSQL)
End With
With rst
Set .ActiveConnection = Nothing 'Disconnect the recordset.
k = .Fields.Count
vaData = .GetRows
End With
cnt.Close
With TEMPLATE
With .ComboBox1
.Clear
.BoundColumn = k
.List = Application.Transpose(vaData)
.ListIndex = -1
End With
End With
Set rst = Nothing
Set cnt = Nothing
End Sub
The sheet is called TEMPLATE and the ComboBox is called ComboBox1.
I have omitted the name of the SQL server in the connection string.
Thanks in advance.
Related
I am fairly new to Access and I have been trying for a while to run an Access query and paste the results in Excel using VBA. I have combined some code I found and I think I almost have it but cannot figure out the last step. Here is the code:
Sub test()
Dim ws As Worksheet
Dim A As Object
Dim rs As Object
Application.DisplayAlerts = False
Set A = CreateObject("Access.Application")
Set ws = ThisWorkbook.Sheets("Sheet1")
A.Visible = True
A.OpenCurrentDatabase ("access database path")
A.DoCmd.OpenQuery ("query name")
Set rs = A.CurrentDb().QueryDefs("query name").OpenRecordset()
If Not rs.EOF Then
ws.Range("A1").CopyFromRecordset rs
End If
rs.Close
Application.DisplayAlerts = True
End Sub
I am trying to run the query and paste the results in cell A1 in sheet 1.
I get a "run time error 3219" for the line:
Set rs = A.CurrentDb().QueryDefs("query name").OpenRecordset()
Any help would be greatly appreciated.
Thanks,
G
I adapted your code to fetch data from an Access query without needing to create a full Access.Application instance. Tested and working in Excel 2010.
Const cstrPath As String = "C:\share\Access\Database2.accdb"
Const cstrQuery As String = "qryBase"
Dim dbe As Object 'DAO.DBEngine '
Dim rs As Object 'DAO.Recordset '
Dim ws As Worksheet
Application.DisplayAlerts = True 'leave alerts on during testing '
Set dbe = CreateObject("DAO.DBEngine.120")
Set rs = dbe.OpenDatabase(cstrPath).OpenRecordset(cstrQuery)
If Not rs.EOF Then
Set ws = ThisWorkbook.Sheets("Sheet1")
ws.Range("A1").CopyFromRecordset rs
End If
rs.Close
Application.DisplayAlerts = True
I would use ADODB recordset. Try the below code. Here I'm connecting to an excel workbook, but you can use the same logic for access database, you just need to change the connection string.
Private con As ADODB.Connection
Private ra As ADODB.Recordset
' SqlString = SQL Query
' Sht = Sheet Name, where the output needs to be displayed
' Rng = Range ("C5"), where the output needs to be displayed
Sub DoSql(SqlString As String, Sht As String, Rng As String, Optional IncludeHeading As Boolean = False)
Dim a As String
Dim res As Variant
Set con = New ADODB.Connection
Set ra = New ADODB.Recordset
res = ""
'a = Set the appropriate connection string for your database
'The below connection is referring to the same excel workbook which contains the macro
a = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=""" & ThisWorkbook.FullName & """;Extended Properties=""Excel 12.0 Xml;HDR=YES"";"
'MsgBox a
'MsgBox SqlString
If Not Left("" & con, 8) = "Provider" Then
con.Open a
End If
If Not ra.State = 0 Then
ra.Close
End If
ra.Open SqlString, con
If Not (ra.EOF And ra.BOF) Then
ra.MoveFirst
Sheets(Sht).Select
If IncludeHeading = True Then
For intColIndex = 0 To ra.Fields.Count - 1
Range(Rng).Offset(0, intColIndex).Value = ra.Fields(intColIndex).Name
Next
Range(Rng).Offset(1, 0).CopyFromRecordset ra
Else
Range(Rng).CopyFromRecordset ra
End If
End If
ra.Close
con.Close
End Sub
I have tried a bunch of different ideas, and I'm still stuck. I'm new to VBA, so I can't figure out how to get exactly what I need, because I don't totally understand the language.
I'm looking to add borders (both outer and inner) around each cell in the data that is returned to the query. How can I write this into the code? I'm going to attach a picture of what the user will hopefully see as well.
Here's what I have:
Sub Button1_Click()
Dim con As ADODB.Connection
Dim cmd As ADODB.Command
Dim rs As ADODB.Recordset
Dim WSP1 As Worksheet
Set con = New ADODB.Connection
Set cmd = New ADODB.Command
Set rs = New ADODB.Recordset
Application.DisplayStatusBar = True
Application.StatusBar = "Contacting SQL Server..."
' Remove any values in the cells where we want to put our Stored Procedure's results.
Dim rngRange As Range
Set rngRange = Range(Cells(8, 2), Cells(Rows.Count, 1)).EntireRow
rngRange.ClearContents
' Log into our SQL Server, and run the Stored Procedure
con.Open "Provider=XXXXXXetc"
' Set up the parameter for our Stored Procedure
' (Parameter types can be adVarChar,adDate,adInteger)
cmd.Parameters.Append cmd.CreateParameter("Assembly", adVarChar, adParamInput, 10, Range("B1").Text)
Application.StatusBar = "Running stored procedure..."
cmd.CommandText = "Custom.PRO_BOM_XXXX"
Set rs = cmd.Execute(, , adCmdStoredProc)
' Copy the results to cell B7 on the first Worksheet
Set WSP1 = Worksheets(1)
WSP1.Activate
If rs.EOF = False Then WSP1.Cells(8, 2).CopyFromRecordset rs
rs.Close
Set rs = Nothing
Set cmd = Nothing
con.Close
Set con = Nothing
Application.StatusBar = "Data successfully updated."
End Sub
Current Outcome:
Expected Outcome:
You want something like:
Sub Button1_Click()
Dim con As ADODB.Connection
Dim cmd As ADODB.Command
Dim rs As ADODB.Recordset
Dim WSP1 As Worksheet
Set con = New ADODB.Connection
Set cmd = New ADODB.Command
Set rs = New ADODB.Recordset
Application.DisplayStatusBar = True
Application.StatusBar = "Contacting SQL Server..."
' Remove any values in the cells where we want to put our Stored Procedure's results.
Dim rngRange As Range
Set rngRange = Range(Cells(8, 2), Cells(Rows.Count, 1)).EntireRow
rngRange.ClearContents
' Log into our SQL Server, and run the Stored Procedure
con.Open "Provider=XXXXXXetc"
' Set up the parameter for our Stored Procedure
' (Parameter types can be adVarChar,adDate,adInteger)
cmd.Parameters.Append cmd.CreateParameter("Assembly", adVarChar, adParamInput, 10, Range("B1").Text)
Application.StatusBar = "Running stored procedure..."
cmd.CommandText = "Custom.PRO_BOM_XXXX"
Set rs = cmd.Execute(, , adCmdStoredProc)
' Copy the results to cell B7 on the first Worksheet
Set WSP1 = Worksheets(1)
'
If rs.EOF then
Application.StatusBar = ""
GoTo Closedown
End If
Dim NumRows As Integer, EndRow As Integer, EndCol As Integer
EndCol = 12 ' You can adjust this
NumRows = rs.RecordCount
EndRow = 7 + NumRows ' - Adjust the number 7 if you ever decide to start pasting from 8
' Do the paste
WSP1.Cells(8, 2).CopyFromRecordset rs
' Now set the range:
Dim PastedRange As Range
With WSP1
Set PastedRange = .Range(.Cells(8, 2), .Cells(EndRow, EndCol))
End With
'
PastedRange.Borders.Color = vbBlack ' Thanks for the tip, sktneer
'
Application.StatusBar = "Data successfully updated."
Closedown:
rs.Close: Set rs = Nothing
Set cmd = Nothing
con.Close: Set con = Nothing
End Sub
Thanks to everyone for the help. Here's what I contrived for a working solution:
Sub Button1_Click()
Dim con As ADODB.Connection
Dim cmd As ADODB.Command
Dim rs As ADODB.Recordset
Dim WSP1 As Worksheet
Set con = New ADODB.Connection
Set cmd = New ADODB.Command
Set rs = New ADODB.Recordset
Application.DisplayStatusBar = True
Application.StatusBar = "Contacting SQL Server..."
Set WSP1 = Worksheets(1)
WSP1.Activate
' Remove any values in the cells where we want to put our Stored Procedure's results.
Dim rngRange As Range
Set rngRange = Range(Cells(8, 2), Cells(Rows.Count, 1)).EntireRow
rngRange.ClearContents
rngRange.ClearFormats
' Log into our SQL Server, and run the Stored Procedure
con.Open "Provider=SQLOLEDB;Data Source=XXXXX;Initial Catalog=XXXXX;Integrated Security=SSPI;Trusted_Connection=Yes"
cmd.ActiveConnection = con
Application.StatusBar = "Running stored procedure..."
cmd.CommandText = "PRO_BOM_XXXXX"
Set rs = con.Execute("Exec Custom.PRO_XXXXX '" & ActiveSheet.Range("B1").Value2 & "','" & ActiveSheet.Range("B2").Value2 & "'")
' Copy the results to cell B7 on the first Worksheet
Set WSP1 = Worksheets(1)
If rs.EOF Then
Application.StatusBar = ""
GoTo Closedown
End If
Dim EndCol As Integer
EndCol = 14
WSP1.Cells(8, 2).CopyFromRecordset rs
'find the last row
Dim LastRow As Long
With ActiveSheet
LastRow = .Cells(.Rows.Count, 3).End(xlUp).Row
End With
' Now set the range:
Dim PastedRange As Range
With WSP1
Set PastedRange = .Range(.Cells(8, 2), .Cells(LastRow, EndCol))
End With
'Add borders
PastedRange.Borders.Color = vbBlack
Application.StatusBar = "Data successfully updated."
Closedown:
rs.Close: Set rs = Nothing
Set cmd = Nothing
con.Close: Set con = Nothing
End Sub
I have the following code to transfer code to excel file:
Dim SaveAsStr As String
Dim appXL As Excel.Application
Dim wbk As Excel.Workbook
Dim wst As Excel.Worksheet
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim rs1 As ADODB.Recordset
Dim rs2 As ADODB.Recordset
Dim LR As Long
Dim startcell As Range
DoCmd.RunMacro "Guardarmcr"
Set appXL = CreateObject("Excel.Application")
appXL.Visible = True
Set wbk = appXL.Workbooks.Add
Set wst = wbk.Worksheets(1)
Set startcell = Range("D16")
Set cn = CurrentProject.AccessConnection
Set rs = New ADODB.Recordset
Set rs1 = New ADODB.Recordset
Set rs2 = New ADODB.Recordset
With rs
Set .ActiveConnection = cn
.Source = "SELECT * FROM ExcelTitulotbl"
.Open
End With
With rs1
Set .ActiveConnection = cn
.Source = "SELECT * FROM Excelotptbl"
.Open
End With
With rs2
Set .ActiveConnection = cn
.Source = "SELECT * FROM ExcelEDTUDCtbl"
.Open
End With
With wst
'.QueryTables.Add Connection:=rs, Destination:=.Range("A1")
'.QueryTables(1).Refresh
.QueryTables.Add Connection:=rs1, Destination:=.Range("d16")
.QueryTables(1).Refresh
.Range("A16").EntireRow.Delete
.Range("e2").Font.Bold = True
.Range("e2").Font.Name = "Calibri"
.Range("e2").Font.Size = 14
.Range("e2") = "VALORACION"
.Range("D5") = "Descripción"
.Range("j5") = "Profesional Colaborador"
.Range("j6") = "Profesional Chilectra"
.Range("e5") = rs("proyectoMain")
.Range("k5") = rs("Empleado")
.Range("k6") = rs("chilectramain")
.Range("B15") = "Recargo"
.Range("D15") = "Número"
.Range("E15") = "Apdto"
.Range("F14") = "Tipo"
.Range("F15") = "Ocurrencia"
.Range("g15") = "Especialidad"
.Range("h14") = "Tipo"
.Range("h15") = "Activo"
TotalE
End With
wbk.Saved = True
Set wks = Nothing
Set wbk = Nothing
Set appXL = Nothing
End Sub
Most of it works all right but if I try to run the command a Second time the worksheet opens but the vba fails showing a 1004 error. It has something to do with LR = Range("E" & Rows.Count).End(xlUp).Row. If I quit the form and enter it again it works the first time but not a second.
Will appreciate some help on this, thanks.
Looks like you are calling the TotalE sub routine, but in it you are not telling the range explicitly what workbook to use.
Activeworkbook.Range() may be better or when you add the workbook, capture the name and pass it to the sub routine.
Strange, but your only calculating the last row in column E and putting that value + 2 into column D two rows down.
All you really need is this code to replace your call to TotalE:
ActiveWorkbook.Sheets(1).Range("D" & ActiveWorkbook.Sheets(1).Range("E" & Rows.Count).End(xlUp).Row + 2) = ActiveWorkbook.Sheets(1).Range("E" & Rows.Count).End(xlUp).Row + 2
End Sub
I construct hundreds of SQL Queries in an excel sheet and each one is placed in a cell of 1 column. What I am looking to do is run each of these SQL statements from excel.
Just wondering if anyone knows a way to convert all my SQL into VBA Strings to that I can loop through all rows to run each query.
I found this which is what I want to do but is there a way I can alter the code so it can read off excel cells rather than a Form?
http://allenbrowne.com/ser-71.html
Thanks
EDIT: Here is a sample SQL that I am trying to convert
SELECT
TT.TEST_TABLE_ID,
TT.TEST_TABLE_NO,
TT.MEMBERSHIP_NUMBER,
TT.TEST_TABLE_TYPE,
from TEST_TABLE TT
I think because each Select is in its own line it causes problems when it converts.
EDIT #2: Here is my code that executes SQL
Sub GetData()
Dim Conn As New ADODB.Connection
Dim RS As New ADODB.Recordset
Dim cmd As New ADODB.Command
Dim sqlText As String
Dim Row As Long
Dim Findex As Long
Dim Data As Worksheet
Dim X As Long
Set Data = Sheets("Results")
Data.Select
Cells.ClearContents
Conn.Open "PROVIDER=ORAOLEDB.ORACLE;DATA SOURCE=ORCL;USER ID=user;PASSWORD=password"
cmd.ActiveConnection = Conn
cmd.CommandType = adCmdText
'sqlText = How to reference Valid SQL cells
cmd.CommandText = sqlText
Set RS = cmd.Execute
For X = 1 To RS.Fields.Count
Data.Cells(1, X) = RS.Fields(X - 1).Name
Next
If RS.RecordCount < Rows.Count Then
Data.Range("A2").CopyFromRecordset RS
Else
Do While Not RS.EOF
Row = Row + 1
For Findex = 0 To RS.Fields.Count - 1
If Row >= Rows.Count - 50 Then
Exit For
End If
Data.Cells(Row + 1, Findex + 1) = RS.Fields(Findex).Value
Next Findex
RS.MoveNext
Loop
End If
Cells.EntireColumn.AutoFit
End Sub
in the SQL text part I want to be able to reference my column of SQL statements that I have. I thought I needed to convert it but you guys are right that if referencing it I can Just use your code Brad.
I tried to incorporate your code brad where my 'sqlText = How to reference Valid SQL cells is but had no success
Here is a start to the code I think you need.
I have placed the SQL in a sheet named "SQL", in Col A.
The issues with this are:
(1) You are placing field names in a row, then the data that is returned into a row. That will require two rows per SQL statement.
(2) I copied the SQL statement from sheet "SQL' and placed in Col A of "Results" (you mentioned you wanted to place results to right of SQL String. (3) You clear the contents of "Results" sheet, so you need to be careful not to erase your SQL if you decide to combine sheets.
Option Explicit
Sub Process_SQL_Strings()
Dim cmd As New ADODB.Command
Dim sqlText As String
Dim Row As Long
Dim Findex As Long
Dim Data As Worksheet
Dim iFldCt As Long
Dim conn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim sConn As String
Dim lLastRow As Long
Dim lRow As Long
Set Data = Sheets("Results")
Data.Select
Cells.ClearContents
conn.Open "PROVIDER=ORAOLEDB.ORACLE;DATA SOURCE=ORCL;USER ID=user;PASSWORD=password"
cmd.ActiveConnection = conn
cmd.CommandType = adCmdText
'' Set conn = New ADODB.Connection
'' sConn = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
'' "Data Source=C:\data\access\tek_tips.accdb;" & _
'' "Jet OLEDB:Engine Type=5;" & _
'' "Persist Security Info=False;"
conn.Open sConn
'sqlText = How to reference Valid SQL cells
lRow = 1
Do
sqlText = Sheets("SQL").Range("A" & lRow)
If sqlText = "" Then
MsgBox "Finished processing " & lRow & " rows of SQL", vbOKOnly, "Finished"
GoTo Wrap_Up
End If
Set rs = New ADODB.Recordset
rs.Open sqlText, conn, adOpenStatic, adLockBatchOptimistic, adCmdText
Data.Cells(lRow, 1) = sqlText
If not rs.EOF then
For iFldCt = 1 To rs.Fields.Count
Data.Cells(lRow, 1 + iFldCt) = rs.Fields(iFldCt - 1).Name
Next
If rs.RecordCount < Rows.Count Then
Data.Range("B" & lRow).CopyFromRecordset rs
Else
Do While Not rs.EOF
Row = Row + 1
For Findex = 0 To rs.Fields.Count - 1
If Row >= Rows.Count - 50 Then
Exit For
End If
Data.Cells(Row + 1, Findex + 1) = rs.Fields(Findex).value
Next Findex
rs.MoveNext
Loop
End If
Cells.EntireColumn.AutoFit
End If
lRow = lRow + 1
Loop
Wrap_Up:
rs.Close
Set rs = Nothing
conn.Close
Set conn = Nothing
End Sub
I am using something this:
Function SQLQueryRun(ByVal query As String, ByVal returnData As Boolean) As Variant
Dim Conn As New ADODB.Connection
Dim ADODBCmd As New ADODB.Command
Dim ret As New ADODB.Recordset
Conn.ConnectionString = "connection_string_here"
Conn.Open
ADODBCmd.ActiveConnection = Conn
ADODBCmd.CommandText = query
Set ret = ADODBCmd.Execute()
If returnData Then
If Not ret.EOF Then SQLQueryRun = ret.GetRows()
Else
SQLQueryRun = True
End If
Conn.Close
Set Conn = Nothing
Set ret = Nothing
End Function
If the second argument is False nothing is returned by function. Are you expecting results from query run?
Also I use a macro to create Query/Pivot table from sql contained in windows clipboard, if you are interested let me know.
You'll need to create a connection to your database and loop through all the cells and execute your code in each cell.
You can use ADO to to make the connection (need to add a reference to Microsoft ActiveX Data Objects 6.1 Library)
You'll need to figure out your connection string, open a connection, then loop through all the cells and execute the SQL in those cells.
Dim cnn As New ADODB.Connection
Dim connectionString As String
Dim cmd As New ADODB.Command
Dim c As Range, ws As Worksheet
Dim rst as ADODB.Recordset
connectionString = "Provider=Microsoft.ACE.OLEDB.12.0;Data source=C:\Database3.accdb;Persist Security Info=False;"
cnn.Open connectionString
cmd.ActiveConnection = cnn
For Each c In ws.Range()
cmd.CommandText = c.Value
set rst = cmd.Execute
'do what you need to with your new recordset before moving on to the next SELECT
Next c
I am trying to make an application that has the feature to import data from access into excel. I am starting with a table named ""1301 Array"" before I give user control over which table. The problem is I get the error "Cannot modify table structure. Another user has the table open", assuming because of the excel sheet I am writing in. Is there a work around to use TransferSpreadsheet for this?
Sub Importfromaccess()
Dim accappl As Access.Application
Dim strpathdb As String
Dim strpathxls As String
strpathdb = Application.GetOpenFilename("Access DataBase (*.accdb),*.accdb")
strpathxls = ActiveWorkbook.FullName
Set accappl = New Access.Application
accappl.OpenCurrentDatabase strpathdb
Dim Page As Worksheet
Dim lRow As Long, LCol As Long
Dim fullrange As String
Dim PageName As Variant
accappl.DoCmd.TransferSpreadsheet acExport, acSpreadsheetTypeExcel12Xml, "1301 Array", strpathxls, True
accappl.Quit
End Sub
The solutions I have found online mostly use sql, but I have no idea how to write in that, or how they got sql to work in excel vba. The solution below seems to do something similar to what I need, but I'm not sure how to tweak to import a table into a new sheet and give it the same name.
Sub Workbook_Open()
Dim cn As Object, rs As Object
Dim intColIndex As Integer
Dim DBFullName As String
Dim TargetRange As Range
DBFullName = "D:\Tool_Database\Tool_Database.mdb"
Application.ScreenUpdating = False
Set TargetRange = Sheets("Sheet1").Range("A1") '1301 Array after creating it?
Set cn = CreateObject("ADODB.Connection")
cn.Open "Provider=Microsoft.Jet.OLEDB.4.0; Data Source=" & DBFullName & ";"
Set rs = CreateObject("ADODB.Recordset")
rs.Open "SELECT * FROM ToolNames WHERE Item = 'Tool'", cn, , , adCmdText
' Write the field names
For intColIndex = 0 To rs.Fields.Count - 1
TargetRange.Offset(1, intColIndex).Value = rs.Fields(intColIndex).Name
Next
' Write recordset
TargetRange.Offset(1, 0).CopyFromRecordset rs
End Sub
Update: Am going to try and use this method
Sub AccessToExcel()
'Declare variables.
Dim dbConnection As ADODB.Connection
Dim dbRecordset As ADODB.Recordset
Dim dbFileName As String
Dim strSQL As String
Dim DestinationSheet As Worksheet
'Set the assignments to the Object variables.
Set dbConnection = New ADODB.Connection
Set dbRecordset = New ADODB.Recordset
Set DestinationSheet = Worksheets("Sheet2")
'Define the Access database path and name.
dbFileName = "C:\YourFilePath\Database1.accdb"
'Define the Provider for post-2007 database files.
dbConnection.Provider = "Microsoft.ACE.OLEDB.12.0;Data Source=" _
& dbFileName & ";Persist Security Info=False;"
'Use SQL's SELECT and FROM statements for importing Table1.
strSQL = "SELECT Table1.* FROM Table1;"
'Clear the destination worksheet.
DestinationSheet.Cells.Clear
With dbConnection
'Open the connection.
.Open
'The purpose of this line is to disconnect the recordset.
.CursorLocation = adUseClient
End With
With dbRecordset
'Create the recordset.
.Open strSQL, dbConnection
'Disconnect the recordset.
Set .ActiveConnection = Nothing
End With
'Copy the Table1 recordset to Sheet2 starting in cell A2.
'Row 1 contains headers that will be populated at the next step.
DestinationSheet.Range("A2").CopyFromRecordset dbRecordset
'Reinstate field headers (assumes a 4-column table).
'Note that the ID field will also transfer into column A,
'so you can optionally delete column A.
DestinationSheet.Range("A1:E1").Value = _
Array("ID", "Header1", "Header2", "Header3", "Header4")
'Close the recordset.
dbRecordset.Close
'Close the connection.
dbConnection.Close
'Release Object variable memory.
Set dbRecordset = Nothing
Set dbConnection = Nothing
Set DestinationSheet = Nothing
End Sub
The first version won't work because you are attempting to write to the Excel file that you currently have open.
Changing to the following line (of the 2nd code) will copy the data to another worksheet:
Set TargetRange = Sheets("WhateverName").Range("A1") 'or
Set TargetRange = Sheets(2).Range("A1")
'..if you know it is the 2nd sheet that
'you want to copy to. Then,
Worksheets(2).Name = "1301 Array"
You could, alternatively, create a new sheet:
Dim wsData As Worksheet
Set wsData = Worksheets.Add
wsData.Name = "1301 Array"
Set TargetRange = wsData.Range("A1")