How to extract data form closed workbook matching more than one criteria without oping the workbook? - vba

I need to extract data from one big Excel matching row and column criteria. I have big Excel with 100 or sheets and bigger than 120mb. I need to extract the data from that workbook to current workbook matching sheet name, column criteria and row criteria.
I have a code which can do that but the problem is if every time I open the workbook in the background and close it that takes too much time. So how can I it without opening it in the background? I have read about ADO connecting but I actually do not understand the code and also I don't understand about how can I do it with excel4macro.
I am including my code. I am new to coding so there will be lots of mistakes I guess. This is for my work purpose.
Sub WCDMA_Network_Planning_DumpData_Extract()
Dim ws As Worksheet
Dim wsname As String
Dim wsnamed As String
Dim finalrow As Integer
Dim finalcol As Integer
Dim paraname1() As Variant
Dim columnnumber As Integer
Dim filename As String
Dim cellnm1() As Variant
Dim rownumber As Integer
Dim firstrow As Integer
Dim firstcolumn As Integer
Dim value() As Variant
Dim add As String
Dim firstrow2 As Integer
Dim finalrow2 As Double
Dim firstcolumn2 As Integer
Dim ra As Range
Dim add2 As String
Dim add3 As String
Dim add4 As String
Dim add5 As String
Dim var As Integer
Dim add6 As String
Dim mypath As String
Dim ol As Integer
Dim firstcelladd As String
Dim firstcell As Range
Dim rl As Integer
Application.ScreenUpdating = False
''this is to get the activehseet name which i will match with the search workbook
filename = ActiveWorkbook.Name
wsname = ActiveSheet.Name
' this is to find "Cell Name" which is my column criteria
Set ra = Range("A1:F10").find(what:="Cell Name", lookat:=xlWhole)
add = ra.Address
add5 = Mid(add, 2, 1) & "1"
add2 = Mid(add, 2, 1) & "22000"
'first column and last row finding of current sheet where i want to extract data
firstcolumn = Range("A1:F10").find(what:="Cell Name", lookat:=xlWhole).Column
firstrow = Range("A1:F10").find(what:="Cell Name", lookat:=xlWhole).Row + 1
finalcolumn = Sheets(wsname).Range("GG2").End(xlToLeft).Column
finalrow = Sheets(wsname).Range(add2).End(xlUp).Row
'array diclaration where i will put my serch criteria and matched value
ReDim paraname1(1 To finalcolumn)
ReDim value(1 To 23000, 1 To finalcolumn)
ReDim cellnm1(1 To finalrow)
var = firstcolumn - 1
'this is for active sheet where i put my seche criteria for row and clumn value
For I = firstcolumn To finalcolumn
'column criteria for search
paraname1(I) = Cells(firstrow - 1, I).value
Next
'row criteria
For j = firstrow To finalrow
cellnm1(j) = Cells(j, firstcolumn).value
Next
''this is the workbook form where i want to get the value
Application.ScreenUpdating = False
mypath = "D:\Office Work\VBA Work\3G Radio Network Planning Data Template.xlsm"
Workbooks.Open filename:=mypath
Application.EnableEvents = False
''select the sheet form whcih i will get the data
Workbooks("3G Radio Network Planning Data Template").Activate
Sheets(wsname).Select
Sheets(wsname).AutoFilterMode = False
''first row and finalrow selection
finalrow2 = Sheets(wsname).Range("A1000000").End(xlUp).Row
firstrow2 = Range("a1: i100").find(what:="Cell Name", lookat:=xlWhole).Row
fistcolumn2 = Range("a1: i100").find(what:="Cell Name", lookat:=xlWhole).Column
''serchrange selection
add3 = Range("a1: i100").find(what:="Cell Name", lookat:=xlWhole).Address
add6 = Mid(add3, 2, 1) & "1"
add4 = Mid(add3, 2, 1) & finalrow2
For k = firstcolumn To finalcolumn
" macth the row criteria form my active sheet to the sheet i want to get the value form''
ol = 1
columnnumber = Application.Match(paraname1(k),Sheets(wsname).Range("2:2"), 0)
For l = firstrow To finalrow
'macth the column value form my first active sheet to the sheet form where i want to get the value from
Set firstcell = Range(add6, add4).find(what:=cellnm1(l), lookat:=xlWhole)
rownumber = Range(add6, add4).find(what:=cellnm1(l), lookat:=xlWhole).Row
firstcelladd = firstcell.Address
On Error GoTo msg
value(ol, k) = Cells(rownumber, columnnumber)
ol = ol + 1
Do
Set firstcell = Range(add6, add4).FindNext(firstcell)
rownumber = firstcell.Row
If firstcell.Address <> firstcelladd Then
value(ol, k) = Cells(rownumber, columnnumber)
ol = ol + 1
End If
Loop Until firstcell.Address = firstcelladd
Next
Next
ol = 1
'ActiveWorkbook.Close False
' select the previsus active workook aging where i wil paste the value
Workbooks(filename).Activate
Sheets(wsname).Select
Sheets(wsname).AutoFilterMode = False
For s = firstcolumn To finalcolumn
rl = firstrow
ol = 1
Do
Cells(rl, s) = value(ol, s)
rl = rl + 1
ol = ol + 1
Loop While value(ol, s) <> ""
Next
Erase cellnm1
Erase paraname1
Erase value
Exit Sub
msg: MsgBox (" Cell Name " & cellnm1(l) & " not found")
End Sub

I guess it is impossible. To access data with definite filters and so on, you'll need to open it, even through ADO. Anyway, you can speed-up the closing, as you don't need to save book, from which you are copying data. That's one part.
Other part, if you are copying it many times, you can organize extract/transform/load task just in to:
open source book
create target book file
filter source data
copy data to target book
save target book
Without closing source book repeat points 2-5 as much as needed. This is maximum you can get.
Other part is that XLSX itself is an ZIP archive, so it will take a plenty of time to decompress it anyway. You can also keep these files on SSD drive or attach virtual RAM disk maybe, this could save a bit more time also.

Related

Excel: Sorting Multple Columns separately

I have an excel sheet which looks like this - All the data is numerical data. The actual sheet has a lot more rows & columns in reality.
https://i.imgur.com/E2HEdXF.png
What I Want to get out of this data is something like this - For each year, I want to sort A & F based on the year's numerical data. So not one sort, but one sort per year.
I don't think there is a simple method for doing this, so I was thinking of 2 possible ways
I export the data into some database & then use SQL queries to get the output I want - I assume there must be some databases which allow you import Excel data.
or
Write a VBA program which does the following - Copy Column D & E into another place & sort based on Column E. Then Copy Column D & F into another place & sort based on Column F & so on & so forth.
I have never done VBA, but I am programmer, so I assume it wouldn't be trouble to do this.
However, I was wondering if there is some other easier way to do it or if not, which of the above two would be a better way to do it.
Copy and Sort
The following will copy the data from columns D:G as column pairs consisting of the first column and each next column, to columns A:B of newly created worksheets of the workbook containing this code and finally sort them descendingly by column B. Already existing worksheets, to be created, will previously be deleted.
Adjust the values in the constants section.
Option Explicit
Sub copyAndSort()
Const sName As String = "Sheet1"
Const sFirst As String = "D1"
Const yCols As String = "E:G"
Const dFirst As String = "A1"
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim sws As Worksheet: Set sws = wb.Worksheets(sName)
Dim srg As Range
Dim yrg As Range
Dim rCount As Long
Dim cCount As Long
With sws.Range(sFirst)
Dim rOff As Long: rOff = .Row - 1
Dim sCell As Range
Set sCell = .Resize(.Worksheet.Rows.Count - rOff) _
.Find("*", , xlFormulas, , , xlPrevious)
If sCell Is Nothing Then Exit Sub
rCount = sCell.Row - rOff
Set srg = .Resize(rCount)
Set yrg = .Worksheet.Columns(yCols).Rows(.Row).Resize(rCount)
cCount = yrg.Columns.Count
End With
Dim sData As Variant: sData = srg.Value
ReDim Preserve sData(1 To rCount, 1 To 2)
Dim yData As Variant: yData = yrg.Value
Dim Result As Variant: ReDim Result(1 To cCount)
Dim c As Long, r As Long
For c = 1 To cCount
Result(c) = sData
For r = 1 To rCount
Result(c)(r, 2) = yData(r, c)
Next r
Next c
Erase yData
Erase sData
Dim dws As Worksheet
Dim drg As Range
Dim dName As String
Application.ScreenUpdating = False
For c = 1 To cCount
dName = Result(c)(1, 2)
On Error Resume Next
Set dws = Nothing
Set dws = wb.Worksheets(dName)
On Error GoTo 0
If Not dws Is Nothing Then
Application.DisplayAlerts = False
dws.Delete
Application.DisplayAlerts = True
End If
Set dws = wb.Worksheets.Add(After:=wb.Sheets(wb.Sheets.Count))
dws.Name = dName
Set drg = dws.Range(dFirst).Resize(rCount, 2)
drg.Value = Result(c)
drg.Sort Key1:=drg.Cells(2), Order1:=xlDescending, Header:=xlYes
Next c
wb.Save
Application.ScreenUpdating = True
End Sub

Comma Delimitting values in a new created column based on a matching condition

I have a value in column A on the transactions sheet which contains an Identifier for a Deal.
To be able to find out the customer information for this Deal I look in another sheet called Deal Information. Here there is a value in Column F which matches a value in Column A on the transactions sheet. Although on the Deal Information sheet it lists all the customers who are part of this deal as well as a unique identifier for each of the customers.
On the transactions sheet I have created a new column where by I want to display the list of ID's associated to a particular deal in comma delimited format if possible if not then a space will be good too.
transactions data:
Column A:ID Column: AA: BID Multiple
1 ?
2 ?
3 ?
4 ?
Roots data:
Column C: ID Column: D: BID
1 100
1 200
1 300
2 101
Expected Result in transaction table based on example.
Column A ID Column AA: BID Multiple
1 100,200,300
2 101
3 ?
4 ?
Sub test()
Dim wb As Workbook
Set wb = ThisWorkbook
Dim ws As Worksheet
Dim lastRow As Long
Set ws = ThisWorkbook.Worksheets("Roots")
lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
Dim valuesArr()
valuesArr = ws.Range("F2:G" & lastRow) ' 1 TO 4, 1 TO 2
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
Dim valuesString As String
Dim currValue As Long
Dim currRotation As Long
Dim index As String
For currRotation = LBound(valuesArr, 1) To UBound(valuesArr, 1)
index = valuesArr(currRotation, 1)
currValue = CStr(valuesArr(currRotation, 2))
If Not dict.Exists(index) Then
dict.Add index, currValue
Else
dict(index) = dict(index) & ";" & currValue
End If
Next currRotation
Dim wsTarget As Worksheet
Dim lastRowTarget As Long
Set wsTarget = ThisWorkbook.Worksheets("transactions")
lastRow = wsTarget.Cells(ws.Rows.Count, "A").End(xlUp).Row
Dim valuesArr2()
valuesArr2 = wsTarget.Range("A2:AA" & lastRow)
Dim testValue As String
For currRotation = LBound(valuesArr2, 1) To UBound(valuesArr2, 1)
testValue = dict(CStr(valuesArr2(currRotation, 1)))
If testValue = vbNullString Then testValue = "?"
valuesArr2(currRotation, 27) = testValue
Next currRotation
wsTarget.Range("A2").Resize(UBound(valuesArr2, 1), UBound(valuesArr2,
27)) = valuesArr2
End Sub
This does an unordered, for the original posting . Assumes data starts in row 2 and has layout as shown below.
Column D being where the concatenated string is output.
*Please note repeated edits to the original question may mean code will no longer fit the stated requirements.
Option Explicit
Sub test()
Dim wb As Workbook
Set wb = ThisWorkbook
Dim ws As Worksheet
Dim lastRow As Long
Set ws = ThisWorkbook.Worksheets("Roots")
lastRow = ws.Cells(ws.Rows.Count, "A").End(xlUp).Row
Dim valuesArr()
valuesArr = ws.Range("A2:B" & lastRow) ' 1 TO 4, 1 TO 2
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
Dim currValue As Long
Dim currRotation As Long
Dim index As String
For currRotation = LBound(valuesArr, 1) To UBound(valuesArr, 1)
index = valuesArr(currRotation, 1)
currValue = CStr(valuesArr(currRotation, 2))
If Not dict.exists(index) Then
dict.Add index, currValue
Else
dict(index) = dict(index) & ";" & currValue
End If
Next currRotation
Dim wsTarget As Worksheet
Dim lastRowTarget As Long
Set wsTarget = ThisWorkbook.Worksheets("transactions")
lastRow = wsTarget.Cells(ws.Rows.Count, "A").End(xlUp).Row
Dim valuesArr2()
valuesArr2 = wsTarget.Range("A2:D" & lastRow)
Dim testValue As String
For currRotation = LBound(valuesArr2, 1) To UBound(valuesArr2, 1)
testValue = dict(CStr(valuesArr2(currRotation, 1)))
If testValue = vbNullString Then testValue = "?"
valuesArr2(currRotation, 4) = testValue
Next currRotation
wsTarget.Range("A2").Resize(UBound(valuesArr2, 1), UBound(valuesArr2, 2)) = valuesArr2
End Sub
Edited as per OP's amended input and output columns
as per your examples, IDs are consecutive in Roots sheet
so you may go as follows
Sub main()
Dim cell As Range
With Worksheets("transactions") 'reference "transaction" sheet
For Each cell In .Range("A2", .Cells(.Rows.Count, "A").End(xlUp)) 'loop through referenced sheet column A cells from row 2 down to last not empty one
cell.Offset(, 26).value = GetIDDeals(cell.value) 'write in current cell offset 26 columns (i.e. column AA) the value of the BID
Next
End With
End Sub
Function GetIDDeals(ID As Variant) As String
With Worksheets("Roots")
With .Range("C1", .Cells(.Rows.Count, "C").End(xlUp)) 'reference its column C cells from row 1 (header) down to last not empty one
.AutoFilter Field:=1, Criteria1:=ID ' filter referenced cells on 1st column with passed ID content
Select Case Application.WorksheetFunction.Subtotal(103, .Columns(1)) 'let's see how many filtered cells
Case Is > 2 'if more than 2, then we have more than 1 filtered value, since header gets always filtered
GetIDDeals = Join(Application.Transpose(.Offset(1, 1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible).value), ",")
Case 2 'if two filtered cells, then we have 1 filtered value, since header gets always filtered
GetIDDeals = .Offset(1, 1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible).value
End Select
End With
.AutoFilterMode = False
End With
End Function

Find the texts in Dynamic Range in another sheet

I am creating a VBA application that will find the text that I have entered in a certain range (Should be dynamic, in order for me to input more in the future). With that the entered texts in the range will look for the words in another sheet column:
Example:
And it will look for the words inputted in another sheet.
Dim Main as Worksheet
Set Main = Sheets("Sheet1")
Dim Raw2 as Worksheet
Set Raw2 = Sheets("Sheet2")
LookFor = Main.Range(D8:100)
Fruits = Raw2.Range("G" & Raw2.Rows.Count).End(xlUp).row
For e = lastRow To 2 Step -1
value = Raw2.Cells(e, 7).value
If Instr(value, LookFor) = 0 _
Then
Raw2.Rows(e).Delete
Honestly I am not sure how to proceed. And the mentioned code is just experiment. Desired output is to delete anything in sheet2 except for the rows that contain the words that I have inputted in the "Look for the words". Hope you can help me. Thank you.
This should do the trick :
Sub Sevpoint()
Dim Main As Worksheet
Set Main = Sheets("Sheet1")
Dim Raw2 As Worksheet
Set Raw2 = Sheets("Sheet2")
Dim LooKFoR() As Variant
Dim LastRow As Double
Dim i As Double
Dim j As Double
Dim ValRow As String
Dim DelRow As Boolean
LooKFoR = Main.Range(Main.Range("G8"), Main.Range("G" & Main.Rows.Count).End(xlUp)).Value
LastRow = Raw2.Range("G" & Raw2.Rows.Count).End(xlUp).Row
For i = LastRow To 2 Step -1
ValRow = Raw2.Cells(i, 7).Value
DelRow = True
'MsgBox UBound(LooKFoR, 1)
For j = LBound(LooKFoR, 1) To UBound(LooKFoR, 1)
If LCase(ValRow)<>LCase(LooKFoR(j, 1)) Then
Else
DelRow = False
Exit For
End If
Next j
If DelRow Then Raw2.Rows(i).Delete
Next i
End Sub

Transporting text from one worksheet to another with two Dynamic ranges- VBA Excel

I have two worksheets (one for input and another for output) and I'm trying to copy some information on cells along a row from the input to cells along a row on the output. My code stops if, on a separated list of ranges, there's no info. The code I'm using is like that right now:
Sub Txt_Manobra()
Dim x As Range
Dim i As Integer
Dim o As Integer
Dim p As Integer
Dim Txt1 As String
Dim Txt2 As Variant
For Each x In Worksheets("Formulario").Range("AK9:AK47").Cells
If x.Value <> "" Then Txt1 = x.Value
o = 8 + x.Count
For i = 1 To 7
p = 3 + i
Txt2 = Worksheets("Formulario").Range(o, p).Value
Worksheets("SdB pg1").Range(Txt1).Cells(1, i) = Txt2
Next i
Next x
End Sub
For each range value (x), it looks on the range txt1 (That is composed of only one row), and it should copy the values on range (D9:J9), one at a time and put on the range determined x (also only one row and with the same number of columns as D9:J9) then look on the next row (D10:J10) and so on.
I'm getting errors and I think this might be easier to do than I Think.
It's like that:
Input
And I want to tranfer to
Output
Each cell in the range "AK9:AK47" Contains a range like "P10:V10"
I haven't made it to work out to see if my "o" changes
If there are valid cell addresses in range AK9:AK47, then something like this should work for you. Note that using single letter variables is bad practice, and you can customize the code by adjusting the constants at the top.
Sub tgr()
Const sDestInfoCol As String = "AK"
Const lHeaderRow As Long = 8
Const lStartCopyCol As String = "D"
Const lFinalCopyCol As String = "J"
Dim wb As Workbook
Dim wsData As Worksheet
Dim wsDest As Worksheet
Dim rDestInfo As Range
Dim rDestTemp As Range
Dim lNumCols As Long
Set wb = ActiveWorkbook
Set wsData = wb.Sheets("Formulario")
Set wsDest = wb.Sheets("SdB pg1")
Set rDestInfo = wsData.Range(sDestInfoCol & lHeaderRow + 1, wsData.Cells(wsData.Rows.Count, sDestInfoCol).End(xlUp))
If rDestInfo.Row <= lHeaderRow Then Exit Sub 'No data
lNumCols = Columns(lFinalCopyCol).Column - Columns(lStartCopyCol).Column + 1
For Each rDestTemp In rDestInfo.Cells
With rDestTemp
If Evaluate("ISREF(" & .Text & ")") Then
wsDest.Range(.Text).Resize(1, lNumCols).Value = wsData.Cells(.Row, lStartCopyCol).Resize(1, lNumCols).Value
End If
End With
Next rDestTemp
End Sub

Excel VBA lastrow & currentrow, add data to row if data is found or add new row

I have an Excel Form that I use, It holds 3 Worksheets - Form, Data and Entries. Basically When you call a new Form with the Button it looks for the lastrow in the Entries sheet (Range "A") adds 1 and that's your new Form ID. Having multiple users use this form I have it set that It also adds the number in the Entries sheet before the form is filled, in order to reserve it so a next user cannot get duplicates.
Here's where I get caught up. I'd like to have currentrow find that sheet id and paste the data in that row, but the code I have is just adding a new row.
This is what it looks like.
Sub Submit()
Dim rng1 As Range
Dim rng2 As Range
Dim multiplerange As Range
Dim currentrow As Long
Dim sheetid As String
Set rng1 = Range("M1,M2,B2,F6,B7,B8,B9,B11,D11,B13,D13,I6,M6,I7,I8,I9,I11,K11,I13,K13,B15,B18,B22,E22,B23,B24,B26,E26,I22,J22,M22,K24,M24,I24,I26,K26,M26,B29,E29,B30,B31,B33,E33,I29,J29,M29,K31,M31,I31,I33,K33,M33,B36,E36,B37,B38,B40,E40,I36,J36,M36,K38,M38,I38,I40,K40,M40")
Set rng2 = Range("G2")
Set multiplerange = Union(rng1, rng2)
Dim WSEntries As Worksheet
Dim WSForm As Worksheet
Set WSEntries = Sheets("Entries")
Set WSForm = Sheets("Form")
sheetid = WSForm.Range("M1").Text
Dim lastrow As Integer
lastrow = WSEntries.Cells(Rows.Count, "A").End(xlUp).Row
For currentrow = 3 To lastrow
If Cells(currentrow, 1) = sheetid Then
Dim i As Integer
i = 1
For Each c In multiplerange
WSEntries.Cells(currentrow + 1, i) = c
i = i + 1
Next
Else
i = 1
For Each c In multiplerange
WSEntries.Cells(lastrow + 1, i) = c
i = i + 1
Next
End If
Next
End Sub
Any help would be great. Thanks
Edit: I also didn't add all my declared items but this is the other part of the script. I've got everything else working just the paste part that I'm having issue with
Got it working!! switched to a Range method
lastrow = WSEntries.Cells(Rows.Count, "A").End(xlUp).Row
For currentrow = 3 To lastrow
If WSEntries.Range("A" & currentrow) = sheetid Then
Dim i As Integer
i = 1
For Each c In multiplerange
WSEntries.Cells(currentrow, i) = c
i = i + 1