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.
Related
I have created a user form in excel to save my records in a sheets like sheet1.
But after few days working with this UserForm, it is now goes slower, because of heavy data saving in sheet1.
Now I want to save all records to a database and want to keep clean my sheet1.
So I can work on my UserForm easily or without any delay. Also wants updates my record by calling it via serial numbers.
but I don't want to keep any record in my sheet1.
my little code is below: -
Sub cmdAdd_Click()
On Error GoTo ErrOccured
BlnVal = 0
If BlnVal = 0 Then Exit Sub
With Application
.ScreenUpdating = False
.EnableEvents = False
End With
Dim txtId, txtName, GenderValue, txtLocation, txtCNum, txtEAddr, txtRemarks
Dim iCnt As Integer
iCnt = fn_LastRow(Sheets("Data")) + 1
If frmData.obMale = True Then
GenderValue = "Male"
Else
GenderValue = "Female"
End If
With Sheets("Data")
.Cells(iCnt, 1) = iCnt - 1
.Cells(iCnt, 2) = frmData.txtName
.Cells(iCnt, 3) = GenderValue
.Cells(iCnt, 4) = frmData.txtLocation.Value
.Cells(iCnt, 5) = frmData.txtEAddr
.Cells(iCnt, 6) = frmData.txtCNum
.Cells(iCnt, 7) = frmData.txtRemarks
.Columns("A:G").Columns.AutoFit
.Range("A1:G1").Font.Bold = True
.Range("A1:G1").LineStyle = xlDash
End If
End With
Dim IdVal As Integer
IdVal = fn_LastRow(Sheets("Data"))
frmData.txtId = IdVal
ErrOccured:
'TurnOn screen updating
Application.ScreenUpdating = True
Application.EnableEvents = True
End Sub
I will always be grateful to you.
Then, please try the next way. I will try creating of the necessary DB, table and fields using Excel VBA, too:
Copy the next piece of code which will create an empty DB, on the path you want:
Sub CreateEmptyDB()
Dim strPath As String, objAccess As Object
strPath = "C:\Your path\testDB"
Set objAccess = CreateObject("Access.Application")
Call objAccess.NewCurrentDatabase(strPath)
objAccess.Quit
End Sub
Programatically create the necessary table with its fields (`Start Date' added only to see how this type of data is handled...):
Sub createTableFields()
'It needs a reference to `Microsoft ActiveX Data Objects 2.x Library` (x = 2 to 9)
Dim Catalog As Object, cn As ADODB.Connection
Dim dbPath As String, scn As String, strTable As String
dbPath = "C:\Teste VBA Excel\testAccess\testDB.accdb"
strTable = "MySpecial_Table"
scn = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & dbPath & ";"
Set Catalog = CreateObject("ADOX.Catalog")
Set cn = New ADODB.Connection
With cn
.Open scn
.Execute "CREATE TABLE " & strTable & " ([Name] text(255) WITH " & _
"Compression, " & "[Gender] text(255) WITH Compression, " & _
"[Location] text(255) WITH Compression, " & _
"[Address] text(255) WITH Compression, " & _
"[Number] number, " & _
"[Remarks] text(255) WITH Compression, " & _
"[Start Date] datetime)"
End With
cn.Close
End Sub
Add records to the newly created DB/Table:
Sub FillDataInDB()
'It needs a reference to `Microsoft ActiveX Data Objects 2.x Library` (x = 2 to 9)
Dim AccessDB As String, strTable As String, sql As String
Dim con As ADODB.Connection, rs As ADODB.Recordset, lastNo As Long
AccessDB = "C:\Teste VBA Excel\testAccess\testDB.accdb"
strTable = "MySpecial_Table"
Set con = CreateObject("ADODB.connection")
con.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & AccessDB
sql = "SELECT * FROM " & strTable
Set rs = CreateObject("ADODB.Recordset")
rs.CursorType = 1 'adOpenKeyset on early binding
rs.LockType = 3 'adLockOptimistic on early binding
rs.Open sql, con
If rs.RecordCount = 0 Then
lastNo = 0 'when no records in the table
Else
rs.MoveLast: lastNo = rs("Number") 'the last recorded value
End If
rs.AddNew
rs("Name") = "Test name" 'frmData.txtName
rs("Gender") = "Test gender" 'GenderValue
rs("Location") = "Test Location" 'frmData.txtLocation.Value
rs("Address") = "Test Address" 'frmData.txtEAddr
rs("Number") = IIf(lastNo = 0, 100, lastNo + 1) 'auto incrementing against the last value
'but starting from 100
'you can use frmData.txtCNum
rs("Remarks") = "Remarkable table..." 'frmData.txtRemarks
rs("Start Date") = Date
rs.Update
rs.Close: con.Close
Set rs = Nothing: Set con = Nothing
End Sub
Run the first two pieces of code in consecutive order (only once) and then start playing with the third one...
You can read the newly created DB Table (returning in an Excel sheet) in this way:
Sub ADO_Connection_ReadTable()
Dim conn As New Connection, rec As New Recordset, sh As Worksheet
Dim AccessDB As String, connString, query As String, strTable As String
AccessDB = "C:\Teste VBA Excel\testAccess\testDB.accdb"
strTable = "MySpecial_Table"
Set sh = ActiveSheet 'use here the sheet you want
connString = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & AccessDB
conn.Open connString
query = "SELECT * from " & strTable & ";"
rec.Open query, conn
'return in the sheet
sh.cells.ClearContents
'getting data from the recordset if any and returning some in columns A:B:
If (rec.RecordCount <> 0) Then
Do While Not rec.EOF
With sh.Range("A" & sh.cells(Rows.count, 1).End(xlUp).row).Offset(1, 0)
.Value2 = rec.fields(0).Value
.Offset(0, 1).Value2 = rec.fields(3)
End With
rec.MoveNext
Loop
End If
rec.Close: conn.Close
End Sub
You can use a query to return specific data according to a specific table field. You can find plenty of examples on the internet.
I tried also showing how to handle an automate recording for the 'Number' field. Of course, if you are able to keep track of it in a different way, you can record it as you need/wont.
Please, test the above code(s) and send some feedback. You can use the DB path as a Private constant at the module level and much other ways to optimize the code. It is just a minimum workable solution only showing the way... :)
I have an MS Access database. The database version is 2002-2003 (therefore mdb). From that database I am exporting several fields into a temporary table in order to represent them on a website. As there is a field which has several comma-separated entries, I am splitting them so that each record has only one entry per field.
Imagine a German-English dictionary with the following record:
en | de
building | Gebäude,Bauwerk
I want to split it as follows:
en | de
building | Gebäude
building | Bauwerk
The VBA function that I am using used to work fine. The database has around 100.000 records. Splitting used to take around 30 minutes. Now it takes the whole day.
This is the function:
Public Sub commasplitfield4()
Dim rstObj As DAO.Recordset, dbObj As DAO.Database
Dim InsertSQL As String
Set dbObj = CurrentDb()
Set rstObj = dbObj.OpenRecordset("qry-export")
DoCmd.SetWarnings False
Do While Not rstObj.EOF
Dim memArr() As String
memArr = Split(rstObj.Fields("field4"), ",")
For i = 0 To UBound(memArr)
InsertSQL = "INSERT INTO exporttemp(field1,field2,field3,field4) VALUES (""" & rstObj.Fields("field1") _
& """, """ & rstObj.Fields("field2") _
& """, """ & rstObj.Fields("field3") & """, """ & memArr(i) & """)"
DoCmd.RunSQL (InsertSQL)
Next
rstObj.MoveNext
Loop
DoCmd.SetWarnings True
End Sub
I cannot say when exactly it started to take so long, but I can say that changing from Windows 7 to Windows 10 didn't make a difference. I am on Windows 10 for a long time and it still used to work well. Also moving from Access 2007 to 2010 and then to 2019 didn't make a difference, at least not at once.
In order to check where the error could lie I went through the following checklist:
I compact the database before starting the function
I tried to start Access in Windows 7 compatibility mode
I removed unused fields
I started the performance analyser and made the changes that were proposed (in two fields I changed the data type)
I split the database into a backend only with the tables and a frontend which contains queries and modules
I exported the content of the backend into a text file and re-imported it into a newly created backend
I stopped the Antivirus while performing the function (although Antivirus used very little processor capacity)
None of that made a notable difference.
Any idea?
The by far best answer was the one from HansUp. Instead of a whole day it takes a couple of minutes now. I cannot even thank HansUp properly because he put the solution in a side comment.
Surprisingly, there is actually little that I had to change in the code. So, the solution was to modify the code as follows:
Public Sub commasplitfield4()
Dim rstObj As DAO.Recordset, dbObj As DAO.Database
Dim InsertSQL As String
Set dbObj = CurrentDb()
Set rstObj = dbObj.OpenRecordset("qry-export")
DoCmd.SetWarnings False
Do While Not rstObj.EOF
Dim memArr() As String
memArr = Split(rstObj.Fields("field4"), ",")
For i = 0 To UBound(memArr)
InsertSQL = "INSERT INTO exporttemp(field1,field2,field3,field4) VALUES (""" & rstObj.Fields("field1") _
& """, """ & rstObj.Fields("field2") _
& """, """ & rstObj.Fields("field3") & """, """ & memArr(i) & """)"
'DoCmd.RunSQL (InsertSQL)
dbObj.Execute (InsertSQL), dbFailOnError 'this line made the difference
Next
rstObj.MoveNext
Loop
'DoCmd.SetWarnings True
End Sub
I can't explain the exact cause of your problem, but I think it takes a lot of time to loop through the recordset and loop through the Array.
The task of separating characters with commas seems to be faster using vba in Excel.
The example source data was for 1000000 records,
The contents separated by each comma were written in two per record, and the records of the converted data were tested with data of 2000000.
Import the original data of Access into Excel (Sheets(1)). (Example table2) ~~> 0.7617188 seconds
Convert the data by separating the data of the imported Sheets(1) with commas.
--> Sheets(2) ~~> 21.58594 seconds
Load data from Sheets(2) by Access applicantion.
~~> 5 minutes
Import the original data of Access
Sub exeSQLgetdata()
Dim Rs As ADODB.Recordset
Dim strConn As String
Dim i As Integer
Dim Fn As String
Dim Ws As Worksheet
Dim st, et
st = Timer
Set Ws = Sheets(1)
Fn = ThisWorkbook.Path & "\" & "Database9.accdb" '<~~ your database path & name
strConn = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=" & Fn & ";"
Set Rs = CreateObject("ADODB.Recordset")
strSQL = "Select field1,field2, field3, field4 from [table2]" '<~~ your raw data table
Rs.Open strSQL, strConn
If Not Rs.EOF Then
With Ws
For i = 0 To Rs.Fields.Count - 1
.Cells(1, i + 1) = Rs.Fields(i).Name
Next i
.UsedRange.Offset(1).Clear
.Range("a2").CopyFromRecordset Rs
End With
End If
Rs.Close
Set Rs = Nothing
et = Timer
Debug.Print "Get Data time : " & et - st & " seconds" '<~~ get data from access database
End Sub
Convert the data by separating the data of the imported Sheets(1) with commas.
Sub splitData()
Dim vR(1 To 1000000, 1 To 4)
Dim vDB As Variant
Dim i As Long, n As Long
Dim k As Variant, v As Variant
Dim Ws As Worksheet
Dim toWs As Worksheet
Dim st, et
st = Timer
Set Ws = Sheets(1)
Set toWs = Sheets(2)
vDB = Ws.Range("a1").CurrentRegion
For i = 1 To UBound(vDB, 1)
k = Split(vDB(i, 4), ",")
For Each v In k
n = n + 1
vR(n, 1) = vDB(i, 1)
vR(n, 2) = vDB(i, 2)
vR(n, 3) = vDB(i, 3)
vR(n, 4) = v
Next v
DoEvents
Next i
With toWs
.UsedRange.Clear
.Range("a1").Resize(UBound(vR, 1), UBound(vR, 2)) = vR
End With
et = Timer
Debug.Print "Split time : " & et - st & " seconds"
End Sub
the code below copies "ADXL364" sheet in my active worksheet, but is there way that I can copy the sheet if it contains "XL364" or "364"
if I put asterisk 'C:\data[adxl364.xls]*ADXL364_QC'!A1 in my code it does not work.
Sub GetRange()
With Range("A:Z")
.Formula = "=If('C:\data\[adxl364.xls]ADXL364_QC'!A1 > 0,'C:\data\[adxl364.xls]ADXL364_QC'!A1,Text(,))"
.Formula = .Value
End With
End Sub
the long code will be getting the location of file from the user then copying a worksheet that contains ADXL364 or XL364
With ActiveWorkbook
Sheets.Add.Name = "Flow_table"
Application.EnableEvents = False
TP_location = Left(TextBox1.Value, InStrRev(TextBox1.Value, "\"))
TP_filename = Right(TextBox1.Value, Len(TextBox1.Value) - InStrRev(TextBox1.Value, "\"))
TP_filename = "[" & TP_filename & "]"
TP_formula = "'" & TP_location & TP_filename & TextBox2.Value & "'!A1"
getcellvalue = "=if(" & TP_formula & ">0," & TP_formula & "," & """"")"
With Range("A:Z")
.Formula = getcellvalue
.Formula = .Value
End With
Sheets.Add.Name = "Job_lists"
End With
Unload UserForm2
End Sub
An ugly, but possible, way would be with a brute force error trapping technique.
However, a more elegant solution might be to use ADO. You could for example run two 'queries': the first on the table schema which would give you your sheet names in the specified file, and the second on the found sheet name. This would produce a RecordSet containing the data of your closed sheet which can be written directly to a Range using the .CopyFromRecordset method. Of course, you could just run the first query to find your sheet name and move on as you have in your posted code.
The example below shows the code for the two queries. It's all late bound so you needn't reference the ADO library but I'll leave that decision to you. I've put a few constants at the top of the module which might need changing depending on which version of Excel you have. You'll also need to write your own error handling (especially to close the connection) but, again, I'll leave that one for you.
Option Explicit
Private Const SCHEMA_TABLES As Integer = 20
Private Const OPEN_FORWARD_ONLY As Integer = 0
Private Const LOCK_READ_ONLY As Integer = 1
Private Const CMD_TEXT As Long = 1
Private Const PROVIDER As String = "Microsoft.ACE.OLEDB.12.0"
Private Const XL_PROP As String = """Excel 12.0;HDR=No"""
Private Const SHEETS_FIELD_NAME As String = "TABLE_NAME"
Public Sub AcquireData()
Dim fPath As String
Dim fName As String
Dim key As String
Dim addr As String
Dim oConn As Object
Dim oRS As Object
Dim connString As String
Dim sql As String
Dim found As Boolean
Dim sheetField As String
'Define the path and file name
fPath = "C:\Users\User\Documents\StackOverflow"
fName = "closed_book.xlsx"
'Define the search key
key = "XL364"
'Define the address of closed worksheet
'If reading one cell then use [address:address], eg "A1:A1"
addr = "A1:E5"
'Late bind the ADO objects
Set oConn = CreateObject("ADODB.Connection")
Set oRS = CreateObject("ADODB.Recordset")
'Open conection
connString = "Provider=" & PROVIDER & ";" & _
"Data Source=" & fPath & "\" & fName & ";" & _
"Extended Properties=" & XL_PROP & ";"
oConn.Open connString
'Search for the sheet name containing your key
'in the tables (ie sheets) schema
found = False
oRS.Open oConn.OpenSchema(SCHEMA_TABLES)
Do While Not oRS.EOF
sheetField = oRS.Fields(SHEETS_FIELD_NAME).Value
If InStr(sheetField, key) > 0 Then
found = True
Exit Do
End If
oRS.MoveNext
Loop
oRS.Close
'Read the target data
If found Then
sql = "SELECT * FROM [" & _
sheetField & addr & "];"
oRS.Open sql, oConn, OPEN_FORWARD_ONLY, LOCK_READ_ONLY, CMD_TEXT
'Write the data to your worksheet
If Not oRS.EOF Then
ThisWorkbook.Worksheets("Sheet1").Range("A1") _
.CopyFromRecordset oRS
End If
End If
'Housekeeping
oRS.Close
Set oRS = Nothing
oConn.Close
Set oConn = Nothing
End Sub
You can test if the text "XL364" is in the sheet name by looping through each sheet and using the InStr (in string) function. e.g.:
For Each ws in Workbooks.Open(filepathStringFromUserInput)
If InStr(1, ws.Name, "XL364") > 0 Then
MsgBox "hi"
'Set hwSheet = ws
End If
Next ws
With hwSheet
'do some code eg:
.Range("A1").Value = "Hi"
End With
I have the code below and it seems to be taking a while to open the recordset and run the query attached (62 seconds to be exact). While 1 minute is fine, when I need to do this 13 times, it begins to take a long time to run the code.
I've debugged the code down to just the opening of the recordset taking the longest time.
My question is: Is there a method to run this faster? (i'm connecting to MS Access 2013 from Excel 2013)
Thanks in advance,
Rich
Sub GetUnits2()
'Declaring the necessary variables.
Dim con As Object
Dim rs As Object
Dim AccessFile As String
Dim strTable As String
Dim SQL As String
Dim myValues() As Variant
Dim i As Long
Dim k As Long
Dim j As Integer
Dim SheetName As String
Dim WeekNumber As Long
Dim year As Long
Dim Model1 As String
Dim Model2 As String
Dim xlrow As Integer
Dim xlcol As Integer
SheetName = "Sheet2"
Sheets(SheetName).Select
Model1 = Sheets(SheetName).Cells(3, 2).Value
Model2 = Sheets(SheetName).Cells(4, 2).Value
'Disable screen flickering.
Application.ScreenUpdating = False
'Specify the file path of the accdb file. You can also use the full path of the file like:
AccessFile = "C:\Users\rich.wolff\Desktop\2014POSDatabase\HMKPOSDatabase2014.accdb"
On Error Resume Next
'Create the ADODB connection object.
Set con = CreateObject("ADODB.connection")
'Check if the object was created.
If Err.Number <> 0 Then
MsgBox "Connection was not created!", vbCritical, "Connection error"
Exit Sub
End If
On Error GoTo 0
'Open the connection.
con.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & AccessFile
'Set Current Week, Year, & Starting Cell
WeekNumber = Sheets(SheetName).Cells(8, 14).Value
year = Sheets(SheetName).Cells(9, 14).Value
xlcol = 14 'Starting Column
xlrow = 11 'Starting Row
'Open Query Loop
For k = 1 To 1
SQL = "SELECT Sum(StoreSalesData.QTY) AS Units"
SQL = SQL & " FROM VSNConversionData INNER JOIN ([Sleepys Store List] INNER JOIN StoreSalesData ON [Sleepys Store List].[Store Code] = StoreSalesData.STR) ON VSNConversionData.VSN = StoreSalesData.VSN"
SQL = SQL & " WHERE (((VSNConversionData.VSNStyle)='" & Model2 & "') AND ((StoreSalesData.WeekNum)=" & WeekNumber & ") AND ((StoreSalesData.Year)=" & year & ") AND ((StoreSalesData.STR) In (SELECT FloorModels2.[Source Org]"
SQL = SQL & " FROM FloorModels2"
SQL = SQL & " WHERE (((FloorModels2.[Source Org]) In (SELECT FloorModels2.[Source Org]"
SQL = SQL & " FROM FloorModels2"
SQL = SQL & " WHERE (((FloorModels2.WeekNumber)=" & WeekNumber & ") AND ((FloorModels2.Year)=" & year & ") AND ((FloorModels2.VSNStyle)='" & Model1 & "')))) AND ((FloorModels2.WeekNumber)=" & WeekNumber & ") AND ((FloorModels2.Year)=" & year & ") AND ((FloorModels2.VSNStyle)='" & Model2 & "')))));"
On Error Resume Next
'Create the ADODB recordset object.
Set rs = CreateObject("ADODB.recordset")
'Check if the object was created.
If Err.Number <> 0 Then
Set rs = Nothing
Set con = Nothing
MsgBox "Connection was not created!", vbCritical, "Connection error"
Exit Sub
End If
On Error GoTo 0
'Set thee cursor location.
rs.CursorLocation = 3 'adUseClient on early binding
rs.CursorType = 1 'adOpenKeyset on early binding
'Open the recordset.
rs.Open SQL, con
'Redim the table that will contain the filtered data.
ReDim myValues(rs.RecordCount)
If Not (rs.EOF And rs.BOF) Then
rs.MoveFirst
Dim dbcol As Integer
dbcol = 0
Worksheets(SheetName).Cells(xlrow, xlcol).ClearContents
Worksheets(SheetName).Cells(xlrow, xlcol).Value = rs(dbcol).Value
Else
rs.Close
con.Close
Set rs = Nothing
Set con = Nothing
Application.ScreenUpdating = True
MsgBox "There are no records in the recordset!", vbCritical, "No Records"
Exit Sub
End If
'Close the recordet
rs.Close
Set rs = Nothing
If WeekNumber = 1 Then
year = year - 1
WeekNumber = 52
Else
year = year
WeekNumber = WeekNumber - 1
End If
' Next Column
xlcol = xlcol - 1
Next
'End Query Loop
con.Close
Set rs = Nothing
Set con = Nothing
Application.ScreenUpdating = True
End Sub
Have I wandered accidentally into a PHP forum?
Declare the ADODB libraries using tools:references - they will run faster, you get intellisense and a listing of all the available properties and options in the Object Browser, and you gain the ability to run the query asynchronously.
That's Early-Binding, an improvement on Late-Binding.
Next, open the Recordset object with dbForwardOnly (slightly faster) and dump it into a VBA array variant with the Recordset.GetRows method: transpose the array in your code, and write it to the range.
I can see that you've made progress on optmising the SQL: try saving it as a parameter query in the database. The ADODB.Command object can open a named query, populate the parameters, and return a recordset - the query itself may or may not run faster, but the lead time to parse the SQL will be significantly faster.
You could try:
Sub M_snb()
c00 = "C:\Users\rich.wolff\Desktop\2014POSDatabase\HMKPOSDatabase2014.accdb"
With Sheets("sheet2")
sn = Array(.Cells(3, 2), .Cells(4, 2), .Cells(8, 14), .Cells(9, 14)) ' model 1, model 2, weeknumber, year
End With
For j = 1 To 13
c01 = "SELECT Sum(StoreSalesData.QTY) AS Units"
c01 = c01 & " FROM VSNConversionData INNER JOIN ([Sleepys Store List] INNER JOIN StoreSalesData ON [Sleepys Store List].[Store Code] = StoreSalesData.STR) ON VSNConversionData.VSN = StoreSalesData.VSN"
c01 = c01 & " WHERE (((VSNConversionData.VSNStyle)='" & sn(1) & "') AND ((StoreSalesData.WeekNum)=" & sn(2) & ") AND ((StoreSalesData.Year)=" & sn(3) & ") AND ((StoreSalesData.STR) In (SELECT FloorModels2.[Source Org]"
c01 = c01 & " FROM FloorModels2"
c01 = c01 & " WHERE (((FloorModels2.[Source Org]) In (SELECT FloorModels2.[Source Org]"
c01 = c01 & " FROM FloorModels2"
c01 = c01 & " WHERE (((FloorModels2.WeekNumber)=" & sn(2) & ") AND ((FloorModels2.Year)=" & sn(3) & ") AND ((FloorModels2.VSNStyle)='" & sn(0) & "')))) AND ((FloorModels2.WeekNumber)=" & sn(2) & ") AND ((FloorModels2.Year)=" & sn(3) & ") AND ((FloorModels2.VSNStyle)='" & sn(1) & "')))));"
With CreateObject("ADODB.recordset")
.Open c01, "Provider=Microsoft.Jet.OLEDB.12.0;Data Source=" & c00
Sheets("sheets2").Cells(11, 14 + j).CopyFromRecordset .DataSource
End With
Next
End Sub
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.