Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 5 years ago.
Improve this question
I am new to VBA but I got this project to work on. I have this excel macro file that takes in as user input a specific date (month, day, year) and displays the graph of that day using the data read from the file of that selected date. Each file is named like "xxxx_20170706.csv". I need to modify this file to achieve below.
Each file is generated once a day containing the temperatures recorded every minute. The problem is, because each file is generated only once a day, and it contains the temperature data of the previous day (data keep being recorded from 00:00 till 23:59), you cannot check or use data of today. Basically, you have to wait until the next day if you wanna see the graph of a day.
In order to fix this issue, we decided to generate one file every hour for each day (that is, 24 files generated per day), and each file contains data of the previous hour. For example, at 1:00am of a day, a file is generated containing temperature data of time from 0:00am till 0:59am of the day. This way, we will be able to see the data of even today.
This means, at 5:30 in the morning, there should be 5 different files generated.
The problem I am having is, I am not sure how I can generate a graph from multiple files. The user input will stay the same, and only prompt a specific date they want to see the temperature data of. The output needs to be one graph reflecting temperature data of the date that exist at that point.
Each file will become named "xxxx_20170706_YY.csv" where YY is the 2 digit hours (from 00 up to 23).
I tried using loop incrementing i and adding it to the end of the file name so it can keep reading the existing files of the selected date. However, it did not work and only displayed the graph of data from the last file read.
If there are 5 files for a day, then all of these 5 files must be used for the graph.
How could this be achieved? An idea I came up with is, I create a new excel file, and each time a file is read in a for-loop, I keep adding the data at the bottom of the file, and at the end read that new excel file once, and generate the graph.
Is there any other better way? Thanks in advance for your help!
I would do that this way:
loop through the hours to current hour to get the file names
inside a loop create query (string)
create connection
create recordset
dump data into Excel sheet, which is used as a source for chart
Please, read my comments in below code:
Option Explicit
'needs reference to MS ActiveX Data Objects x.x Library
Sub GetCsvData()
Dim sPath As String, sFileName As String
Dim i As Integer, iCurrHour As Integer
Dim sSQL As String, sConn As String
Dim oConn As ADODB.Connection, oRst As Recordset, oWsh As Worksheet
On Error GoTo Err_GetCsvData
iCurrHour = Hour(Now) - 1
For i = 0 To iCurrHour
sFileName = "xxxx_" & Format(Date, "yyyyMMdd") & "_" & Right("00" & CStr(i), 2) & ".csv"
sSQL = sSQL & "SELECT * FROM " & sFileName & vbCr & "UNION ALL" & vbCr
Next
sSQL = "SELECT fnl.*" & vbCr & "FROM (" & vbCr & sSQL & ") AS fnl;"
MsgBox sSQL
'temporary exit sub; remove below line
GoTo Exit_GetCsvData
sPath = "c:\txtFilesFolder\" 'path have to ends with "\"
'change HDR=Yes if the first row of csv file contains the names of columns
sConn = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & sPath & ";Extended Properties='text;HDR=No;FMT=Delimited';"
Set oConn = New ADODB.Connection
With oConn
.ConnectionString = sConn
.Open
End With
Set oRst = New ADODB.Recordset
oRst.Open sSQL, oConn, adOpenStatic, adLockReadOnly
Set oWsh = ThisWorkbook.Worksheets("SourceSheetForChart")
oWsh.Range("A1").CopyFromRecordset oRst
Exit_GetCsvData:
Set oWsh = Nothing
If Not oRst Is Nothing Then oRst.Close
Set oRst = Nothing
If Not oConn Is Nothing Then oConn.Close
Set oConn = Nothing
Exit Sub
Err_GetCsvData:
MsgBox Err.Description, vbExclamation, Err.Number
Resume Exit_GetCsvData
End Sub
Note: if your text files contain very specific delimiter or decimal separator, you have create schema.ini file.
For further details, please see:
Much ADO about text files
Schema.ini File
Good luck!
Related
I have created a query that works great with no errors in Access, and while trying to translate this to my vba setup I can't figure out why I am not getting any values, even though the query clearly works in access, and causes no errors on the VBA side.
Pulling my hair out here, I have created a side table to see if I could "pseudo-insert" the calculated value, but I get the same issue as above - insert works with no errors in access, goes through in vba with no issues, but doesn't actually insert any data.
I have copied the string queries while pausing code to make sure EVERYTHING matches up between the Access query that works and the VBA query, and haven't found any differences.
I read somewhere since I am trying to pull a "first line" data piece that there may be some HDR setting that I could change, but when I tried to apply any fixes I found they opened another instance of excel and opened a dialogue box.
Please let me know what I am doing wrong here.
Public Function PullNextLineItemNumB(QuoteNum) As Integer
Dim strQuery As String
Dim ConnDB As New ADODB.Connection
Dim myRecordset As ADODB.Recordset
Dim QuoteModifiedNum As String
ConnDB.Open ConnectionString:="Provider = Microsoft.ACE.OLEDB.12.0; data
source=" & ThisWorkbook.Path & "\OEE Info.accdb"
'Query to try and make Access dump the value "18" into the table so I can
grab it after the query is finished, sidestepping excel not working
strQuery = "INSERT INTO TempTableColm (TempColm) SELECT
MAX(MID([Quote_Number_Line],InStr(1,[Quote_Number_Line]," & Chr(34) & "-"
& Chr(34) & ")+1)) AS MaxNum from UnifiedQuoteLog where Quote_Number_Line
like '" & QuoteNum & "*'"
ConnDB.Execute strQuery
'Original query, returns "18" as expected in Access, and null or empty in
the recordset
strQuery = "SELECT MAX(MID([Quote_Number_Line],InStr(1,
[Quote_Number_Line]," & Chr(34) & "-" & Chr(34) & ")+1)) from
UnifiedQuoteLog where Quote_Number_Line like '" & QuoteNum & "*'"
Set myRecordset = ConnDB.Execute(strQuery)
Do Until myRecordset.EOF
For Each fld In myRecordset.Fields
Debug.Print fld.Name & "=" & fld.Value
Next fld
myRecordset.MoveNext
Loop
myRecordset.Close
Set myRecordset = Nothing
ConnDB.Close
Set ConnDB = Nothing
End Function
Actual output from access is "18" which is expected, output from excel's vba recordset is always null or empty string.
It appears I solved the problem, while looking into this apparently the excel operator using ADODB with access is % for LIKE and NOT * (because reasons). As soon as I made that change everything started working.
Can someone explain why that is a thing? I really want to understand why this was a design choice.
Using SQL to query an Excel worksheet without a header row echos the information I have found in all my research. But that isn't what I am getting. Has there been a change in Excel 2016, or is my implementation wrong?
Sub vndrrst()
Dim cn As Object
Set cn = CreateObject("adodb.connection")
strfilename = ThisWorkbook.Path
cn.ConnectionString = _
"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" & strfilename & _
";Extended Properties=""text;HDR=NO;imex=1"";"
cn.Open
Set rs = cn.Execute("select * from My_Text_File.txt where F2='04-62425'")
Set rs = Nothing
Set cn = Nothing
End Sub
The error message reads "No value given for one or more required parameters." at the Set rs statement. When I change F2 to MFGID (the actual field name given in the header of this text file), it both runs and gives accurate output in the debug window... even though HDR=No.
So, how can I use the F field names if the file has a header this time? The reason this becomes interesting is because I have many text/csv/delimited files sent to my FTP, some have headers, some don't, and some have headers sometimes.
Edit: the schema.ini file reads
[My_Text_File.txt]
Format=TabDelimited
in case it matters.
A different related question is ADO Recordset to Excel spreadsheet opens properly in Excel 2007, has a missing parameter in Excel 2013, but the motivation/project of the OP is different, and therefore the answer is irrelevant.
I have scoured the net for days trying to figure this out, but apparently my gaps in Access are too severe and the answer eludes me. Someone has apparently already answered this question, however I'm not able utilize the information.
My specific situation:
Table1 has 30,000+ rows and multiple columns. "Photo Path" is a text field with the path and filename of an image. "Photo" is an OLE Object field currently empty.
What I would like to do is store the image specified in "Photo Path" as an OLE object in "Photo".
Table1 Current State:
Name - Photo Path - Photo
Impala - C:\Cars\Impala.jpg -
Jeep - C:\Cars\Jeep.jpg -
Table1 Desired Result:
Name - Photo Path - Photo
Impala - C:\Cars\Impala.jpg - LONG BINARY DATA
Jeep - C:\Cars\Jeep.jpg - LONG BINARY DATA
I don't know how to execute FileToBlob() against my entire database using the generously provided code. The authors seem to expect me to use a form, which I was unable to get to work as well.
What I think I want is an SQL statement that will execute against every row in Table1 using FileToBlob() or something close to it.
I've tried variations of the following statement in the SQL Query to no avail.
SELECT Table1.[Photo Path], FileToBlob(Table1.[Photo Path],Table1.Photo) As Photo
FROM Table1;
Thank you for taking the time to read this and providing an answer.
Had to figure this one out for myself as there were no responses. For those may follow looking for an actual answer, here it is.
I modified the code that that I found to fit my specific problem.
Create a new module and put the code below in it. If by chance the code does not work, you can try going to Tools-->References and if not already selected, select "Microsoft DAO X.x Object Library" where X.x is the latest library. If it still doesn't run you'll have to check to see if you need to select any other references.
There are so many records to go through, I felt better doing this through code instead of a query that may take a long time to execute and one won't know what is going on. In the code I have it writing to the status bar in Access so you know where you are at (but if the files are small it will probably fly by, but at least you know it is working).
To run the code, just put your cursor anywhere in the routine and I first like to press F8 which steps into the code just to make sure I'm in the right routine. Then press F5 to run the rest of the code. If you want to create a form to run the code instead you can do that too. Just create a button and on the "on click" event add the code:
call Load_Photo()
If you want to see the status updates, make sure the main access window is visible before you run the code (If you run from a form, it will already be there).
Note I renamed the field "Name" in Table1 to "strName" because "Name" is a reserved word. I'd suggest not using "Name" as a field name. You might be OK, but you could run into issues at some point, especially when referencing the field through code. If you choose not to change the field name, change the code.
Also note that the sample code provided stored as a binary. So if you create an Access form to show the records, the image will not automatically appear - there is some other manipulation necessary that I am not familiar with off hand.
Without further ado, here's the code to solution I was looking for:
Option Compare Database
Option Explicit
Public Sub Load_Photo()
On Error GoTo LoadFileError
Dim strSQL As String
Dim rstTable As DAO.Recordset
Dim strStatus As String
Dim count As Integer
Dim strFile As String
Dim nFileNum As Integer
Dim byteData() As Byte
Dim varStatus As Boolean
'
' In case something happens part way through the load, just load photos that have not been loaded yet.
'
strSQL = "Select [strName], [Photo Path], [Photo] from Table1 Where [Photo] is null"
Set rstTable = CurrentDb.OpenRecordset(strSQL)
If rstTable.RecordCount > 0 Then
rstTable.MoveFirst
count = 0
Do While Not rstTable.EOF
strFile = rstTable![Photo Path]
If Len(Dir(strFile)) > 0 Then
nFileNum = FreeFile()
Open strFile For Binary Access Read As nFileNum
If LOF(nFileNum) > 0 Then
count = count + 1
'
' Show user status of loading
'
strStatus = "Loading photo " & count & " for " & rstTable![strName] & ": " & rstTable![Photo Path]
varStatus = SysCmd(acSysCmdSetStatus, strStatus)
DoEvents
ReDim byteData(1 To LOF(nFileNum))
Get #nFileNum, , byteData
rstTable.Edit
rstTable![Photo] = byteData
rstTable.Update
Else
MsgBox ("Error: empty file, can't load for Name = " & rstTable![strName] & " and Photo Path = " & rstTable![Photo Path])
End If
Close nFileNum
Else
MsgBox ("Error: File not found for Name = " & rstTable![strName] & " and Photo Path = " & rstTable![Photo Path])
End If
rstTable.MoveNext
Loop
End If
LoadFileExit:
If nFileNum > 0 Then Close nFileNum
rstTable.Close
strStatus = " "
varStatus = SysCmd(acSysCmdSetStatus, strStatus)
Exit Sub
LoadFileError:
MsgBox "Error " & Err.Number & ": " & Err.Description, vbCritical, "Error on " & strFile
Resume LoadFileExit
End Sub
I have the below excel sheet that is set as shared and is being access by multiple users within the team:
Sheet http://im47.gulfup.com/xQTWqT.png
As the sheet is being updated with new records very often, I have set the below sharing options and that the sheet is being saved and the other users changes are being updated every five minutes (the minimum that you can set):
Options http://im47.gulfup.com/SBX4jf.png
The problem happens when 2 users try to update the database at the same time within the 5 minutes, then excel will prompt them that this cell already contains data and will offer to resolve the changes.
Is there any way to avoid this happening.
I have searched and come across Disconnected ADO Recordset, but I am not very clear on how they could aid in my scenario.
Any help will be highly appreciated.
Never mind I figured it out:
Create a user entry form in one workbook, and create another workbook for the storage of the data (lets call it Book1).
Next this is the code:
Private Sub CommandButton1_Click()
' Add the ActiveX Object Library
' Tools ---> References ---> Microsoft ActiveX Data Objects 2.5 Library
Dim cn As ADODB.Connection
Set cn = New ADODB.Connection
With cn
.Provider = "Microsoft.Jet.OLEDB.4.0"
.ConnectionString = "Data Source=C:\Users\Name\Downloads\Book1.xls;" & _
"Extended Properties=Excel 8.0;"
.Open
End With
strSQL = "INSERT INTO [Sheet1$](Name, Age, Nationality, Amount) values('" & UserForm1.TextBox1.Value & "','" & UserForm1.TextBox2.Value & "','" & UserForm1.TextBox3.Value & "','" & UserForm1.TextBox4.Value & "')"
cn.Execute strSQL
End Sub
I have several excel files that are used for entering data. Files are identical in functionality, one for each service center of ours. In the form there is button that launches a macro which transforms data to table format on another sheet which is then later uploaded to Access db.
Everything worked fine on my own computer. Adding new rows, updating existing rows and deleting existing roles. I had used early binding which lead to problems when I moved files to our network drive. I managed to convert files to late binding but then other problems arose.
Most of the time, uploading to Access isn't working, especially when multiple users try to do stuff at the same time. Most common error code is that I am not using updateable query or that this method doesn't support backwards scrolling. I sorry for not reporting actual error codes, but I can't replicate them at the moment.
My connection code is as follows, it is bit of a mix of copy paste code from different examples.
Opening the connection and other prestuff
Sub excel2access()
Const adUseClient = 3
Const adUseServer = 2
Const adLockOptimistic = 3
Const adOpenKeyset = 1
Const adOpenDynamic = 2
Dim oConn As Object
Dim cmd As Object
Dim rs As Object
Dim r As Long
Dim criteria As String
Dim Rng As Range
Set oConn = CreateObject("ADODB.Connection")
Set cmd = CreateObject("ADODB.Command")
oConn.Open "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source= '" & Range("dbpath").Value & "\" & Range("dbfile").Value & "' ;"
Set rs = CreateObject("ADODB.Recordset")
rs.CursorLocation = adUseClient
rs.CursorType = adOpenStatic
rs.LockType = adLockOptimistic
rs.Open "Select * from need_rows WHERE service_center = '" & Range("scenter_name").Value & "'", oConn
r = 2 ' the start row in the worksheet
Sheets("data").Select
This following bit looks through data in excel sheet and tries to find match from recordset found for that service center. If match is not found new record is created and if match is found the old record is updated.
Do While Len(Range("A" & r).Formula) > 0
With rs
criteria = Range("D" & r).Value
.Find "identifier='" & criteria & "'"
If (.EOF = True) Or (.BOF = True) Then
.AddNew ' create a new record
.Fields("service_center") = Range("scenter_name").Value
.Fields("product_id") = Range("A" & r).Value
.Fields("quantity") = Range("B" & r).Value
.Fields("use_date") = Range("C" & r).Value
.Fields("identifier") = Range("D" & r).Value
.Fields("file_type") = Range("file_type").Value
.Fields("use_type") = Range("E" & r).Value
.Fields("updated_at") = Now
.Update
Else
If .Fields("quantity") <> Range("B" & r).Value Then
.Fields("quantity") = Range("B" & r).Value
.Fields("updated_at") = Now
.Update ' stores the new record
End If
End If
.MoveFirst
End With
r = r + 1
Loop
rs.Close
Set rs = Nothing
Set oConn = Nothing
MsgBox "Confirmation message"
End Sub
Edit: Based on link by barrowc I changed cursor type to adOpenStatic. I made a test with several users trying to upload data at the same time and everything worked perfectly. Until one user stayed in the file and spent quite a while editing data there and then tried to upload data to db and got following error message: https://dl.dropbox.com/u/3815482/vba_error.jpg
Again, I am back where I started from.
Also, I am open to feedback on my code in general as well.
I am using Office 2010.
Am I doing it wrong? All help is appreciated.
You are going to have a lot of issues with the database being locked by other users. This is for several reasons:
From what I can see you are not handling errors. Therefore if your script errors half way through the connection will be left open thus causing lock problems.
By the looks of this, the macros could potentially keep the connection open for a decent amount of time (assuming no errors).
Having created a lot of macros connecting to an MS Access database I can tell you straight up. You are going to have a lot of connection issues where the database is being locked by spreadsheets that someone has left open all day/night due to things such as not handling unexpected errors (the connection never closes).
Even once you fix the problems all you need is ONE person to be using the spreadsheet with the old code and they will continue to lock the database.
One massive problem is that if someone connects to the database when its already open by someone else I believe they inherit the connection type of the already opened database resulting in a daisy chain of write locks. You then need to make sure all connections are severed in order to reset the connection.
You have also not shown us how the data is put into the spreadsheet in the first place. Perhaps you are not correctly closing the connection and that could potentially be the reason why sometimes the database is locked.
There are many different things you could try to get around this:
Easiest would be to use MS Access Front End + MS Access Back End.
Instead of pressing this button and uploading the data through connection strings you could make it save the file in a folder which would then be processed by an ms access database that is sitting there watching the folder. This would mean that you upload script would be written in MS Access and just be processing the files. Wouldn't be as instantaneous as your current approach but all write connections would be coming from the same machine/user in this circumstance.
Persist with the current method: eventually you may get it to a stable state but it will be a lot of frustration and effort as determining the reason for a lock may not always be easy. You could at least look at who has the file locked at the time and work from there but as mentioned earlier they may not have been the cause of the lock. They may have just inherited the lock type.
Personally I like to use MS Excel to display MS Access data for users, but avoid like a plague getting it to update MS Access. However if I was to do this I would do it through the use of oConn.Execute commands not opening a recordset, comparing and slowing pushing as that would keep the connection open too long.
Sorry for the wall of text. Hope this information helps.
sounds to me like Jet isn't reliable enough for your environment. I frequently use SQL Server / Access Data Projects to consolidate information from multiple spreadsheets into a single database backend that doesn't barf when you add a half dozen users.
You could also try using action queries.
First I would try to update using (you may need to format the now value)
dim count as long
oConn.Execute "UPDATE need_rows SET quantity = " & Range("B" & r).Value & ", updated_at = #" & Now & "# WHERE service_center = '" & Range("scenter_name").Value & "' AND identifier='" & Range("D" & r).Value & "' AND quantity <> " & Range("B" & r).Value", count
If count is zero then no row was updated, so either there was no row to update or the quantity hasn't changed. Either way we can try to INSERT, which will fail in the latter case, but without causing problems.
if count = 0 then
count = oConn.Execute "INSERT ...", count
if count = 0 then
' quantity has not changed, so nothing to do
else
' new record inserted
end if
else
' record updated
end if