excel vba macro - extract value from where clause - excel

In columnA of excel sheet 'Input' I have the following (with each line being on a new row in the sheet):
update my_table
set time = sysdate,
randfield1 = 'FAKE',
randfield5 = 'ME',
the_field8 = 'test'
where my_key = '84'
;
update my_table
set time4 = sysdate,
randfield7 = 'FAeKE',
randfield3 = 'MyE',
the_field9 = 'test'
where my_key = '37';
I'm trying to create a new sheet 'output' that only contains the following values in columnA but I don't know how to extract the bit in between the quotes after --> where my_key:
84
37
Some notes: it would be great to be able to specify the fieldname in cell B1 of sheet 'input', in this example it would be my_key.
Previously, I've been doing this manually using filter column where text contains 'where' then stripping out everything after the equals then doing a find/replace on single quotes and ;s. Has anyone been able to achieve this with a single button click macro?

While using Filtering or Find is very efficient I don't think you will see much difference in using a variant array to hold the all values for your Input Sheet, to be tested against a regex using a fieldname in InputB1, with any numeric portions of the match being dumped to Column A Output.
Sub VarExample()
Dim ws1 As Worksheet
Dim ws2 As Worksheet
Dim X
Dim Y
Dim lngRow As Long
Dim objRegex
Dim objRegexMC
Set ws1 = ActiveWorkbook.Sheets("Input")
Set ws2 = ActiveWorkbook.Sheets("Output")
Set objRegex = CreateObject("vbscript.regexp")
objRegex.Pattern = ".+where.+" & ws1.[b1] & ".+\'(\d+)\'.*"
X = ws1.Range(ws1.[a1], ws1.Cells(Rows.Count, "A").End(xlUp)).Value2
ReDim Y(1 To UBound(X, 1), 1 To UBound(X, 2))
For lngRow = 1 To UBound(X, 1)
If objRegex.test(X(lngRow, 1)) Then
Set objRegexMC = objRegex.Execute(X(lngRow, 1))
lngCnt = lngCnt + 1
Y(lngCnt, 1) = objRegexMC(0).submatches(0)
End If
Next
ws2.Columns("A").ClearContents
ws2.[a1].Resize(UBound(Y, 1), 1).Value2 = Y
End Sub

A simple solution but definitely not a good one could be like this:
Sub getWhere()
Dim sRow as Integer
Dim oRow as Integer
Dim curSheet as Worksheet
Dim oSheet as Worksheet
dim words() as String
Set curSheet = ThisWorkbook.Sheets("Input")
Set oSheet = ThisWorkbook.Sheets("Output")
sRow = 1
oRow = 1
Do while curSheet.Range("A" & sRow).Value <> ""
If Instr(lcase(curSheet.Range("A" & sRow).Value), "where") > 0 Then
words = Split(curSheet.Range("A" & sRow).Value, " ")
oSheet.Range("B" & oRow).Value = words(1)
oSheet.Range("C" & oRow).Value = getNumeric(words(3))
oRow = oRow + 1
End If
sRow = sRow +1
Loop
End Sub
Function getNumeric(ByVal num As String) As Long
Dim i As Integer
Dim res As String
For i = 1 To Len(num)
If Asc(Mid(num, i, 1)) >= 48 And Asc(Mid(num, i, 1)) <= 57 Then res = res & Mid(num, i, 1)
Next
getNumeric = CLng(res)
End Function

Related

In Excel Sheet how to Eliminate or Remove, Filter and copy the selected records defined in another sheet using dynamic array list (VBA Module)

I need the experts help as I am new in this area. I am trying to create the Dynamic array Macro for Excel sheet (VBA). In which I want to eliminate (delete or hide) the number of records on the bases of data selected in one particular column (“AlertCount”) in main Sheet “StatusReport” using dynamic array list.
Example : StatusReport (Worksheet)
Filter_Criteria (Worksheet)
Expected output :
All record should display without "1055" and "1056" related Alert Count (Eliminate Record)
But its removed all the records now instead of selected value
My Module as below it display the filter records only but I need to eliminate the selected filter records . VBA Module as below :
Sub DeleteFilter_Data()
Set Data_sh = ThisWorkbook.Sheets("StatusReport")
Set Filter_Criteria = ThisWorkbook.Sheets("Filter_Criteria")
Data_sh.AutoFilterMode = False
Dim AlertCount_List() As String
Dim n As Integer
n = Application.WorksheetFunction.CountA(Filter_Criteria.Range("A:A")) - 1
ReDim AlertCount_List(n) As String
Dim i As Integer
For i = 0 To n
AlertCount_List(i) = Filter_Criteria.Range("A" & i + 2)
Next i
Dim Arr01 As Variant
Dim i01 As Integer
Dim i02 As Integer
'Creates a list of everything in Column I, minus everything in Filter_Criteria list
Arr01 = Range("I2", Range("I2").End(xlDown))
For i01 = 1 To UBound(Arr01, 2)
For i02 = 0 To n - 1
If Arr01(i01, 1) = AlertCount_List(i02) Then
Arr01(i01, 1) = ""
End If
Next i02
Next i01
'Turns list into strings (needed for the Filter command).
Dim ListEdited() As String
ReDim ListEdited(1 To UBound(Arr01, 1)) As String
For i01 = 1 To UBound(Arr01, 2)
ListEdited(i01) = Arr01(i01, 1)
Next i01
'Filter command that keeps all entries except any found within the Filter_Criteria Sheet.
Data_sh.UsedRange.AutoFilter 9, ListEdited(), xlFilterValues
End Sub
Please help me out with corrected Macro using dynamic array list.
Thanks
Susheel
I think you are asking to keep all Alert_Counts except for the ones on the Filter_Criteria sheet? The code below does this. Please let me know if I have misunderstood your questions and I will try again.
EDIT 20210630: I have updated the below code.
Sub HideFilter_Data()
Set Data_sh = ThisWorkbook.Sheets("StatusReport")
Set Filter_Criteria = ThisWorkbook.Sheets("Filter_Criteria")
Data_sh.AutoFilterMode = False
Dim AlertCount_List() As String
Dim n As Integer
n = Application.WorksheetFunction.CountA(Filter_Criteria.Range("I:I")) - 1
ReDim AlertCount_List(n) As String
Dim i As Integer
For i = 0 To n
AlertCount_List(i) = Filter_Criteria.Range("I" & i + 2)
Next i
Dim Arr01 As Variant
Dim i01 As Integer
Dim i02 As Integer
'Creates a list of everything in Column I, minus everything in Filter_Criteria list
Arr01 = Range("I2", Range("I2").End(xlDown))
For i01 = 1 To UBound(Arr01, 1)
For i02 = 0 To n - 1
If Arr01(i01, 1) = AlertCount_List(i02) Then
Arr01(i01, 1) = ""
End If
Next i02
Next i01
'Turns list into strings (needed for the Filter command).
Dim ListEdited() As String
ReDim ListEdited(1 To UBound(Arr01, 1)) As String
For i01 = 1 To UBound(Arr01, 1)
ListEdited(i01) = Arr01(i01, 1)
Next i01
'Filter command that keeps all entries except any found within the Filter_Criteria Sheet.
Data_sh.UsedRange.AutoFilter 9, ListEdited(), xlFilterValues
'Data_sh.UsedRange.AutoFilter 9, AlertCount_List(), xlFilterValues
'Data_sh.UsedRange.AutoFilter 9, Criteria1:="<> 1056" ‘ This work fine but it's a hard coded value
End Sub
I have got the solution of how to Filter, Eleminate or Hide and Copy the Selected records to another worksheet. The list of Filter data defined in another worksheet and execute the Module by Button press events on the worksheet.
For Eliminate Data case we need to create the 2 list from main worksheet and another one for eliminate the records worksheet. And Compare the both the list and replace the matched case with null or blank in main sheet
Sub HideFilter_Data()
Set Data_sh = ThisWorkbook.Sheets("StatusReport")
Set Filter_Criteria = ThisWorkbook.Sheets("Filter_Criteria")
Data_sh.AutoFilterMode = False
Dim AlertCount_List() As String
Dim n As Integer
n = Application.WorksheetFunction.CountA(Filter_Criteria.Range("A:A")) - 1
ReDim AlertCount_List(n) As String
Dim i As Integer
For i = 0 To n
AlertCount_List(i) = Filter_Criteria.Range("A" & i + 2)
Next i
' Create the List of main worksheet
Dim Arr01 As Variant
Dim i01 As Integer
Dim i02 As Integer
Dim r As Integer
Dim r1 As Integer
r = Application.WorksheetFunction.CountA(Data_sh.Range("I:I")) - 2
ReDim StatusCount_List(r + 1) As String
For r1 = 0 To r
StatusCount_List(r1) = Data_sh.Range("I" & r1 + 2)
Next r1
'Creates a list of everything in Column I, minus everything in Filter_Criteria list
Dim str As Variant
Dim cnt As Integer
cnt = 0
' Executing the double loop for comparing both the List and eleminate the match data from the main sheet.
For Each Item In StatusCount_List
For Each subItem In AlertCount_List
If Item = subItem Then
StatusCount_List(cnt) = ""
End If
Next subItem
cnt = cnt + 1
Next Item
Data_sh.UsedRange.AutoFilter 9, StatusCount_List(), xlFilterValues
End Sub
Main Worksheet :
Eliminating Criteria (Hide the records)
Output (Eliminated / Hide/ Remove) as below:
Filter the selected records. Filter list defined in another worksheet.
If we need to select the selected record from the list of another sheet by using dynamic array.
Option Explicit ' Force explicit variable declaration.
Sub Filter_Data()
Dim Data_sh As Worksheet
Dim Filter_Criteria As Worksheet
Dim Output_Sh As Worksheet
Set Data_sh = ThisWorkbook.Sheets("StatusReport")
Set Filter_Criteria = ThisWorkbook.Sheets("Filter_Criteria")
Set Output_Sh = ThisWorkbook.Sheets("Output")
Output_Sh.UsedRange.Clear
Data_sh.AutoFilterMode = False
Dim AlertCount_List() As String
Dim n As Integer
n = Application.WorksheetFunction.CountA(Filter_Criteria.Range("A:A")) - 1
ReDim AlertCount_List(n) As String
Dim i As Integer
For i = 0 To n
AlertCount_List(i) = Filter_Criteria.Range("A" & i + 2)
Next i
Data_sh.UsedRange.AutoFilter 9, AlertCount_List(), xlFilterValues
End Sub
Output :
Copy the selected records to new worksheet. Filter list defined in another worksheet.
Option Explicit ' Force explicit variable declaration.
Sub CopyFilter_Data()
Dim Data_sh As Worksheet
Dim Filter_Criteria As Worksheet
Dim Output_Sh As Worksheet
Set Data_sh = ThisWorkbook.Sheets("StatusReport")
Set Filter_Criteria = ThisWorkbook.Sheets("Filter_Criteria")
Set Output_Sh = ThisWorkbook.Sheets("Output")
Output_Sh.UsedRange.Clear
Data_sh.AutoFilterMode = False
Dim AlertCount_List() As String
Dim n As Integer
n = Application.WorksheetFunction.CountA(Filter_Criteria.Range("A:A")) - 1
ReDim AlertCount_List(n) As String
Dim i As Integer
For i = 0 To n
AlertCount_List(i) = Filter_Criteria.Range("A" & i + 2)
Next i
'Data_sh.UsedRange.AutoFilter 9, Array("1055", "1056"), xlFilterValues
Data_sh.UsedRange.AutoFilter 9, AlertCount_List(), xlFilterValues
Data_sh.UsedRange.Copy Output_Sh.Range("A1")
Data_sh.AutoFilterMode = False
'MsgBox ("Data has been copied")
End Sub
Output :
Please, try the next code. As I said (twice) in my comments it is not possible to filter more than two "not equal to" type conditions. So, it solves the problem as you presented in your question (two conditions):
Sub filterCriteriaArray()
Dim Data_sh As Worksheet, Filter_Criteria As Worksheet, lastR As Long, arrC()
Set Data_sh = ThisWorkbook.Sheets("StatusReport")
Set Filter_Criteria = ThisWorkbook.Sheets("Filter_Criteria")
lastR = Filter_Criteria.Range("A" & Filter_Criteria.rows.count).End(xlUp).row
arrC = Filter_Criteria.Range("A2:A" & lastR).value
Data_sh.UsedRange.AutoFilter field:=9, Criteria1:="<>" & arrC(1, 1), Operator:=xlAnd, Criteria2:="<>" & arrC(2, 1)
End Sub
Edited:
The next code version uses AdvancedFilter, which allows using more criteria of the type you need, but it does not uses array as criteria. I used a trick, creating a range in a newly add sheet (hidden), based on the array extracted from your criteria sheet:
Sub filterCriteriaFromArray()
Dim Data_sh As Worksheet, Filter_Criteria As Worksheet, crit As Worksheet, lastR As Long, arrCr()
Dim strHeader As String, filtRng As Range, rngCrit As Range, i As Long
strHeader = "Head8" ' "AlertCount" 'important the be the correct header (of I:I column)
Set Data_sh = ActiveSheet 'ThisWorkbook.Sheets("StatusReport")
Set filtRng = Data_sh.Range(Data_sh.Range("A1"), _
Data_sh.cells(Data_sh.UsedRange.rows.count, Data_sh.cells(1, Data_sh.Columns.count).End(xlToLeft).Column))
Set Filter_Criteria = Data_sh.Next 'ThisWorkbook.Sheets("Filter_Criteria")
lastR = Filter_Criteria.Range("A" & Filter_Criteria.rows.count).End(xlUp).row 'last row in Filter_Criteria
arrCr = Filter_Criteria.Range("A2:A" & lastR).value 'put criteria values in the array
On Error Resume Next
Set crit = Sheets("CriteriaSh") 'check if sheets "CriteriaSh" exists
If err.Number <> 0 Then
err.Clear 'if it does not exist, it is created
Set crit = Data_sh.Parent.Sheets.Add(After:=Worksheets(Sheets.count))
crit.Name = "CriteriaSh"
crit.Visible = xlSheetVeryHidden
Else
crit.cells.ClearContents 'if it exists its cells are cleared
End If
On Error GoTo 0
For i = 1 To UBound(arrCr) 'Build the range to be used in AdvancedFilter criteria
crit.cells(1, i).value = strHeader
crit.cells(2, i).value = "<>" & arrCr(i, 1)
Next i
'set the criteria range:
Set rngCrit = crit.Range(crit.Range("A1"), crit.cells(2, UBound(arrCr)))
filtRng.AdvancedFilter Action:=xlFilterInPlace, CriteriaRange:=rngCrit, Unique:=False
End Sub

Excel VBA Code pastes result into wrong range

A script that copies a range into another range. However, when I try to copy the range from Sheet1 to Sheet2 the result won't be pasted into column J, it get pasted with an offset of 8 columns (column R). I cant understand why? Both RowCountSummary and ColumnCountSummary are set to 0, i.e. first index of the range?
Sub InsertForecastData()
Dim ColumnsCount As Integer
Dim ColCounter As Integer
Dim RowsCount As Integer
Dim ForeCastRange As Range
Dim ForecastWS As Worksheet
Dim SummaryWs As Worksheet
Dim PasteRange As Range
Dim ColumnCountSummary As Integer
Dim RowCountSummary As Integer
ColumnsCount = 300
ColCounter = 0
RowsCount1 = 0
RowsCount2 = 47
ColumnCountSummary = 0
RowCountSummary = 0
Do While ColCounter <= ColumnsCount
Worksheets("Sheet1").Select
Set ForeCastRange = Worksheets("Sheet1").Range("B2:KN49")
With ForeCastRange
.Range(.Cells(RowsCount1, ColCounter), .Cells(RowsCount2, ColCounter)).Copy
End With
Worksheets("Sheet2").Select
Set PasteRange = Worksheets("Sheet2").Range("J2:J13915")
With PasteRange
.Range(.Cells(RowCountSummary, ColumnCountSummary), .Cells(RowCountSummary + RowsCount2, ColumnCountSummary)).PasteSpecial
End With
RowCountSummary = RowCountSummary + 48
ColCounter = ColCounter + 1
Loop
End Sub
This behaviour has been encountered before and can seen with this simple demo
Sub test()
With Sheet1.Range("J3:J100")
Debug.Print .Range(.Cells(0, 0), .Cells(47, 0)).Address
End With
End Sub
which results in $R$4:$R$51. If you repeat run for the columns B to J the results are B,D,F,H,J,L,N,P showing the doubling effect. B is OK I think because of the zero column number.
You can probably fix your code by setting RowCountSummary = 1 and ColumnCountSummary = 1 and adding .parent
With PasteRange
.Parent.Range(.Cells(RowCountSummary, ColumnCountSummary), _
.Cells(RowCountSummary + RowsCount2, ColumnCountSummary)).PasteSpecial
End With
or you could try this
Sub InsertForecastData1()
Const columnCount As Integer = 3
Const rowCount As Integer = 48
Const sourceCol As String = "B"
Const targetCol As String = "J"
Const startRow As Integer = 2
Const records As Integer = 300
Dim rngSource as Range, rngTarget As Range
Dim start as Single, finish as Single
Set rngSource = Worksheets("Sheet1").Range(sourceCol & startRow)
Set rngSource = rngSource.Resize(rowCount, columnCount)
Set rngTarget = Worksheets("Sheet2").Range(targetCol & startRow)
start = Timer
Application.ScreenUpdating = False
Dim i As Integer
For i = 1 To records
'Debug.Print rngSource.Address, rngTarget.Address
rngSource.Copy rngTarget
Set rngSource = rngSource.Offset(rowCount, 0)
Set rngTarget = rngTarget.Offset(rowCount, 0)
Next i
Application.ScreenUpdating = True
finish = Timer
MsgBox "Completed " & records & " records in " & finish - start & " secs"
End Sub
See Remarks section the docs

How do I turn a validation list from a cell into a list with VBA

I have a sheet that I need to paste data to according to the validation lists in those sheets. In the sheet, there are many columns each with their own data validation list - some are written directly as "yes;no" others are references "='$$VALUES$$'!$IJ$1:$IJ$12".
What I need is to find a way to add each item in each list to an array. Using the code below I could find the references above.
Debug.Print Cells(2, 6).Validation.Formula1
Is there any elegant way to store the output as a list containing each valid input. My only idea so far is to first check which type of output I get, and then if it is the list form of "yes;no" then look for the number of ; and then split it item by item. And in case its the sheet range reference split it by sheet and range and convert that range to an array.
Something like this, will do it. I'd set a range rather than using activecell, and also check validation is present to reduce your errors.
Sub get_val_lists()
Dim arrOutput() As Variant
If Left(ActiveCell.Validation.Formula1, 1) <> "=" Then
arrOutput = Split(ActiveCell.Validation.Formula1, ",")
Else
arrOutput = Application.Transpose( _
Range(Mid(ActiveCell.Validation.Formula1, 2)).value)
End If
End Sub
I was a bit pressed for time so I ended up doing an inelegant solution myself. Posting it here in case somebody else runs into the same problem.
Sub ValidList()
Dim strFormula As String
Dim intLastSemi As Integer
Dim intCurSemi As Integer
Dim intSemi As Integer
Dim aryList() As Variant
Dim intLen As Integer
Dim blnCont As Boolean
Dim strSheet As String
Dim strRange As String
Dim intSplit As Integer
Dim ws As Worksheet
Dim rng As Range
Dim e As Variant
Dim Row As Integer
Dim Col As Integer
'This is just an example, turning it into a fucntion based on row and col later
'so now my test validation list is just in A1
Row = 1
Col = 1
strFormula = Cells(Row, Col).Validation.Formula1
intLen = Len(strFormula)
If InStr(1, strFormula, "=") Then 'Sheet reference
intSplit = InStr(1, strFormula, "!")
strSheet = Right(Left(strFormula, intSplit - 1), intLen - intSplit - 3)
strRange = Right(strFormula, intLen - intSplit)
Set ws = Worksheets(strSheet)
Set rng = ws.Range(strRange)
aryList() = rng
ElseIf Not InStr(1, strFormula, ";") Then 'Hardcoded list
intSemi = 0
intLastSemi = 0
blnCont = True
While blnCont
intCurSemi = InStr(intLastSemi + 1, strFormula, ";")
If intCurSemi <> 0 Then
intSemi = intSemi + 1
ReDim Preserve aryList(intSemi)
aryList(intSemi) = Right(Left(strFormula, intCurSemi - 1), intCurSemi - intLastSemi - 1)
intLastSemi = intCurSemi
ElseIf intCurSemi = 0 Then
intSemi = intSemi + 1
ReDim Preserve aryList(intSemi)
aryList(intSemi) = Right((strFormula), intLen - intLastSemi)
blnCont = False
End If
Wend
End If
'For my attempt at passing the array to a function
'For Each e In aryList
' MsgBox e
'Next
'ReDim ValidList(UBound(aryList))
'ValidList = aryList
End Sub

Search string in a range (text template) and replace from dynamic rows

Currently I have a template which is in range called rngP1.
And this contains a text below:
"This is to confirm that strTitle has been enacted on strDate for strCompany."
Basically, I have a data in another sheet that will be used to replace these 3 strings from my template:
So what I would like to happen is that in every row data it will search strings strTitle, strDate, and strCompany and replace them according to the data of each row.
I have a code already, however, it doesn't work as I expected:
Sub example()
Dim wsMain As Worksheet
Set wsMain = Sheets("Main")
Dim wsTemplate As Worksheet
Set wsTemplate = Sheets("Template")
Dim textToReplace As Variant
Dim array_example()
Dim Find_Text As Variant
Dim str As String
last_row = wsMain.Range("A1").End(xlDown).Row 'Last row of the data set
ReDim array_example(last_row - 1, 2)
Find_Text = Array("strTitle", "strDate", "strCompany")
str = wsTemplate.Range("rngP1").Value
'Storing values in the array
For i = 0 To last_row - 1
array_example(i, 0) = wsMain.Range("A" & i + 2)
array_example(i, 1) = wsMain.Range("C" & i + 2)
array_example(i, 2) = wsMain.Range("D" & i + 2)
Next
For i = LBound(array_example, 1) To UBound(array_example, 1)
For j = LBound(array_example, 2) To UBound(array_example, 2)
For a = 0 To UBound(Find_Text)
str = Replace(str, Find_Text(a), array_example(i, j))
Next a
Next j
MsgBox str
Next i
End Sub
Wrong Output:
It should be:
This is to confirm that Title1 has been enacted on 13-October-18 for Company X.
And next one would be the next row which is title 2. So on and so fort.
If you have an alternative way to do it, I appreciate it.
Here is a working example:
You can push the data range from a worksheet into an array with one line without looping
DataArr = wsMain.Range("A2:D" & LastRow).Value
You need only 2 loops for the replacing:
one to loop through the data rows
one to loop through the variables to replace
Your template str was not initialized within the loop, but you need a fresh template for every data row.
Note that the array loaded from the range starts counting from 1 but the variables array starts counting from 0.
Option Explicit
Sub Example()
Dim Template As String
Template = "This is to confirm that strTitle has been enacted on strDate for strCompany."
'load your template string from worksheet here!
Dim Variables As Variant 'variables to be replaced
Variables = Array("strTitle", "strDate", "strCompany")
Dim wsMain As Worksheet
Set wsMain = ThisWorkbook.Worksheets("Main")
Dim LastRow As Long 'this method is more reliable to find the last used row
LastRow = wsMain.Cells(wsMain.Rows.Count, "A").End(xlUp).Row
Dim DataArr As Variant 'load the complete data range into an array
DataArr = wsMain.Range("A2:D" & LastRow).Value
Dim Output As String
Dim iRow As Long, iVar As Long
For iRow = LBound(DataArr, 1) To UBound(DataArr, 1) '1 to LastRow
Output = Template 'initialize with the template!
For iVar = LBound(Variables) To UBound(Variables) ' 0 to 2
Output = Replace(Output, Variables(iVar), DataArr(iRow, iVar + 1))
Next iVar
Debug.Print Output
Next iRow
End Sub

Excel 2013 Overflow due to lack of VBA optimization

I would like to export data from a consolidated sheet (DATA) to multiple sheets regarding criteria.
I have a total of 13 criteria, each criteria has to be exported in its dedicated sheet.
I'm trying to optimize this macro (only 2 criteria here) because it lag out
Sub copy()
Application.ScreenUpdating = False
Dim i As Long
Dim j As Long
Dim sh As Worksheet
Dim feuillePrincipale As Worksheet
Dim S01Sheet As Worksheet
Dim S02Sheet As Worksheet
Set feuillePrincipale = ThisWorkbook.Sheets("DATA")
Set S01Sheet = ThisWorkbook.Sheets("S01")
Set S02Sheet = ThisWorkbook.Sheets("S02")
For Each sh In ThisWorkbook.Worksheets
If sh.Name = "S01" Then
i = 2
j = 2
While Not IsEmpty(feuillePrincipale.Cells(i, 1))
If feuillePrincipale.Cells(i, 11).Value Like "S01*" Then
feuillePrincipale.Cells.Rows(i).EntireRow.copy S01Sheet.Rows(j)
j = j + 1
End If
i = i + 1
Wend
End If
If sh.Name = "S02" Then
i = 2
j = 2
While Not IsEmpty(feuillePrincipale.Cells(i, 1))
If feuillePrincipale.Cells(i, 11).Value Like "S02*" Then
feuillePrincipale.Cells.Rows(i).EntireRow.copy S02Sheet.Rows(j)
j = j + 1
End If
i = i + 1
Wend
End If
Next
Application.ScreenUpdating = True
End Sub
If you have any idea, I read I can use Advanced filter but as you guess I'm new in VBA so I'm listening any tips!
Here is the Advanced Filter method you asked for:
Public Sub Christophe()
Const FILTER_COLUMN = 11
Dim i&, rCrit As Range, rData As Range, aShts
aShts = ["SO"&row(1:13)]
Set rData = Sheets("DATA").[a1].CurrentRegion
Set rCrit = rData.Resize(2, 1).Offset(, rData.Columns.Count + 2)
rCrit(1) = rData(1, FILTER_COLUMN)
For i = 1 To UBound(aShts)
rCrit(2) = aShts(i, 1) & "*"
rData.AdvancedFilter xlFilterCopy, rCrit, Sheets(aShts(i, 1)).[a1].Resize(, rData.Columns.Count)
Next
rCrit.Clear
End Sub
The execution time should be instantaneous.
Note: this assumes that you do have 13 criteria, each starting with "SO" and that they occupy column 11 of the Data sheet. It also assumes that you already have 13 sheets named SO1... SO13 in the workbook.
UPDATE
Based on new information that the pattern of the criteria can change, please try this version instead. Note, that it assumes that the sheets already exist and that the sheet names match the criteria:
Public Sub Christophe()
Const FILTER_COLUMN = 11
Dim i&, rCrit As Range, rData As Range, aShts
aShts = Array("SO1", "SO2", "ADQ03", "LocS10")
Set rData = Sheets("DATA").[a1].CurrentRegion
Set rCrit = rData.Resize(2, 1).Offset(, rData.Columns.Count + 2)
rCrit(1) = rData(1, FILTER_COLUMN)
For i = 0 To UBound(aShts)
rCrit(2) = aShts(i) & "*"
rData.AdvancedFilter xlFilterCopy, rCrit, Sheets(aShts(i)).[a1].Resize(, rData.Columns.Count)
Next
rCrit.Clear
End Sub
Try using an array to set your criteria sheets:
Dim shArray As Variant
Dim shArrayString As String
Dim feuillePrincipale As Excel.Worksheet
Dim i As Long
Dim j As Long
Set feuillePrincipale = ThisWorkbook.Sheets("DATA")
j = 1
'// Create array and populate
shArray = Array("S01", "S02", "S03", "S04") '// add as required
'// Create string representation of array
shArrayString = "{"""
For i = LBound(shArray) To UBound(shArray)
shArrayString = shArrayString & shArray(i) & ""","""
Next
shArrayString = Left(shArrayString, Len(shArrayString) - 2) & "}"
'//Start loop
With feuillePrincipale
For i = 2 To .Cells(.Rows.Count, 1).End(xlUp).Row
If Not Evaluate("ISERROR(MATCH(" & Left(.Cells(i, 11), 3) & "," & shArrayString & ",0))") Then
.Rows(i).Copy Sheets(shArray(WorksheetFunction.Match(Left(.Cells(i, 11), 3), shArray, 0))).Cells(j, 1)
j = j + 1
End If
Next
End With
It's a bit unclear because if you follow the code you've posted - it's actually just copying and pasting data to the same sheet...
Yes, you should use an autofilter and use a special select to get only the visible cells.
If you want the loop method, you should loop through each row on sheets("DATA") and use a Select Case Statement to decide onto which sheet the data is placed.
By looping through each sheet you are adding loops that will slow it down.
Application.ScreenUpdating = False
Dim i As Long
Dim j As Long
Dim cel As Range
Dim sh As Worksheet
Dim feuillePrincipale As Worksheet
Dim S01Sheet As Worksheet
Dim S02Sheet As Worksheet
Set feuillePrincipale = ThisWorkbook.Sheets("DATA")
Set S01Sheet = ThisWorkbook.Sheets("S01")
Set S02Sheet = ThisWorkbook.Sheets("S02")
For Each cel In feuillePrincipale.Range(feuillePrincipale.Range("A1"), feuillePrincipale.Range("A1").End(xlDown))
Select Case Left(cel.offset(,10).value, 3)
Case "S01"
j = S01Sheet.Range("A" & Rows.count).End(xlUp).Offset(1).Row
feuillePrincipale.Cells.Rows(cel.Row).EntireRow.copy S01Sheet.Rows(j)
Case "S02"
j = S02Sheet.Range("A" & Rows.count).End(xlUp).Offset(1).Row
feuillePrincipale.Cells.Rows(cel.Row).EntireRow.copy S02Sheet.Rows(j)
'Case .... keep adding select statement till you get to the last condition
Case Else
End Select
Next cel
Application.ScreenUpdating = True

Resources