I've written a (below)macro that pulls data from the sql server 2008 r2. My issue is when the user runs the macro for first time by entering Jobnumber (say J0001) excel puts data on the spreadsheet starting from cell "A1" which is fine. The issue here is, when the user runs the macro for the second time by entering the jobnumber (say J0002), excel puts the data for Jobnumber (J0002) on cell "A1" and shifts the cells for J0001(first job) to cell "F" instead of moving down. How can I shift the previous entry down in the spreadsheet with the latest entry on top?
Here is my macro and attachment:
Sub Task()
Dim sqlstring As String
Dim connstring As String
Dim Strcode As String
Strcode = Trim(InputBox("Please enter a Job #", "Task history"))
sqlstring = "select distinct m.JobNumber , cast(m.ExpectedDate as DATE) 'Ship Date' ,m.CustLongName 'Customer' & _
" from ArchiveJobHeader m left join AuxiliaryInfoFile af (nolock) on af.jobnumber=m.jobnumber & _
" where m.JobNumber = '" & Trim(Strcode) & "'" & _
" order by 'Resulttime'"
connstring = "ODBC;DSN=SQLDSN;UID=test;PWD=test123"
Dim thisQT As QueryTable
Set thisQT = ActiveSheet.QueryTables.Add(Connection:=connstring, Destination:=Range("a1", "a1000"))
thisQT.BackgroundQuery = False
thisQT.Sql = sqlstring
thisQT.Refresh
End Sub][1]
If you incorporate a lastRow check and then assign a variable the Next Row number, you can concatenate your Range and it will be a new row every time.
Dim lastRow As Long, nextRow As Long
lastRow = Sheets("Sheet Name").Range("A" & Rows.count).End(xlUp).row
nextRow = lastRow + 1
Then when you set your Range, concatenate the variable with the string.
Set thisQT = ActiveSheet.QueryTables.Add( _
Connection:=connstring, _
Destination:=Range("A" & nextRow))
I'm not sure what you are doing with row 1000 as shown in your question. But this is the idea of using a variable with your normal Range Address.
You could have something like this:
Sub a()
'Must set Reference to "Microsoft ActiveX Data Objects 2.8 Library"
Dim ws As Worksheet
Dim n As Long ' Row To Write In
Dim sql As String
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim i As Long
Set ws = ThisWorkbook.Worksheets("Tab Name")
'Assuming you already have Headings in row 1 _
and start adding records from "A2" down...
n = ws.Range("A10000").End(xlUp).row + 1
'you sql string above is missing some double quotes...
sql = "select distinct m.JobNumber, cast(m.ExpectedDate as DATE) 'Ship Date', m.CustLongName 'Customer'" & _
" from ArchiveJobHeader m left join AuxiliaryInfoFile af (nolock) on af.jobnumber=m.jobnumber" & _
" where m.JobNumber = '" & Trim(Strcode) & "'" & _
" order by 'Resulttime'"
Set cn = New ADODB.Connection
' using an ODBC DSN... as in <http://msdn.microsoft.com/en-us/library/ms807027.aspx>
cn.Open "SQLDSN", "test", "test123"
Set rs = cn.Execute(sql) ' likely, in your case, to return one record only, _
so you are on there right away
For i = 0 To rs.Fields.Count
ws.Cells(n, i + 1) = rs(i)
Next
rs.Close
cn.Close
End Sub
You would need to put more work into this, I am afraid, but this is the direction you may consider.
Related
I am trying to import some fields from two tables that need to be joined from SSMS. The importation process is through VBA. When importing from a singular table, my query ran fine but when I tried introducing a joined format, I received the error Run-time error -2147217900 (80040e14) Incorrect Syntax near 'dd' This piece of code is where the error gets thrown:
'The line item below is what gets highlighted and throws the error message
rsData.Open strQuery, objConn
Sub GetData_Using_VERSION_ID()
Dim strConnectionString As String, strQuery As String, strMessage As String
Dim objConn As ADODB.Connection
Dim rsData As New ADODB.Recordset
Dim varArrayReader As Variant
Dim tot_rows As Long
Dim intLBoundColumn As Long, intUBoundColumn As Long ' this variable is meant to hold the lower and upper indexes of the 1st dimension of the Array (columns)
Dim intLBoundRow As Long, intUBoundRow As Long ' this variable is meant to hold the lower and upper indexes of the 2nd dimension of the Array (rows)
Dim intLBoundColumn2 As Long, intUBoundColumn2 As Long ' this variable is meant to hold the lower and upper indexes of the 1st dimension of the Array (columns)
Dim intLBoundRow2 As Long, intUBoundRow2 As Long ' this variable is meant to hold the lower and upper indexes of the 2nd dimension of the Array (rows)
Dim intLBoundColumn3 As Long, intUBoundColumn3 As Long ' this variable is meant to hold the lower and upper indexes of the 1st dimension of the Array (columns)
Dim intLBoundRow3 As Long, intUBoundRow3 As Long ' this variable is meant to hold the lower and upper indexes of the 2nd dimension of the Array (rows)
Dim intLBoundColumn4 As Long, intUBoundColumn4 As Long ' this variable is meant to hold the lower and upper indexes of the 1st dimension of the Array (columns)
Dim intLBoundRow4 As Long, intUBoundRow4 As Long ' this variable is meant to hold the lower and upper indexes of the 2nd dimension of the Array (rows)
Dim i As Long, j As Long
Dim found As Integer
Dim VId As String
Dim ews As Excel.Worksheet
Dim sws As Excel.Worksheet
Dim lws As Excel.Worksheet
'Variable to determine if App version is found for ny_id lookup
found = 0
VId = Cells(1, 3)
Set ews = Worksheets("Employer_Data")
Set sws = Worksheets("Student_Data")
Set lws = Worksheets("Loan_Data")
'Determine if Ny_id has been found or not to set the initial total rows
'tot_rows is the starting point for the inserting of the headers and data beneath them
If Cells(2, 1) = "" Or Cells(1, 1) = "" Then
tot_rows = 3
Else
tot_rows = Range("A2").End(xlDown).Row + 3
End If
'See if valid app version id input for lookup
If VId = "" Or Not IsNumeric(VId) Then
Rows(tot_rows - 1 & ":" & Rows.Count).ClearContents
ews.Rows(tot_rows - 1 & ":" & Rows.Count).ClearContents
sws.Rows(tot_rows - 1 & ":" & Rows.Count).ClearContents
lws.Rows(tot_rows - 1 & ":" & Rows.Count).ClearContents
Cells(tot_rows - 1, 1).Value = "Bad Application_Version_ID Entered"
ews.Cells(tot_rows - 1, 1).Value = "No Application_Version_ID Entered"
sws.Cells(tot_rows - 1, 1).Value = "No Application_Version_ID Entered"
lws.Cells(tot_rows - 1, 1).Value = "No Application_Version_ID Entered"
Else
'Connection string for our Dev Database
strConnectionString = "Provider = SQLOLEDB;" _
& "Data Source=NAME REMOVED FOR DATA INTEGRITY;" _
& "Initial Catalog=NAME REMOVED FOR DATA INTEGRITY;" _
& "User ID=NAME REMOVED FOR DATA INTEGRITY;" _
& "Password=NAME REMOVED FOR DATA INTEGRITY;"
'***THIS CODE WORKS BY PULLING FIELDS FROM A SINGULAR TABLE SOURCE BUT I NEED TO PULL FIELDS FROM A JOINED TABLE
'Add/Update/Remove Fields Here
'NY_CALCULATION SQL Statement to pull data
' strQuery = ""
' strQuery = strQuery & "SELECT NY_ID, APPLICATION_VERSION_ID, grad_year, year, month, calc_year, marital_status, number_dependents, " _
' & "annual_gross_salary , qual_income, month_part_cont, monthly_payment_non_law, monthly_payment_law, month_lrap_benefit, spouse_annual_gross_salary, " _
' & "spouse_monthly_payment_ug , orig_principle_law, adjusted_orig_principle_law " _
' & "FROM [dbo].[NY_CALCULATION]" & " WHERE APPLICATION_VERSION_ID in (" & VId & ") order by application_version_id, month"
'*************************************************************
'THIS IS THE QUERY STRUCTURE THAT I NEED TO USE TO PULL THE NECESSARY FIELDS FROM SSMS INTO AN EXCEL FILE, HOWEVER AS MENTIONED I RECEIVE AN ERROR. I ASSUME THAT MAYBE I DIDN'T STRUCTURE THE QUERY PROPERLY?
strQuery = ""
strQuery = strQuery & "SELECT ah.NY_ID, ah.NEW_APPLICATION_ID, dd.GRAD_YEAR, dd.FIRST_NAME, dd.LAST_NAME, dd.MARITAL_STATUS, dd.NUMBER_DEPENDENTS, " _
& "dd.LRAP_PROGRAM, dd.CREATED_TIME " _
& "FROM [dbo].[NY_APPLICATION_HISTORY] ah" _
& "LEFT JOIN [dbo].[NY_DEMOGRAPHIC_DATA] dd" _
& "ON dd.APPLICATION_VERSION_ID = ah.NEW_APPLICATION_ID" _
& " WHERE APPLICATION_VERSION_ID in (" & VId & ")" _
& "ORDER BY ah.NEW_APPLICATION_ID"
'Execute SQL statement
Set objConn = New ADODB.Connection
objConn.Open strConnectionString
Set rsData = New ADODB.Recordset
'The line item below is what gets highlighted and throws the error message
rsData.Open strQuery, objConn
'Check to see if we were able to find data with select statement
If rsData.EOF Then
Cells(tot_rows - 1, 2).Value = "No Calculation Data found for that Application_Version_ID"
Else
I need to get values from Sheet1 Column A (number of rows changes every day - can be more than 7,000) and Sheet2 Column B (also dynamic - changes every day), put those values from the columns into an array in STRING type (can be two arrays as long as I can use them in the query) and use the array in vba query WHERE ... IN ('array') to be run in MS-SQL server.
I've tried different ways to get the values into an array but have failed as many solutions offered need to use Array AS Variant when I need String type (to work in the query). One method that kind of worked was getting the values (comma separated) into one cell in another sheet and using that cell.value in the query. But that method is only good for total rows of 3000 or less. I've tried adding more - like cell2.value, cell3.value, but I would get errors (ex)if there were no values available for cell3.value. Please help.
Sub GetData()
Dim oConn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim fld As ADODB.Field
Dim mssql As String
Dim row As Integer
Dim Col As Integer
Dim WB As ThisWorkbook
'============THIS IS THE PART I NEED HELP WITH ======================
Dim strArray() As String 'TRYING TO GET VALUES FROM COLUMN AS ARRAY
Dim TotalRows As Long
Dim i As Long
TotalRows = Rows(Rows.count).End(xlUp).row
ReDim strArray(1 To TotalRows)
For i = 1 To TotalRows
strArray(i) = Cells(i, 1).Value & "','" 'TRYING TO INCLUDE COMMAS BETWEEN VALUES
Next
'===========================================================================
Set WB = ThisWorkbook
Application.ScreenUpdating = False
Set oConn = New ADODB.Connection 'NEED TO CONNECT TO SQL SERVER TO RUN QUERY
Set rs = New ADODB.Recordset
mssql = "SELECT Order.ID, Order.OrderDate, Order.Account" _
& " FROM dbo.tbl_Order" _
& " WHERE Order.ID IN ('" & strArray() & "0'")" '<=== THIS IS WHERE I NEED TO INSERT STRING ARRAY
oConn.ConnectionString = "driver={SQL Server};" & _
"server=SERVER01;authenticateduser = TRUE;database=DATABASE01"
oConn.ConnectionTimeout = 30
oConn.Open
rs.Open mssql, oConn
If rs.EOF Then
MsgBox "No matching records found."
rs.Close
oConn.Close
Exit Sub
End If
' ===clear data in columns in worksheet as new values are copied over old ones
' ===this part is working fine
Worksheets("Sheet3").Range("A:P").ClearContents
' START WRITING DATA TO SHEET3
row = 5
Col = 1
For Each fld In rs.Fields
Sheet3.Cells(row, Col).Value = fld.Name
Col = Col + 1
Next
rs.MoveFirst
row = row + 1
Do While Not rs.EOF
Col = 1
For Each fld In rs.Fields
Sheet1.Cells(row, Col).Value = fld
Col = Col + 1
Next
row = row + 1
rs.MoveNext
Loop
rs.Close
oConn.Close
End Sub
You don't want to build an array but rather a string with the values you need. I stripped down your code to illustrate how this works:
Sub GetData()
Dim values As String
Dim mssql As String
Dim TotalRows As Long
Dim i As Long
TotalRows = Rows(Rows.Count).End(xlUp).row
For i = 1 To TotalRows
values = values & "'" & Cells(i, 1).Value & "',"
Next
values = Mid(values, 1, Len(values) - 1)
mssql = "SELECT Order.ID, Order.OrderDate, Order.Account " & _
"FROM dbo.tbl_Order " & _
"WHERE Order.ID IN (" & values & ")"
MsgBox mssql
End Sub
Get Zero-Based From Column
If you can use an array of strings (don't know SQL), for column A you could e.g. use:
Dim strArray() As String: strArray = getZeroBasedFromColumn(Range("A2"))
If you have to use a string then you will have to do:
Dim strArray As String
strArray = "'" & Join(getZeroBasedFromColumn(Range("A2")), "','") & "'"
and use strArray without the parentheses.
Qualify the first cell range (A2) appropriately e.g. WB.Worksheets("Sheet1").Range("A2").
Both solutions use the following function.
The Code
Function getZeroBasedFromColumn( _
FirstCell As Range) _
As Variant
Const ProcName As String = "getZeroBasedFromColumn"
On Error GoTo clearError
If Not FirstCell Is Nothing Then
Dim rg As Range
Set rg = FirstCell.Resize(FirstCell.Worksheet.Rows.Count - FirstCell.Row + 1) _
.Find("*", , xlFormulas, , , xlPrevious)
If Not rg Is Nothing Then
Set rg = FirstCell.Resize(rg.Row - FirstCell.Row + 1)
Dim rCount As Long: rCount = rg.Rows.Count
Dim Data As Variant
If rCount = 1 Then
ReDim Data(1 To 1, 1 To 1): Data(1, 1) = rg.Value
Else
Data = rg.Value
End If
Dim arr() As String: ReDim arr(0 To rCount - 1)
Dim i As Long
For i = 1 To rCount
arr(i - 1) = CStr(Data(i, 1))
Next i
getZeroBasedFromColumn = arr
End If
End If
ProcExit:
Exit Function
clearError:
Debug.Print "'" & ProcName & "': Unexpected Error!" & vbLf _
& " " & "Run-time error '" & Err.Number & "':" & vbLf _
& " " & Err.Description
Resume ProcExit
End Function
Sub getZeroBasedFromColumnTEST()
Dim arr() As String: arr = getZeroBasedFromColumn(Range("A1"))
End Sub
An alternative (if you have the necessary database permissions) is to create a temporary table and use a JOIN in place of the WHERE IN().
Option Explicit
Sub GetData()
' get list of order numbers from Col A Sheet1 and COl B Sheet2
Dim wb As Workbook, wsOut As Worksheet
Dim rngA As Range, rngB As Range
Dim ar, ar1, ar2, iLastRow As Long, SQL As String
Dim i As Long, n As Long, id As String
Set wb = ThisWorkbook
Set wsOut = wb.Sheets("Sheet3")
' copy sheet1 Column A into array ar1
iLastRow = Sheet1.Cells(Rows.Count, "A").End(xlUp).row
Set rngA = Sheet1.Range("A1:A" & iLastRow)
If rngA.Rows.Count = 1 Then
ar1 = Array(0, rngA.Cells(1, 1).Value2)
Else
ar1 = Application.Transpose(rngA.Value2)
End If
'Debug.Print "A", LBound(ar1), UBound(ar1)
' copy sheet2 column B into array ar2
iLastRow = Sheet2.Cells(Rows.Count, "B").End(xlUp).row
Set rngB = Sheet2.Range("B1:B" & iLastRow)
If rngB.Rows.Count = 1 Then
ar2 = Array(0, rngB.Cells(1, 1).Value2)
Else
ar2 = Application.Transpose(rngB.Value2)
End If
'Debug.Print "B", LBound(ar2), UBound(ar2)
' connect to DB and create temporary table
Dim oConn As New ADODB.Connection
With oConn
.ConnectionString = "driver={SQL Server};" & _
"server=SERVER01;authenticateduser = TRUE;database=DATABASE01"
.ConnectionTimeout = 30
.Open
End With
oConn.Execute "CREATE TABLE #tmp (ID varchar(20) NOT NULL,PRIMARY KEY (ID ASC))"
' prepare insert query
SQL = "INSERT INTO #tmp (ID) VALUES (?)"
Dim cmd As New ADODB.Command
With cmd
.CommandType = adCmdText
.ActiveConnection = oConn
.CommandText = SQL
.Parameters.Append .CreateParameter("p1", adVarChar, adParamInput, 20)
End With
' insert array values into temp table
Dim t0 As Single: t0 = Timer
For Each ar In Array(ar1, ar2)
oConn.BeginTrans
For i = 1 To UBound(ar)
id = Trim(ar(i))
If Len(id) > 0 Then
cmd.Execute n, id
End If
Next
oConn.CommitTrans
'Debug.Print i - 1 & " Inserted"
Next
n = oConn.Execute("SELECT COUNT(*) FROM #tmp")(0)
'Debug.Print n & " records inserted into #tmp in " & Format(Timer - t0, "#.0 secs")
' select records using join as where filter
SQL = " SELECT Ord.ID, Ord.OrderDate, Ord.Account" _
& " FROM [tbl_Order] as Ord" _
& " JOIN #tmp ON Ord.ID = #tmp.ID"
' output result
Dim rs As New ADODB.Recordset
Set rs = oConn.Execute(SQL, n)
wsOut.Range("A:P").ClearContents
' header
Dim fld, cell As Range
Set cell = wsOut.Cells(5, 1)
For Each fld In rs.Fields
cell = fld.Name
Set cell = cell.Offset(0, 1)
Next
' data
wsOut.Cells(6, 1).CopyFromRecordset rs
oConn.Close
' end
n = wsOut.Cells(Rows.Count, 1).End(xlUp).row - 6
MsgBox n & " rows witten in " & Format(Timer - t0, "0.00 secs")
End Sub
First of all, thanks in advance for your time and help. Here is my situation;
I'm trying to add some values from an existing excel workbook into an existing table in an access database. Here is the code I've found and played with it a little according to my needs, but when I click on the button, it does nothing. No error messages, no imported values, just does nothing.
Private Sub Command39_Click()
On Error GoTo Err_Handler
Dim wbFDU As Workbook
Dim objExcelApp As Excel.Application
Dim db As Database
Dim rstWL As Recordset
Dim columnI As String
Dim columnS As String
Dim searchInC As String
Dim C As String
Dim I As String
Dim M As String
Dim S As String
Dim W As String
Dim iCounter As Integer
Set db = CurrentDb
Set rstWL = db.OpenRecordset("Select * FROM WL WHERE WLDate = Date()-1")
Set objExcelApp = New Excel.Application
objExcelApp.Workbooks.Open ("\\CDB\Shared\MNG\CIO Mng.xlsm")
Set wbFDU = objExcelApp.Workbooks(1)
If rstWL.EOF = False Then
rstWL.MoveFirst
Do While rstWL.EOF = False
iCounter = 1
C = "C" & iCounter
I = "I" & iCounter
M = "M" & iCounter
S = "S" & iCounter
W = "W" & iCounter
Do Until wbFDU.Worksheets("Rep").Range(C).Value = ""
searchInC = wbFDU.Worksheets("Rep").Range(C).Value
If rstWL!CustName = searchInC Then
columnI = wbFDU.Worksheets("Rep").Range(I).Value + wbFDU.Worksheets("Rep").Range(M).Value
columnS = wbFDU.Worksheets("Rep").Range(S).Value + wbFDU.Worksheets("Rep").Range(W).Value
rstWL.Edit
rstWL.Fields("LCDCO") = columnI
rstWL.Update
rstWL.Fields("ECDCO") = columnS
rstWL.Update
End If
iCounter = iCounter + 1
C = "C" & iCounter
I = "I" & iCounter
M = "M" & iCounter
S = "S" & iCounter
W = "W" & iCounter
Loop
rstWL.MoveNext
Loop
End If
wbFDU.Close False
Set wbFDU = Nothing
rstWL.Close
Set rstWL = Nothing
db.Close
Set db = Nothing
Exit Sub
Err_Handler:
MsgBox "The following error has occured." & vbCrLf & vbCrLf & _
"Error Number " & Err.Number & vbCrLf & _
"Error Description" & Err.Description & vbCrLf & _
"Your application will close!", _
vbCritical, "An Error has Occured"
End Sub
Since couple of days cannot figure it out what I'm doing wrong.
Consider directly querying Excel workbook in MS Access without the need of recordset looping. Specifically, use a temp table re-created or cleaned out each time to use in UPDATE query with WL.
However, it seems your workbook does not use any column headers, so specify HDR=No which will result in F1, F2, F3, ... in query resultset. Otherwise, use HDR=Yes and reference named columns.
Make-Table Query (after first time, use delete/append query for subsequent Excel updates)
SELECT *
INTO myExcelTempTable
FROM [Excel 12.0 Xml;HDR=No;Database=\\CDB\Shared\MNG\CIO Mng.xlsm].[Rep$] AS t;
Update Query
UPDATE WL
INNER JOIN myExcelTempTable AS t
ON WL.CustomerId = t.F3 --F3 being Column C (customer id)
SET LCDCO = F9, --F9 being Column I
ECDCO = F19 --F19 being Column S
WHERE WL.WLDate = Date()-1;
Do not use comments in above query in MS Access. Only included here to guide you.
A row from the Source List Worksheet (SLW) columns (1, 2 & 3) needs pasted into the Master List Worksheet (MLW) columns (3, 4 & 5) [same order] if the unique ID number (SLW1 = MLW3) does NOT already exists in the "Master List" (same workbook).
My first Excel VBA project ever. So any and all advice/suggestions/corrections/short cuts would be great. This code is what I have fumbled creating. As you know, its not working.
Sub Transfer()
Dim SLR As Integer 'SourceList's Woksheets Last Row
Dim MLR As Integer 'MasterList's Woksheets Last Row
Dim SC As Integer 'SourceList Counting through the loop (ROW NUMBER)
Dim SR As Range 'SourceList A-C Row data
'(Source information 3 rows to be transfered)
Dim ID As Integer 'Unique code of Projects
Dim Found As Range
Sheets("SourceList").Activate
SLR = Cells(Rows.Count, "A").End(xlUp).Row
'Start loop to go through SourceList unique ID numbers
For SC = 2 To SLR
'Copy SourceList ID number into Variable "ID"
ID = Sheets("SourceList").Range(1, SC)
'Also, Save Range into Variable so it doesn't have to
'go back and forth between Worksheets
Set SR = Range(Cells(1, SC), Cells(3, SC))
Sheets("MasterList").Activate
Found = Columns("C:C").Find(What:=ID, After:=ActiveCell, LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False).Activate
If Found Is Nothing Then
MLR = Cells(Rows.Count, "C").End(xlUp).Row + 1
Range(Cells(3, MLR)) = SR
SR.ClearContents
End If
Sheets("SourceList").Activate
Next SC
End Sub
Although I've posted a link for you to check out, I will post this solution which I've used before.
Sub ject()
Dim con As Object: Set con = CreateObject("ADODB.Connection")
Dim rec As Object: Set rec = CreateObject("ADODB.Recordset")
Dim datasource As String
datasource = ThisWorkbook.FullName ' returns the fullpath
Dim sconnect As String
sconnect = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=" & datasource & ";" & _
"Extended Properties=""Excel 12.0;HDR=YES"";"
con.Open sconnect
Dim sqlstr As String
' This basically executes anti-join if you know SQL
sqlstr = "SELECT * "
sqlstr = sqlstr & "FROM [SWL$] e "
sqlstr = sqlstr & "LEFT JOIN [MWL$] u "
sqlstr = sqlstr & "ON e.ID = u.ID "
sqlstr = sqlstr & "WHERE u.ID IS NULL "
sqlstr = sqlstr & "AND e.ID IS NOT NULL;"
rec.Open sqlstr, con, 3, 1
' Dump data that meets your requirement
With Sheets("MWL")
Dim lr As Long
lr = .Range("D" & .Rows.Count).End(xlUp).Row + 1
.Range("D" & lr).CopyFromRecordset rec
End With
End Sub
Considerations:
Your SWL and MWL sheet data should start at Row 1 with headers.
Both should have the header name ID which contains the unique identifier. If not, you can adjust the code above.
So what the code does is access ADO (Active Data Objects) to be able to execute data comparison using SQL commands. It is way faster than the conventional Range to Range comparison (looping). I'm not sure if it is faster than Array to Array comparison but it is certainly easier to read and adjust once you get the hang of it. Anyways, this maybe a little bit too much at the moment (since you said it is your first project), but this is tried and tested and certainly works.
IMPORTANT: Notice the sconnect variable. You need to use the correct Connection String depending on the version of your Excel.
I want to do something very simple: I have an Access database with one table mapping thousands of product IDs to product information fields. In an Excel worksheet, the user types in perhaps 100 product IDs in the first column. I need for the remaining columns to pull in information from the Access database for the corresponding IDs. Specifically:
if I use MS-Query, it seems to insist on the output being a table. I simply want the output to be inside a single cell. Preferably, a formula that involves a SQL-type query.
I don't want any of the values to be updated automatically, but rather want all the columns updated only on user demand (the user could either choose refresh through a menu, or a VBA-based refresh button on the sheet is fine as well).
I'm thinking this would be a straightforward use case, but it seems surprisingly hard to find a solution. Thank you in advance!
Working from Excel, you can use ADO to connect to a database. For Access and Excel 2007/2010, you might:
''Reference: Microsoft ActiveX Data Objects x.x Library
Dim cn As New ADODB.Connection
Dim rs As New ADODB.Recordset
''Not the best way to refer to a workbook, but convenient for
''testing. it is probably best to refer to the workbook by name.
strFile = ActiveWorkbook.FullName
''Connection string for 2007/2010
strCon = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & strFile _
& ";Extended Properties=""Excel 12.0 xml;HDR=Yes;"";"
cn.Open strCon
''In-line connection string for MS Access
scn = "[;DATABASE=Z:\Docs\Test.accdb]"
''SQL query string
sSQL = "SELECT a.Stuff, b.ID, b.AText FROM [Sheet5$] a " _
& "INNER JOIN " & scn & ".table1 b " _
& "ON a.Stuff = b.AText"
rs.Open sSQL, cn
''Write returned recordset to a worksheet
ActiveWorkbook.Sheets("Sheet7").Cells(1, 1).CopyFromRecordset rs
Another possibility returns a single field from MS Access. This example uses late binding, so you do not need a library reference.
Dim cn As Object
Dim rs As Object
Dim strFile As String
Dim strCon As String
Dim strSQL As String
Dim s As String
Dim i As Integer, j As Integer
strFile = "z:\docs\test.accdb"
strCon = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & strFile
''Late binding, so no reference is needed
Set cn = CreateObject("ADODB.Connection")
Set rs = CreateObject("ADODB.Recordset")
cn.Open strCon
''Select a field based on a numeric reference
strSQL = "SELECT AText " _
& "FROM Table1 a " _
& "WHERE ID = " & Sheets("Sheet7").[A1]
rs.Open strSQL, cn, 3, 3
Sheets("Sheet7").[B1] = rs!AText
OK, this may seem a bit lengthy - Create an Excel-table - in the first row (from column two) you have the Fieldnames Exactly as you have them in the access-table, in the first column you have the desired key-values (e.g. CustomerIDs).
When you run the macro it fills in what it finds...
Sub RefreshData()
Const fldNameCol = 2 'the column with the first fieldname in it'
Dim db, rst As Object
Set db = DBEngine.workspaces(0).OpenDatabase("C:\path\to\db\name.accdb")
Set rst = db.openrecordset("myDBTable", dbOpenDynaset)
Dim rng As Range
Dim showfields() As Integer
Dim i, aRow, aCol As Integer
ReDim showfields(100)
Set rng = Me.Cells
aRow = 1 'if you have the fieldnames in the first row'
aCol = fldNameCol
'***** remove both '' to speed things up'
'On Error GoTo ExitRefreshData'
'Application.ScreenUpdating = False'
'***** Get Fieldnames from Excel Sheet'
Do
For i = 0 To rst.fields.Count - 1
If rst.fields(i).Name = rng(aRow, aCol).Value Then
showfields(aCol) = i + 1
Exit For
End If
Next
aCol = aCol + 1
Loop Until IsEmpty(rng(aRow, aCol).Value)
ReDim Preserve showfields(aCol - 1)
'**** Get Data From Databasetable'
aRow = 2 'startin in the second row'
aCol = 1 'key values (ID) are in the first column of the excel sheet'
Do
rst.FindFirst "ID =" & CStr(rng(aRow, aCol).Value) 'Replace ID with the name of the key field'
If Not rst.NoMatch Then
For i = fldNameCol To UBound(showfields)
If showfields(i) > 0 Then
rng(aRow, i).Value = rst.fields(showfields(i) - 1).Value
End If
Next
End If
aRow = aRow + 1
Loop Until IsEmpty(rng(aRow, aCol).Value)
ExitRefreshData:
Application.ScreenUpdating = True
On Error GoTo 0
End Sub
And if you dont want your fieldnames in the excel sheet replace the paragraph "Get Fieldnames From Excelsheet" with this:
fieldnames = Split("field1name", "", "", "field3name")
For j = 0 To UBound(fieldnames) - 1
For i = 0 To rst.fields.Count - 1
If rst.fields(i).Name = fieldnames(j) Then
showfields(j + fldNameCol) = i + 1
Exit For
End If
Next
Next
ReDim Preserve showfields(UBound(fieldnames) - 1 + fldNameCol)
and add this at the top
dim j as integer
dim fieldnames