Excel: Sorting Multple Columns separately - sql

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

Related

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

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.

Pasting multiple ranges to another sheet in vba

I'd like the code to paste 'cashb' underneath 'rngcel', but every time
I run the code 'cashb''s value appears above 'rngCel'.value. Rngcell's range is from A2:A34, I'd like 'Cashb' to appear right below it at A35. I tried putting 'A35' in the
range but it does not work.
This is the code that I want to appear below rngcel.value.
Sheets(" Price").Range("A35").Resize(Cashb.Rows.Count).Value = Cashb.Value
I'd also like to return the column that's 5 columns to the right of "cashb"range
I appreciate any help that I receive.
This is the code that I have.Thanks in advance.
Sub liveP()
Application.ScreenUpdating = False
Dim rngTicker As Range
Dim rngCel As Range
Dim Cashb As Range
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets.Add(After:= _
ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count))
ws.Name = "Live"
Set rngTicker = Worksheets("prices").Range("H16:H200")
Set Cashb = Sheets("prices").Range("cashbalances")
For Each rngCel In rngTicker
If rngCel.Font.ColorIndex = 33 Then
Worksheets("Live").Cells(Rows.Count, 1).End(xlUp).offset(1).Resize(1, 2).Value = Array(rngCel.offset(, "-7").Value, rngCel.Value) ' this is range cell value'
WorkSheets("Live").Range("A35").Resize(Cashb.Rows.Count).Value = Cashb.Value.offset ' this is the value I'd like to appear under rngcel value
'New data that im posting on the Live sheet'
Sheets("Live").Range("C2:H33").Formula = "=($B2 +$C5)"
Sheets("Live").Range("A1") = "Header1"
Sheets("Live").Range("B1") = "Header2"
Sheets("Live").Range("C1") = "Header3"
Sheets("Live").Range("D1") = "Header4"
Sheets("Live").Range("E1") = "Header5"
Sheets("Live").Range("F1") = "Header6"
End If
Next
Application.ScreenUpdating = True
End Sub
Try This
Sub liveP()
Application.ScreenUpdating = False
Dim rngTicker As Range
Dim rngCel As Variant 'used in for each this should be variant
Dim Cashb As Range
Dim ws As Worksheet
Dim LastRow As Long 'dimensioned variable for the last row
Dim CashbRows As Long 'dimensioned variable for Cashb rows
Set ws = ThisWorkbook.Sheets.Add(After:= _
ThisWorkbook.Sheets(ThisWorkbook.Sheets.Count))
ws.Name = "Live"
Set rngTicker = Worksheets("prices").Range("H16:H200")
Set Cashb = Sheets("prices").Range("cashbalances")
'Assuming "cashbalances" is a named range in the worksheet
CashbRows = Cashb.Rows.Count
For Each rngCel In rngTicker
If rngCel.Font.ColorIndex = 33 Then
With Worksheets("Live")
LastRow = .Cells(Rows.Count, 1).End(xlUp).Row + 1 'set lastrow variable
.Cells(LastRow, 1) = rngCel.Offset(0, -7).Value 'putting value 7 columns before into live worksheet column A
.Cells(LastRow, 2) = rngCel.Value 'putting value into live worksheet column B
.Range(.Cells(35, 1), .Cells(35 + CashbRows, 1)) = Cashb.Offset(, 5).Value 'im not really sure if this line is going to work at all
'New data that im posting on the Live sheet'
.Range("C2:H33").Formula = "=($B2 +$C5)"
.Range("A1") = "Header1"
.Range("B1") = "Header2"
.Range("C1") = "Header3"
.Range("D1") = "Header4"
.Range("E1") = "Header5"
.Range("F1") = "Header6"
End With
End If
Next
Application.ScreenUpdating = True
End Sub
Modifications:
rngCel is now a variant not a range
Using a LastRow Variable to get away from offset
Removed the array when placing data into "LIVE" because why not
CashbRows will now only be calculated one time before the loop. Saving time
The With Worksheets("Live") statement is a time saving measure.
You were calling A35 as a range, which it is not, then resizing to a range maybe? Hard to know when I cant tell what "cashbalances" is. If "cashbalances is only 1 row or may ever be 1 row, then you will need to add an If Then Else control to handle it.
Also A35 gets overwritten every single loop... so not sure what you want to do there.
I hope I was able to understand your questions well enough to get you going in the right direction.
EDIT
Stop treating "cashbalances" as a named range. I believe VBA is hanging onto the original row numbers of the range, similar to how Variant arrays start at 1 when assigned as I do in the following. It does not look like you are modifying "cashbalances" so create a variant array before the loop but after CashbRows.
EXAMPLE:
Dim CB() as variant, j as long
with sheets("PUT THE SHEET NAME OR INDEX HERE")
CB = .range(.cells(1,6), .cells(CashbRows,6)).value 'address to whatever .offset(,5) is
'i assumed cashb was in column A
Instead of .Range(.Cells(35, 1), .Cells(35 + CashbRows, 1)) = Cashb.Offset(, 5).Value Use:
For j = 1 to CashbRows
.cells(34 + j, 1) = CB(j)
Next j

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

If Cells in column A equal cells in column A on other workbook copy that row

I've been stuck on this for ages, it seems relatively simple in my head but I cant get it to work.. So what I need is say if I have a cell in workbook1 equals Bob, if that cell is in the same column in another workbook, copy that row..
So example.. if Bob in column A workbook1 is found in Column A workbook2 copy whatever is in the column b,c,d,e on bobs row into workbook2..
I could get it to work for singular ones easily but its for 500+ entries.
I've tried using arrays here is what I have got so far (the code is currently in a button on workbook1)
Dim owb As Workbook
Dim test1(500) As String, test2(500) As String, test3(500) As String, test4(500) As String
With Application
.DisplayAlerts = False
.ScreenUpdating = False
.EnableEvents = False
End With
fpath = "\Work\new location\test subject.xlsx" 'file location
Set owb = Application.Workbooks.Open(fpath) 'open file
For i = 1 To 500 'for each I
test1(i) = ThisWorkbook.Worksheets("Allsites").Cells(i, 1).Value
test2(i) = ThisWorkbook.Worksheets("Allsites").Cells(i, 8).Value
test3(i) = owb.Worksheets("Sheet2").Cells(i, 1).Value
test4(i) = owb.Worksheets("Sheet2").Cells(i, 2).Value 'declare locations
If test3(i) = test1(i) Then
test2(i) = test4(i)
End If
Next
In the example above, you're checking for a match in the exact cell (eg the value in A5 is the same as the one in A5), so I've assumed the same in the code below.
Dim sourceSheet As Worksheet
Dim destinationSheet As Worksheet
Dim columnNumber As Integer
Set sourceSheet = Worksheets("Sheet3")
Set destinationSheet = Worksheets("Sheet2")
Dim sourceArr() As Variant
Dim destArr() As Variant
sourceArr = sourceSheet.Range("A1:E500")
destArr = destinationSheet.Range("A1:E500")
For i = 1 To 500 'for each I
If destArr(i, 1) = sourceArr(i, 1) Then
For columnNumber = 2 To 5
destArr(i, columnNumber) = sourceArr(i, columnNumber)
Next
End If
Next
destinationSheet.Range("A1:A500").Value = destArr
There is a nice article about transferring data between arrays and worksheet ranges at http://www.cpearson.com/excel/ArraysAndRanges.aspx. Working with an array and writing the whole array in one go will be quicker than writing the value of each cell individually.
Update:
If the data can be in any row on the source spreadsheet, you can use Find to search for it. This may be quite a bit slower:
For i = 1 To 500 'for each I
Dim found As Range
Set found = searchRange.Find(destArr(i, 1), LookIn:=xlValues, lookat:=xlWhole)
If Not found Is Nothing Then
For columnNumber = 2 To 5
destArr(i, columnNumber) = found.Offset(0, columnNumber - 1)
Next
End If
Next
You may want to consider using a VLookup function in the worksheet rather than using VBA.

Selecting a field in macro and cutting it out in a loop

I need to select a field of cells (table) in an Excel worksheet, cut the selection out and then paste it into a new separate sheet. There are like thousand tables below one another in this worksheet and I want to automaticly cut them out and paste them into separate sheets. The tables are separated by cells with the # symbol inside but I dont know if it is helpful in any way. When I recorded this macro for the first table it run like this:
Sub Makro1()
Range("A2:AB20").Select
Selection.Cut
Sheets.Add After:=Sheets(Sheets.Count)
ActiveSheet.Paste
End Sub
Now I want to make a loop which would go through the whole worksheet, dynamically select every table which would be delimited by the # sign in a col A and paste it into new sheet. I dont want to choose exact range A2:AB20, but I want to make selection according to this # sign.
Here's a screenshot
This will populate an array with the indicies of all your hash values. This should provide you with the reference point that you need to collect the appropriate data.
Sub FindHashmarksInColumnA()
Dim c As Range
Dim indices() As Long
Dim i As Long
Dim iMax As Double
Dim ws As Worksheet
Set ws = ActiveSheet
i = 0
iMax = Application.WorksheetFunction.CountIf(ws.Range("A:A"), "#")
ReDim indices(1 To iMax)
For Each c In ws.UsedRange.Columns(1).Cells
If c.Value = "#" Then
i = i + 1
indices(i) = c.Row
End If
Next c
' For each index,
' Count rows in table,
' Copy data offset from reference of hashmark,
' Paste onto new sheet in appropriate location etc.
End Sub
Try this code. You might need to adjust the top 4 constants to your need:
Sub CopyToSheets()
Const cStrSourceSheet As String = "tabulky"
Const cStrStartAddress As String = "A2"
Const cStrSheetNamePrefix As String = "Result"
Const cStrDivider As String = "#"
Dim rngSource As Range
Dim lngMaxRow As Long, lngLastDividerRow As Long, lngRowCount As Long
Dim wsTarget As Worksheet
Dim lngCounter As Long
Application.ScreenUpdating = False
Application.Calculation = xlCalculationManual
'Delete old worksheets
Application.DisplayAlerts = False
For Each wsTarget In Sheets
If InStr(wsTarget.Name, cStrSheetNamePrefix) Then wsTarget.Delete
Next
Application.DisplayAlerts = True
With Sheets(cStrSourceSheet)
Set rngSource = .Range(cStrStartAddress)
lngLastDividerRow = rngSource.Row
lngMaxRow = .Cells(Rows.Count, 1).End(xlUp).Row
End With
Set rngSource = rngSource.Offset(1)
While rngSource.Row < lngMaxRow
If rngSource = cStrDivider Then
lngCounter = lngCounter + 1
Set wsTarget = Sheets.Add(After:=Sheets(Sheets.Count))
wsTarget.Name = cStrSheetNamePrefix & " " & lngCounter
lngRowCount = rngSource.Row - lngLastDividerRow - 1
rngSource.Offset(-lngRowCount - 1).Resize(lngRowCount).EntireRow.Copy _
wsTarget.Range("A1").Resize(lngRowCount).EntireRow
lngLastDividerRow = rngSource.Row
End If
Set rngSource = rngSource.Offset(1)
Wend
Application.ScreenUpdating = True
Application.Calculation = xlCalculationAutomatic
End Sub