I am trying to add the following logic to a macro in Excel from a SQL proc and am having trouble converting it:
CASE WHEN Dept < '600' THEN '0' + convert(varchar,RTRIM(Dept))
when Dept between '650' and '899' THEN convert(varchar,RTRIM(Dept)) + '0'
ELSE convert(varchar,RTRIM(Dept)) + '0' END As Dept_Num
The display I desire is this:
If the Dept number in Table1[DEPT1] is between 0 and 599, then add a
leading zero, aka 001 becomes 0001.
If the Dept number in Table1[DEPT1] is between 650 and 899, then add
a following zero, aka 650 becomes 6500.
If the Dept number in Table1[DEPT1] is any other number, add a
following zero, aka 600 becomes 6000.
Dim deptnum As Integer, result As String
deptnum = Range("Table1[Dept1]").Value
If deptnum < 600 Then deptnum = Left(Range("Table1[Dept1]") & "0000", 4)
Ifelse deptnum = Right(Range("Table1[Dept1]") & "0000", 4)
As you can see, I am struggling with the concept of multiple conditions and this is my first time writing a statement like this...any help is appreciated!
Dim varcell as Variant
For Each varcell in ThisWorkbook.Sheets("DeptData").ListObjects(1).ListColumns(8).DataBodyRange
If varcell.value < 600 Then
varcell.value = "0" & cstr(varcell.value)
Else
varcell.value = cstr(varcell.value) & "0"
End If
Next
Please note that we need to update the points where the ' is.
Sub Testdeptnum()
Dim deptnum As Integer
Dim result As String
Dim deptrng As Range
Dim SourceSheet As Worksheet
Set SourceSheet = ThisWorkbook.Sheets("Data")
Set deptrng = SourceSheet.Range("DEPT1")
For Each c In deptrng.Cells
If c.Value < 600 Then
deptnum = c.Value
result = "0" & deptnum
SourceSheet.Cells(c.Row, 2).NumberFormat = "00##"
SourceSheet.Cells(c.Row, 2).Value = result
ElseIf c.Value > 650 And c.Value < 899 Then
deptnum = c.Value
result = deptnum & "0"
SourceSheet.Cells(c.Row, 2).NumberFormat = "00##"
SourceSheet.Cells(c.Row, 2).Value = result
Else
deptnum = c.Value
result = deptnum & "0"
SourceSheet.Cells(c.Row, 2).NumberFormat = "00##"
SourceSheet.Cells(c.Row, 2).Value = result
End If
Next c
End Sub
I'm not entirely sure what the format of your Table1 data is in thus I can't really be sure what format it needs to be targeting, etc.
The gist of it is this: you need to iterate through the cells, not the range. The range.value can't return a value for each cell, it will return a single value. In order to evaluate the individual value of each cell in a range, you have to step through the range in a loop (I used a For Each).
Keep in mind, your results may vary depending on what format your workbook/data is in. Here my "Table1" is assumed to be a Worksheet named "Data" which you can replace as you need to. If Table1 is your workbook or file name, you don't need to include it at all unless you also interact with another file/workbook within your same function/module.
Looks like you're just missing a few keywords. Also, formatting this in the below manner will make this more obvious:
Dim deptnum As Integer, result As String
deptnum = Range("Table1[Dept1]").Value
If deptnum < 600 Then
deptnum = Left(Range("Table1[Dept1]") & "0000", 4)
Else
deptnum = Right(Range("Table1[Dept1]") & "0000", 4)
End If
Related
I also need to apply Match function in VBA, and the Arg 1 also has to be Date. The Array (Arg 2) I defined in the "Name Manager" (Tab Formula of Excel). I need to use the Name Manager since I have several array/ table reference based on the type of currency. For example, if the currency is USD, the reference table is USDREF which I defined as Range("B21:B36"). For the other currency, ex. JPY, I use array/ table reference JPYREF which is defined as JPYREF from Range("B36:B50"). It applies for other currencies. Here's my code :
Dim Original As Workbook
Dim SRate As Worksheet
Dim ccy As String
Dim i, j, k, l, Rank1, Rank2, Rate1, Rate2 As Integer
Dim srow, erow, x As Long
Dim DateFCYIDR, Date1, Date2 As Date
Sub fcyidr()
Set Original = ThisWorkbook
Set SRate = Original.Sheets("Rate")
SRate.Activate
Range("A36").Select
ccy = ActiveCell.Value
While ActiveCell.Value <> ""
For i = 1 To 15
If i = 1 Or i = 15 Then
j = ActiveCell.Row
ActiveCell.Offset(0, 8) = "=+$G$" & j - 15 & "/G" & j
ActiveCell.Offset(1, 0).Select
Else
DateFCYIDR = ActiveCell.Offset(0, 7).Value
Rank1 = WorksheetFunction.Match(CLng(DateFCYIDR), USDREF, 0)
End If
Next i
Wend
where USDREF is defined in Name Manager. But it didn't work.
How do I solved the problem?
So I am a student currently studying VBA. I'm doing ok in the class but still kind of iffy on what how to program. For the question I'm on I have to take numbers in a column and multiply them unless they are less than or equal to zero. Here is an example done by hand of what the result should look like (which are not the same numbers as the actual problem, these are much simpler).
Here is what I have written so far. I'm kinda treating the column as a 1 x 10 array.
Function multpos(C As Variant)
Dim i As Integer
Dim MD As Long
For i = 1 To 9
MD = C(1, i) * C(1, i + 1)
Next i
If C(i, 1) > 0 Then C(i, 1) = C(i, 1) Else C(i, 1) = 1
If C(i + 1, 1) > 0 Then C(i + 1, 1) = C(i + 1, 1) Else C(i + 1, 1) = 1
multpos = MD
End Function
While MD satisfies the equation, it only works for the first two and then doesn't. Intuitively I want to do something like this
MD = C(1, i) * C(1, i)
Next i
Etc but this is also not mathematically correct. So if I had
MD = C(1, i)
how can I get it to multiply by the next value from here? Feel free to look at my other code and correct me as well since that could just as easily be wrong. Thank you for help in advance.
Something like this should work for you. I tried to comment the code for clarity:
Public Function PRODUCTIF(ByVal vValues As Variant, ByVal sCriteria As String) As Double
Dim vVal As Variant
Dim dResult As Double
'Iterate through vValues and evaluate against the criteria for numeric values only
For Each vVal In vValues
If IsNumeric(vVal) Then
If Evaluate(vVal & sCriteria) = True Then
'Value is numeric and passed the criteria, multiply it with our other values
'Note that until a valid value is found, dResult will be 0, so simply set it equal to the first value to avoid a 0 result
If dResult = 0 Then dResult = vVal Else dResult = dResult * vVal
End If
End If
Next vVal
'Output result
PRODUCTIF = dResult
End Function
And you would call the function like this: =PRODUCTIF(A1:A10,">0")
you could exploit AutoFiler() method
Function multpos(C As Range, criteria As String)
Dim MD As Long
Dim cell As Range
With C.Columns(1)
If IsEmpty(.Cells(1, 1)) Then .Cells(1, 1) = "|header|"
.AutoFilter Field:=1, Criteria1:=criteria
If Application.WorksheetFunction.Subtotal(103, .Cells) > 1 Then
MD = 1
For Each cell In C.SpecialCells(xlCellTypeVisible).SpecialCells(xlCellTypeConstants, xlNumbers)
MD = MD * cell.Value
Next cell
End If
If .Cells(1, 1) = "|header|" Then .Cells(1, 1).ClearContents
.Parent.AutoFilterMode = False
End With
multpos = MD
End Function
to be exploited in your main sub like:
MsgBox multpos(Range("A1:A10"), ">0")
I just started VBA coding and I am struck here:
For one cell this program works:
Dim score As Integer, result As String
score = Range("A1").Value
If score >= 60 Then
result = "pass"
Else
result = "fail"
End If
Range("B1").Value = result
And how about a column of cells? Can loop works for this?
My code using loop - But How to define variable in range?
Dim score As Integer, result As String, I As Integer
score = Range("AI").Value
For I = 1 To 6
If score >= 60 Then
result = "pass"
Else
result = "fail"
End If
Range("BI").Value = result
Next I
Thanks in advance!
Almost, you just need to use string concatenation (&)
Dim score As Integer, result As String, I As Integer
'score = Range("AI").Value
For I = 1 To 6
score = Range("A" & I).Value '// Needs to be inside the loop to update.
If score >= 60 Then
result = "pass"
Else
result = "fail"
End If
Range("B" & I).Value = result
Next I
This can also be written as:
For i = 1 To 6
Range("B" & i).Value = IIf(Range("A" & i).Value >= 60, "pass", "fail")
Next
you can also go with a "formula" approach:
Range("B1:B6").FormulaR1C1 = "=If(RC1 >= 60, ""pass"", ""fail"")"
thus maintaining that check active for any possible subsequent change in columns A cells values
or, should you want to have "static" values only:
With Range("B1:B100")
.FormulaR1C1 = "=If(RC1 >= 60, ""pass"", ""fail"")"
.Value = .Value
End With
I have tried every approach that I know but I haven't been able to set the cell value with its date. I suspect there is a tiny problem, but I don't know where.
Function getDates(strDateString) As Date
Dim x As Variant
x = Split(strDateString, "/")
If UBound(x) = 2 Then
strday = x(2)
strMonth = x(1)
strYear = x(0)
Else
strYear = Mid(strDateString, 1, 4)
strMonth = Mid(strDateString, 6, 2)
strday = Mid(strDateString, 8, 2)
End If
getDates = DateSerial(strYear, strMonth, strday)
End Function
For k = 11 To 12
strdates = getDates(Cells(2, k).Value)
Set Worksheets(strNewSheetName).Range(Cells(1, k), Cells(1, k)).Value = CStr(strdates)
MsgBox (strdates)
Next k
MsgBox will return the correct date, but the value always remains empty! I tried converting it to a string with each of these methods:
cells(r,c).value
CSTR(strDates)
cdate()
But none of these worked.
Change
Set Worksheets(strNewSheetName).Range(Cells(1, k), Cells(1, k)).Value = CStr(strdates)
to
Worksheets(strNewSheetName).Cells(1, k).Value = CStr(strdates)
Set is used to set the value of an object but you're not dealing with an object when setting a cell value. Furthermore, defining a range from one cell to itself is also redundant.
First time poster and new to programming in general. I have a project in which i have to build a financial model to mine for data in excel. I have succeeded in building said model on VBA. I have ran tests on 3,000 line dataset and it was successful. I will briefly explain what it does.
I track a given stock on a given day on multiple exchanges. I download the data (roughly 935,000 lines) The first step is copy all the data for a given exchange (roughly 290,000) onto a new sheet (this takes roughly 8min), then I create a new column to log the bid ask spread (12secs), the next step is what Im having trouble with, I basically rank each line of data twice, one column for Bid size and one column for Ask size. I created a function which uses excel Percentile function and ranks based on where the given bid and ask size lands. As of right now, I have been running the Macro for the last 35min and has yet to execute. I cant attempt the other macros since each macro depends on the previous one.
So my basic issue is that since my data set is large, my model keeps crashing.The code seems to be fine when working with the test data, and it doesn't throw any errors when I run the program, but with the larger data set it just crashes. Does anyone have any suggestions? Is this normal with such large amounts of data?
Thanks in advance.
Sham
Here is the sub and function thats giving me the trouble, the sub takes in the required inputs to run the function and then pops into the assigned cell. The code is suppose to repeat the process for three separate sheets. For now, Id like it to work on one sheet, hence used the comments to not include the loop
Sub Bucketting()
Dim firstRow As Long
Dim lastRow As Long
Dim counter As Long
Dim bidRange As Range
Dim offerRange As Range
Dim bidScroll As Range
Dim offerScroll As Range
Dim Ex As String
Dim i As Integer
'For i = 1 To 1 Step 1 'Sheet Selection Process
' If i = 1 Then
' Ex = "Z"
' ElseIf i = 2 Then
' Ex = "P"
' Else
' Ex = "T"
' End If
Sheets("Z").Select 'Sheet selected
With ActiveSheet
firstRow = .UsedRange.Cells(1).Row + 1
lastRow = .UsedRange.Rows.Count
Set bidRange = .Range("F2:F" & lastRow)
Set offerRange = .Range("G2:G" & lastRow)
For counter = lastRow To firstRow Step -1
Set bidScroll = .Range("F" & counter)
Set offerScroll = .Range("G" & counter)
With .Cells(counter, "J")
.Value = DECILE_RANK(bidRange, bidScroll)
End With
With .Cells(counter, "K")
.Value = DECILE_RANK(offerRange, offerScroll)
End With
Next counter
End With
Range("J1").Select
ActiveCell = "Bid Rank"
ActiveCell.Offset(0, 1) = "Offer Rank"
'Next i
End Sub
Function DECILE_RANK(DataRange, RefCell)
'Credit: BJRaid
'DECILE_RANK(The Range of data)
'Declares the function that can be called in the spreadsheet cell - enter '=DECILE_RANK(A5:A50,A5)
'Using the percentile worksheet function calculate where the 10th, 20th etc percentile of the reference range are
DEC1 = Application.WorksheetFunction.Percentile(DataRange, 0.1)
DEC2 = Application.WorksheetFunction.Percentile(DataRange, 0.2)
DEC3 = Application.WorksheetFunction.Percentile(DataRange, 0.3)
DEC4 = Application.WorksheetFunction.Percentile(DataRange, 0.4)
DEC5 = Application.WorksheetFunction.Percentile(DataRange, 0.5)
DEC6 = Application.WorksheetFunction.Percentile(DataRange, 0.6)
DEC7 = Application.WorksheetFunction.Percentile(DataRange, 0.7)
DEC8 = Application.WorksheetFunction.Percentile(DataRange, 0.8)
DEC9 = Application.WorksheetFunction.Percentile(DataRange, 0.9)
' Calculate the Decile rank that the reference cell value sits within
If (RefCell <= DEC1) Then DECILE_RANK = 1
If (RefCell > DEC1) And (RefCell <= DEC2) Then DECILE_RANK = 2
If (RefCell > DEC2) And (RefCell <= DEC3) Then DECILE_RANK = 3
If (RefCell > DEC3) And (RefCell <= DEC4) Then DECILE_RANK = 4
If (RefCell > DEC4) And (RefCell <= DEC5) Then DECILE_RANK = 5
If (RefCell > DEC5) And (RefCell <= DEC6) Then DECILE_RANK = 6
If (RefCell > DEC6) And (RefCell <= DEC7) Then DECILE_RANK = 7
If (RefCell > DEC7) And (RefCell <= DEC8) Then DECILE_RANK = 8
If (RefCell > DEC8) And (RefCell <= DEC9) Then DECILE_RANK = 9
If (RefCell > DEC9) Then DECILE_RANK = 10
End Function
935,000 lines is a lot for excel. Like, really a lot. Barring saying using a real database, If your application is literally putting a =Percentile(...) in each cell, I would recommend Trying to use another tool for that. Perhaps something within VBA itself. More generally, use something outside of a cell - then store the result value in the cell. There is a lot of overhead in maintaining those formulas that are interdependent on 935k rows of data.
The problem is that your looping through each row individually, the Excel way is to try and work with whole ranges at once whenever possible. I would load the ranges into arrays, then modify your DECILE_RANK code to work with the items in the array.
Note that variant arrays that read ranges in are 2-D.
Here is the fully functioning code including my custom VBA array slicer. Note that it was only tested on a small dataset:
Sub Bucketting()
Dim lastRow As Long
Dim bidArray As Variant
Dim offerArray As Variant
Sheets("Sheet1").Select 'Sheet selected
With ActiveSheet
lastRow = .UsedRange.Rows.Count + 1
bidArray = .Range("F2:F" & lastRow)
offerArray = .Range("G2:G" & lastRow)
Range("J2:J" & lastRow).Value = GetArraySlice2D(DECILE_RANK(bidArray), "column", 1, 1, 0)
Range("K2:K" & lastRow).Value = GetArraySlice2D(DECILE_RANK(offerArray), "column", 1, 1, 0)
End With
Range("J1").Select
ActiveCell = "Bid Rank"
ActiveCell.Offset(0, 1) = "Offer Rank"
End Sub
Function DECILE_RANK(DataRange As Variant) As Variant
' Credit: BJRaid
' DECILE_RANK(The Range of data)
' Declares the function that can be called in the spreadsheet cell - enter '=DECILE_RANK(A5:A50,A5)
Dim DEC(0 To 10) As Variant
Dim i As Integer, j As Integer
'Using the percentile worksheet function calculate where the 10th, 20th etc percentile of the reference range are
DEC(0) = 0
For i = 1 To 9
DEC(i) = Application.WorksheetFunction.Percentile(DataRange, 0.1 * i)
Next i
DEC(10) = Application.WorksheetFunction.Max(DataRange)
' Calculate the Decile rank that the reference cell value sits within
For i = 1 To UBound(DataRange, 1)
For j = 1 To 10
If ((DataRange(i, 1) > DEC(j - 1)) And (DataRange(i, 1) <= DEC(j))) Then
DataRange(i, 1) = j
Exit For
End If
Next j
Next i
DECILE_RANK = DataRange
End Function
Public Function GetArraySlice2D(Sarray As Variant, Stype As String, Sindex As Integer, Sstart As Integer, Sfinish As Integer) As Variant
' this function returns a slice of an array, Stype is either row or column
' Sstart is beginning of slice, Sfinish is end of slice (Sfinish = 0 means entire
' row or column is taken), Sindex is the row or column to be sliced (NOTE:
' 1 is always the first row or first column)
' an Sindex value of 0 means that the array is one dimensional 3/20/09 Lance Roberts
Dim vtemp() As Variant
Dim i As Integer
On Err GoTo ErrHandler
Select Case Sindex
Case 0
If Sfinish - Sstart = UBound(Sarray) - LBound(Sarray) Then
vtemp = Sarray
Else
ReDim vtemp(1 To Sfinish - Sstart + 1)
For i = 1 To Sfinish - Sstart + 1
vtemp(i) = Sarray(i + Sstart - 1)
Next i
End If
Case Else
Select Case Stype
Case "row"
If Sfinish = 0 Or (Sstart = LBound(Sarray, 2) And Sfinish = UBound(Sarray, 2)) Then
vtemp = Application.WorksheetFunction.Index(Sarray, Sindex, 0)
Else
ReDim vtemp(1 To Sfinish - Sstart + 1)
For i = 1 To Sfinish - Sstart + 1
vtemp(i) = Sarray(Sindex, i + Sstart - 1)
Next i
End If
Case "column"
If Sfinish = 0 Or (Sstart = LBound(Sarray, 1) And Sfinish = UBound(Sarray, 1)) Then
vtemp = Application.WorksheetFunction.Index(Sarray, 0, Sindex)
Else
ReDim vtemp(1 To Sfinish - Sstart + 1)
For i = 1 To Sfinish - Sstart + 1
vtemp(i) = Sarray(i + Sstart - 1, Sindex)
Next i
End If
End Select
End Select
GetArraySlice2D = vtemp
Exit Function
ErrHandler:
Dim M As Integer
M = MsgBox("Bad Array Input", vbOKOnly, "GetArraySlice2D")
End Function
I'm not sure if this will directly address your problem, but have you considered using Application.ScreenUpdating = False? Don't forget to set it back to true once your data has processed.