I am struggling with an interesting problem that I have never experienced before and cannot seem to find any information about online. Here is the setup:
I am looping through a set of rows in a table - each row has a column for sheet name and a link from which I want to webscrape some data. I have written VBA code which loops through each row, creates a new sheet with the correct name, creates a querytable in that sheet, web scrapes the correct link and deletes the querytable.
Here is the code:
Sub WQ_Refresh(wsname As String, wqName As String, wqURL As String, strFC As String)
Dim ws As Worksheet
Dim wq As QueryTable
Dim errno As Long
Dim loopcnt As Integer
Dim refreshTime As Double
Dim lastrow As Long
refreshTime = Timer
Application.StatusBar = "Now downloading " & wqName & " for " & strFC
If wsname = "" Then Exit Sub
Set ws = ThisWorkbook.Sheets(wsname)
If ws.QueryTables.Count > 0 Then
Set wq = ws.QueryTables(1)
wq.Delete
End If
lastrow = ws.Range("A" & ws.Rows.Count).End(xlUp).Row
If lastrow > 1 Then lastrow = lastrow + 1
Set wq = ws.QueryTables.Add(Connection:="URL;" & wqURL, Destination:=ws.Range("A" & lastrow))
errno = 1
loopcnt = 0
Do While Not (errno = 0 And loopcnt < 10)
On Error Resume Next
With wq
.FieldNames = True
.WebFormatting = xlWebFormattingNone
.WebSelectionType = xlAllTables
.Refresh BackgroundQuery:=False
.Delete
End With
loopcnt = loopcnt + 1
errno = Err.Number
If loopcnt = 10 Then HashtagFail wqName
On Error GoTo 0
Loop
If lastrow > 1 Then
ws.Rows(lastrow - 1 & ":" & lastrow).Delete
End If
Set wq = Nothing
Application.StatusBar = "Downloaded " & wqName & " in " & Round(Timer - refreshTime, 0) & " seconds"
For some reason that I cannot understand, the wq.Delete function will correctly delete the created wq but it will also remove a connection in another sheet which is set up manually as part of the file.
To be clear - it does not DELETE the connection. It is still there but if you look at its properties, and go to the Used In tab, it shows as no longer used in any sheet.
I have no idea why this is happening - for me, the code should clearly delete only the connection in the sheet in the loop and not impact any other connections in the file.
This seems like a fundamental excel bug but I'd really like any input you have because I'm completely stuck.
Appreciate your help!
Based on the comment form #DeanDeVilliers The solution that worked was to reference the table by name. Here is the working code if anyone is curious
Sub WQ_Refresh(wsname As String, wqName As String, wqURL As String, strFC As String)
Dim ws As Worksheet
Dim wq As QueryTable
Dim errno As Long
Dim loopcnt As Integer
Dim refreshTime As Double
Dim lastrow As Long
refreshTime = Timer
Application.StatusBar = "Now downloading " & wqName & " for " & strFC
If wsname = "" Then Exit Sub
Set ws = ThisWorkbook.Sheets(wsname)
If ws.QueryTables.Count > 0 Then ws.QueryTables("WQ_" & wqName).Delete
lastrow = ws.Range("A" & ws.Rows.Count).End(xlUp).Row
If lastrow > 1 Then lastrow = lastrow + 1
Set wq = ws.QueryTables.Add(Connection:="URL;" & wqURL, Destination:=ws.Range("A" & lastrow))
errno = 1
loopcnt = 0
Do While Not (errno = 0 And loopcnt < 10)
On Error Resume Next
With wq
.FieldNames = True
.WebFormatting = xlWebFormattingNone
.WebSelectionType = xlAllTables
.Refresh BackgroundQuery:=False
.Name = "WQ_" & wqName
' .Delete
End With
loopcnt = loopcnt + 1
errno = Err.Number
If loopcnt = 10 Then HashtagFail wqName
On Error GoTo 0
ws.QueryTables("WQ_" & wqName).Delete
Loop
If lastrow > 1 Then
ws.Rows(lastrow - 1 & ":" & lastrow).Delete
End If
Set wq = Nothing
Application.StatusBar = "Downloaded " & wqName & " in " & Round(Timer - refreshTime, 0) & " seconds"
End Sub
Related
I tried to set up a code to have a message alert pop-up when a reagent is expiring in 7 days and when the reagent is expired, when the workbook opens. The message should include the reagent that is expired. I only attempted to have the code work for the 'FA Reagents' (A4:A20) and those reagents expiration dates (C4:C20), but I eventually would like to have the code work for all the reagents in this sheet.
Excel Sheet
Private Sub Workbook_Open()
Dim ws As Worksheet
Dim rReagents As Range
Set rReagents = Range("A4:A20")
Dim rExpiration As Range
Set rExpiration = Range("C4:C20")
Dim lLastrow As Long, i As Long
Set ws = Worksheets("Reagent-Equipment")
lLastrow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
With ws
For i = 2 To lLastrow
If .Cells(i, 2) = Date + 7 Then MsgBox ("Reagent expiring in 7 days for " & .Cells(i, 1))
If .Cells(i, 2) = Date Then MsgBox ("Reagent expiring today for " & .Cells(i, 1))
Next
End With
End Sub
This is the code I tried, but I can't a notification to pop-up when the workbook opens when a reagent is expired, or expired within 7 days.
You may find the following code of interest
Private Sub Workbook_Open()
Dim myShortDate As String
Dim myExpired As String
Dim ws As Worksheet
Set ws = Worksheets("Reagent-Equipment")
Dim myReagents As Variant
myReagents = Application.WorksheetFunction.Transpose(ws.Range("A4:A20").Value)
Dim myExpiry As Variant
myExpiry = Application.WorksheetFunction.Transpose(ws.Range("C4:C20").Value)
Dim myIndex As Long
myIndex = 1
Dim myItem As Variant
For Each myItem In myReagents
If Now > VBA.CDate(myExpiry(myIndex)) Then
If VBA.Len(myExpired) = 0 Then
myExpired = vbTab & myReagents(myIndex)
Else
myExpired = myExpired & vbCrLf & vbTab & myReagents(myIndex)
End If
ElseIf Now + 7 > VBA.CDate(myExpiry(myIndex)) Then
If VBA.Len(myShortDate) = 0 Then
myShortDate = vbTab & myReagents(myIndex) & " on " & myExpiry(myIndex)
Else
myShortDate = myShortDate & vbCrLf & vbTab & myReagents(myIndex) & " on " & myExpiry(myIndex)
End If
End If
myIndex = myIndex + 1
Next
If VBA.Len(myExpired) > 0 Then
myExpired = "Expired Reagents" & vbCrLf & vbCrLf & myExpired & vbCrLf & vbCrLf
End If
If VBA.Len(myShortDate) > 0 Then
myShortDate = "Reagents due to expire " & vbCrLf & vbCrLf & myShortDate & vbCrLf & vbCrLf
End If
Dim myMessage As String
myMessage = myExpired & myShortDate
If VBA.Len(myMessage) > 0 Then
MsgBox myMessage, vbCritical
End If
End Sub
I've got a code that i need to run monthly on 500.000 lines of Excel database. Code goes through 1 whole database of different Owbers and splits it onto different tabs - creating them if they don't originally exist. I'm pretty new to coding and creating it and making it work was a big success for me, but it takes ages for it to go through the whole spreadsheet (5mins / 10.000 records - around 3 - 5 hours through the whole spreadsheet).
Is anyone able to have a look and maybe make it work faster? I'm not good with understanding arrays, but i think working on them could make it work better.
Sorry for poor coding:
`
'Loop through spreadsheet and create new tabs if needed
Sub Copy_To_Tab()
Dim Main As Worksheet
Dim a, LR, LR2, LR3 As Integer
Dim Sht As String
Set Main = Sheets(1)
Application.ScreenUpdating = False
a = 2
LR = Main.Range("A" & Rows.Count).End(xlUp).Row
Do Until a > LR
ponownie:
Sht = Main.Range("R" & a).Value
If Sht = "" Then GoTo drugi:
On Error Resume Next
LR2 = Sheets(Sht).Range("A" & Rows.Count).End(xlUp).Row + 1
If Err.Number = 9 Then GoTo stworz:
Main.Range("A" & a & ":AB" & a).Copy Sheets(Sht).Range("A" & LR2)
drugi:
If Main.Range("R" & a).Value <> Main.Range("S" & a).Value Then
ponownie2:
Sht2 = Main.Range("S" & a).Value
If Sht2 = "" Then GoTo nastepny:
On Error Resume Next
LR3 = Sheets(Sht2).Range("A" & Rows.Count).End(xlUp).Row + 1
If Err.Number = 9 Then GoTo stworz2:
Main.Range("A" & a & ":AB" & a).Copy Sheets(Sht2).Range("A" & LR3)
End If
nastepny:
a = a + 1
Loop
Application.ScreenUpdating = True
MsgBox "Finished"
Exit Sub
stworz:
CreateSheet (Sht)
GoTo ponownie:
stworz2:
CreateSheet (Sht2)
GoTo ponownie2:
End Sub
'Create new worksheet and name it
Sub CreateSheet(Nazwa As String)
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets.Add(After:= _
ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count))
ws.Name = Nazwa
Sheets(1).Range("A1:AZ1").Copy ws.Range("A1")
End Sub
`
I'm trying to execute a macro from a new worksheet with a button so that it runs in another worksheet (named "ARF Export").
Unfortunately I don't know how to set the worksheet I want the macro to run in to ("ARF Export"). Please could you advise me on how to proceed?
The error I get when I run this code in a different sheet is:
Error 3265 Item cannot be found in the collection corresponding to the requested name or ordinal in procedure export_data
When I step into Debug I don't get an error until the end but it skips through my For Loop on line 38 next i
for x = 2 To nextrow
DatabaseData.AddNew
For i = 1 To 35
DatabaseData(Cells(1, i).Value) = Worksheets("ARF Export").Cells(x, i).Value
Next i
DatabaseData.Update
Next x
All code below---
Option Explicit
Sub CopyDatatoAccess()
Dim DatabaseConn As ADODB.Connection
Dim DatabaseData As ADODB.Recordset
Dim Pathway
Dim x As Long, i As Long
Dim nextrow As Long
On Error GoTo errorhandler:
Pathway = Worksheets("ARF Export").Range("AR2").Value
nextrow = Worksheets("ARF Export").Range("As2").Value
Set DatabaseConn = New ADODB.Connection
If Worksheets("ARF Export").Range("A2").Value = "" Then
MsgBox "ARF form is not present for Upload"
Exit Sub
End If
DatabaseConn.Open "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & Pathway
Set DatabaseData = New ADODB.Recordset
DatabaseData.Open Source:="ARFs", _
ActiveConnection:=DatabaseConn, _
CursorType:=adOpenDynamic, _
LockType:=adLockOptimistic, _
Options:=adCmdTable
For x = 2 To nextrow
DatabaseData.AddNew
For i = 1 To 35
DatabaseData(Cells(1, i).Value) = Worksheets("ARF Export").Cells(x, i).Value
Next i
DatabaseData.Update
Next x
DatabaseData.Close
DatabaseConn.Close
Set DatabaseData = Nothing
Set DatabaseConn = Nothing
MsgBox "The ARF is now uploaded"
Application.ScreenUpdating = True
Worksheets("ARF Export").Cells.Range("AK2").Value = Worksheets("ARF Export").Cells.Range("AK4").Value
Worksheets("ARF Export").Cells.Range("AK5").Value = Worksheets("ARF Export").Cells.Range("AK4").Value + 1
On Error GoTo 0
Exit Sub
errorhandler:
Set DatabaseData = Nothing
Set DatabaseConn = Nothing
MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure Export_Data"
End Sub
Thanks for the help
-Turns out I needed to reference DatabaseData(Cells(1, i).Value) once i did this
For x = 2 To nextrow
DatabaseData.AddNew
For i = 1 To 35
DatabaseData(Worksheets("ARF Export").Cells(1, i).Value) = Worksheets("ARF Export").Cells(x, i).Value
Next i
DatabaseData.Update
Next x
It worked great. Thank you for your help all!
I try to merge multiple Excel file in to one master sheet.
The code below works perfect because is not duplicating the merged data. My only issue is that is copying also the formulas from other file ... what I need is only the values.
I try to change some part of the code to
PasteSpecial Paste:=xlPasteValues
Error Image
Error 400 Image
Than I get an error (.
Code Used:
Sub sumit()
Dim fso As New FileSystemObject
Dim NoOfFiles As Double
Dim counter As Integer
Dim r_counter As Integer
Dim s As String
Dim listfiles As Files
Dim newfile As Worksheet
Dim mainworkbook As Workbook
Dim combinedworksheet As Worksheet
Dim tempworkbook As Workbook
Dim rowcounter As Double
Dim rowpasted As Integer
Dim delHeaderRow As Integer
Dim Folderpath As Variant
Dim headerset As Variant
Dim Actualrowcount As Double
Dim x As Long
Dim Delete_Remove_Blank_Rows As String
Range("A:A").Clear
Range("B:B").Clear
Range("C:C").Clear
Folderpath = ActiveWorkbook.Sheets(2).Range("I7").Value
headerset = ActiveWorkbook.Sheets(2).Range("F4").Value
Delete_Remove_Blank_Rows = ActiveWorkbook.Sheets(2).Range("F3").Value
NoOfFiles = fso.GetFolder(Folderpath).Files.Count
Dim Files_Count_No_Of_Rows_In_Sheets(1000) As Double 'declare the array of the size of no of files in the folder
Set listfiles = fso.GetFolder(Folderpath).Files
counter = 0
r_counter = 1
rowcounter = 1
Actualrowcount = 0
For Each fls In listfiles
counter = counter + 1
Range("A" & counter).Value = fls.Name
Next
'MsgBox ("count of files in folder is " & NoOfFiles)
Set mainworkbook = ActiveWorkbook
Set combinedworksheet = mainworkbook.Sheets(2)
mainworkbook.Sheets(3).UsedRange.Clear
'MsgBox ("Sheet is clear for the data to be copied")
For i = 1 To NoOfFiles
mainworkbook.Sheets("Combine").Activate
'MsgBox ("Sheet 3 is Activated")
mainworkbook.Sheets("Combine").Range("A" & rowcounter).Select
Application.Workbooks.Open (Folderpath & "\" & Range("A" & i).Value)
Set tempworkbook = ActiveWorkbook
Set newfile = ActiveSheet
rowpasted = rowcounter
'MsgBox ("pointer at " & rowpasted)
newfile.UsedRange.Copy
'MsgBox ("Data is copied")
mainworkbook.Sheets(3).Paste
'MsgBox ("Data is pasted successfully")
'MsgBox ("Blank rows has been deleted " & Remove_Blank_Rows & " " & headerset)
If Delete_Remove_Blank_Rows = "Yes" Then
'If Remove_Blank_Rows = Yes Then
'MsgBox ("Blank rows has been deleted" & Delete_Remove_Blank_Rows)
For x = mainworkbook.Sheets("Combine").Cells.SpecialCells(xlCellTypeLastCell).Row To 1 Step -1
If WorksheetFunction.CountA(mainworkbook.Sheets("Combine").Rows(x)) = 0 Then
mainworkbook.Sheets("Combine").Rows(x).Delete
'MsgBox ("Blank rows has been deleted" & Remove_Blank_Rows)
End If
Next
End If
rowcounter = mainworkbook.Sheets(3).UsedRange.Rows.Count + 1
'MsgBox ("row counter is updated" & rowcounter)
rowpasted = rowcounter - rowpasted
'MsgBox ("No fo rows pasted" & rowpasted)
delHeaderRow = rowcounter - rowpasted
'MsgBox ("Which row to delete" & delHeaderRow)
'MsgBox ("Pointer at row beforw deletion" & rowpasted)
If headerset = "Yes" Or headerset = "YES" Or headerset = "yes" Then
If delHeaderRow <> 1 Then
mainworkbook.Sheets(3).Rows(delHeaderRow).EntireRow.Delete
rowcounter = rowcounter - 1
rowpasted = rowpasted - 1
Else
End If
Else
End If
'MsgBox ("Header deleted")
'MsgBox ("row counter is updated" & rowcounter)
combinedworksheet.UsedRange.ClearOutline
'combinedworksheet.
tempworkbook.Close
'MsgBox ("no of rows are abt to get pasted in sheet 1")
Files_Count_No_Of_Rows_In_Sheets(i) = rowpasted
Actualrowcount = Actualrowcount + rowpasted
Next i
mainworkbook.Sheets(1).UsedRange.ClearContents
For Each fls In listfiles
r_counter = r_counter + 1
mainworkbook.Sheets(1).Range("A" & r_counter).Value = fls.Name
mainworkbook.Sheets(1).Range("B" & r_counter).Value = Files_Count_No_Of_Rows_In_Sheets(r_counter - 1)
mainworkbook.Sheets(1).Range("A" & r_counter, "B" & r_counter).Borders.Value = 1
Next
mainworkbook.Sheets(1).Range("B" & r_counter + 1).Interior.ColorIndex = 46
mainworkbook.Sheets(1).Range("B" & r_counter + 1).Value = Actualrowcount
mainworkbook.Sheets(1).Range("B" & r_counter + 1).Borders.Value = 1
mainworkbook.Sheets(1).Range("A1", "B1").Interior.ColorIndex = 46
mainworkbook.Sheets(1).Range("A1", "B1").Borders.Value = 1
mainworkbook.Sheets(1).Range("A1").Value = "Files List"
mainworkbook.Sheets(1).Range("B1").Value = "No Of Rows"
MsgBox ("List of Files are Availabe in sheet 1..Total " & NoOfFiles & " Files Combiled")
End Sub
In the error image there is mainworkbook.Sheets(3).Paste xlPasteValues which is wrong and should be mainworkbook.Sheets(3).Cells(rowcounter, 1).PasteSpecial xlPasteValues.
The code below is what i use to rename a bunch of sheets within a workbook. it works perfectly. It renames the sheet based off of a cell in that sheet. but now i have two sheets trying to use the same name. So i want to keep the same code but add a loop so if that happens, it will add a "2" to the second sheet. Ie cell contains "John Doe". Sheet will rename to "John Doe" and the next sheet that tries to use it will rename "John Doe 2"
Thank you
Sub RenameLaborLog()
Dim rs As Worksheet
For Each rs In Sheets
rs.Name = Split(rs.Range("H4").Value, " ")(1) & ", " & Left(Split(rs.Range("H4").Value)(0), 1) & "."
Next rs
End Sub
just to show another way you can reach your goal
Sub RenameLaborLog()
Dim rs As Worksheet, i As Long, str As String
On Error Resume Next
For Each rs In Sheets
str = Split(rs.Range("H4").Value, " ")(1) & ", " & Left(Split(rs.Range("H4").Value)(0), 1) & "."
rs.Name = str
i = 1
While Err.Number <> 0 And i < 20
Err.Clear: i = i + 1
rs.Name = str & i
Wend
If Err.Number <> 0 Then MsgBox "Error: " & rs.Name & " cant be set to any " & str: Exit Sub
Next rs
End Sub
it tries to set the name (and if that is not working it sets the name & 2 - 19 (if that doesnt work, it pops up a message box and exits the sub)
Use a controlled error to adjust the string containing the worksheet name until it find something it can use.
Sub RenameLaborLog()
Dim rs As Worksheet, snam As String, idupe As Long
On Error GoTo bm_Dupe_WS_Name
For Each rs In Sheets
idupe = 1
snam = Split(rs.Range("H4").Value, " ")(1) & ", " & _
Left(Split(rs.Range("H4").Value)(0), 1) & "."
rs.Name = snam
Next rs
bm_Dupe_WS_Name:
If idupe > 8 Then
Debug.Print Err.Number & ": " & snam & " - " & Err.Description
Exit Sub
ElseIf Right(snam, 1) = CStr(idupe) Then
snam = Trim(Left(snam, Len(snam) - 1))
End If
idupe = idupe + 1
snam = snam & Chr(32) & idupe
Resume
End Sub
I have it set yo attempt a numerical suffix up to 9. It it reaches that, it reports the error and exits the sub. I would not recommend running this without an escape clause. If nothing else, you may run into an illegal character when parsing the string for the worksheet name.
Based on the link #Scott Craner provided in his comment, I am providing another solution that I believe is a bit more functional and cleaner and easier to read.
Sub RenameLaborLog()
Dim rs As Worksheet, sName As String
For Each rs In Sheets
sName = Split(rs.Range("H4").Value, " ")(1) & ", " & Left(Split(rs.Range("H4").Value)(0), 1) & "."
i = 1
Do
If Not WorksheetExist(sName) Then
rs.Name = sName
Exit Do
Else: sName = sName & "_" & i + 1
End If
Loop
Next rs
End Sub
Function WorksheetExist(sName As String, Optional wb As Workbook) As Boolean
Dim wbCheck As Workbook, ws As Worksheet
If wb Is Nothing Then Set wbCheck = ThisWorkbook Else: Set wbCheck = wb
WorksheetExist = False
For Each ws In wbCheck.Worksheets
If ws.Name = sName Then
WorksheetExist = True
Exit For
End If
Next
End Function
Jeeped beat me to it, but here is another possible adjustment you could make:
Sub RenameLaborLog()
Dim rs As Worksheet, wsName As String, wsCheck As Worksheet, i As Integer
For Each rs In Sheets
' Get the sheet name
wsName = Split(rs.Range("H4").Value, " ")(1) & ", " & Left(Split(rs.Range("H4").Value)(0), 1) & "."
' Check if it exists
Set wsCheck = Nothing: On Error Resume Next: Set wsCheck = wsName: On Error GoTo 0
' Check if multiples already exist
While Not wsCheck Is Nothing
' If even one exits, "i" will be iterated
i = i + 1
Set wsCheck = Nothing: On Error Resume Next: Set wsCheck = wsName & "_" & i: On Error GoTo 0
Wend
' If at least one name already existed, name it with the current iteration.
If Not i = 0 Then wsName = wsName & "_" & i
rs.Name = wsName
Next rs
Set rs = Nothing: Set wsCheck = Nothing
End Sub