Excel Power Query, VBA and recalculation: It's complicated - excel

I recently got started with Excel Power Query, retrieving data from SharePoint. Things worked nicely until I noticed that some cells in a worksheet doing lookups on the query result (both in the same workbook) did not get calculated. The value just showed 0. When I selected the cell and hit enter, recalculation for the cell happened and the correct value was shown. Calculation method is "Automatic except data tables" (I hope I translated this correctly).
I traced the issue to a specific UDF I use within many of my UDFs, a function checking whether a range (or more precise a parameter) has been recalculated and if not, the outer UDF is exited immediately. This is a common recommendation found on many sites to improve performance and I never had any issues with it - until the arrival of power query, that is.
There seem to be a few possible solutions which one could pursue:
Somehow trigger a full recalculation&rebuild after a query is executed. Would require some kind of trigger or event (which I currently don't know).
Make the UDF which checks if the argument is dirty more intelligent. By the observed behavior, my takeaway is, that the table containing the query result always counts as dirty(?). If so, teaching the UDF about this, would help.
Make sure that the query results always count as clean in excel.
I would rather avoid the first solution.
The third solution sound as if it may have unwanted side effects, so my preference would be to the second venue: Improve the UDF checking if an argument is dirty.
Here it is in its current state (leading to the above described behavior):
Public Function chkRngCalculated(theParameter As Variant) As Boolean
Dim vHasFormula As Variant
chkRngCalculated = True
On Error GoTo Fail
If TypeOf theParameter Is Excel.Range Then
vHasFormula = theParameter.HasFormula
' HasFormula can be True, False or Null:
' Null if the range contains a mix of Formulas and data
If IsNull(vHasFormula) Then vHasFormula = True
If vHasFormula Then
' CountA returns 0 if any of the cells are not yet calculated
If Application.WorksheetFunction.CountA(theParameter) = 0 Then chkRngCalculated = False
End If
' a calculated parameter is Empty if it references uncalculated cells
ElseIf VarType(theParameter) = vbEmpty Then
chkRngCalculated = False
End If
Exit Function
Fail:
chkRngCalculated = False
End Function
It is usually called in one of the first lines of a UDF, never from Excel cells directly obviously. The following shows how it is called from my lookup function:
Public Function searchKeyed(target As Range, rowKey As Variant, columnKey As Variant) As Variant
If Not chkRngCalculated(target) Then Exit Function
...
I verified that, if I comment out the if statement, the values do get updated correctly upon query update.
Background updating of the queries has been turned off. Switching to 'Automatic' recalculation from 'Automatic except data tables' does not change the described behavior.
Has someone had a similar experience and found a solution?

Related

Searching for Specific Column Headers in Excel File - Runtime Error 91

I am attempting to write some excel vba code that will process the content of certain columns of data. Given the worksheet has some level of dynamic change (columns added and removed from time to time), I want my code to "find" the specific columns by their header names, and ultimately return the column number. My File has roughly 50 columns.
The problem is this: My code works just fine to find many of the columns (headers) I am interested in returning the column index, but some of the columns "while clearly existing", will return Nothing and thus, throws the runtime 91 error.
I can say, without a doubt that when I execute the .find, that truly, the columns DO exist (like the Comments column). I can randomly change the failing hdr search column to a different header name, passing it to the function in the code and some columns are found just fine, and other, cause the runtime error. I have checked the "failing" headers for special characters, blanks, LF's etc. No luck. Even tried re-ordering the 4 rows using FindColHdrNum function. Again, no luck.
Was hoping fresh eyes may provide answer. Simplified code is below which is triggered by a button on main excel worksheet. I have not worked with functions much in VBA, and even where the function does not generate the Runtime Error, it is not returning the column value, but this is a secondary problem I can work on once I get the find code not blowing up (returning 0).
Sub Button119_Click()
Dim L4RankCol As Integer
Dim DecomDriverCol As Integer
Dim SupTermImpactYrCol As Integer
Dim Comments As Integer
Dim L3RankCol As Integer
L4RankCol = FindColHdrNum("L4 Rank") '<-- This works
DecomDriverCol = FindColHdrNum("Decom Driver") '<-- This works
SupTermImpactYrCol = FindColHdrNum("Support Termination Impact Yr") '<-- This works
Comments = FindColHdrNum("Comments") '<-- This does not work
End Sub
Function FindColHdrNum(strHdr As String) As Integer
Dim rngAddress As Range
Set rngAddress = Range("Headers").Find(strHdr)
FindColumnHdrNum = rngAddress.Column '<--runtime error is caused by Nothing returned
End Function
Issue turns out to be a spurious line feed that was embedded in the header. It was strange as I kept re-typing it, but of course, I would always start at the "first letter" of the "comment" header, when in fact, the character preceded that. Thanks to all, for the help!
The name of your function is FindColHdrNum but you wrote this into the function:
FindColumnHdrNum = rngAddress.Column
Instead of:
FindColHdrNum = rngAddress.Column

spotfire calculated column using over function

I want to create a calculated column "indicateur" that traces the boolean values
when I have a True, I increment the indicator by 1, however i want the false rows to have the value of the last true indicator.
and when i pass to a new ID, the incrementation starts from zero.
I already tried some spotfire expression using the over function but not getting the right results
case
when [boolean] then sum(If([boolean],1,0)) over (Intersect([ID],AllPrevious([ID])))
else 0
end
You have a couple of issues here. Your case statement is sub setting the data... it will only calculate sums where boolean is true.
The main issue is the over statement though. Something like this should give the correct answer
sum(If([boolean],1,0)) over (Intersect([ID],AllPrevious([Timestamp])))

vba quarterly returns

Can anybody tell me a VBA Function or button that would calculate quarterly returns only on a quarterly basis (i.e., first quarter return is based on January till March returns) without overlapping observations.
The function that I'm using now is just the AVERAGE of, for example, returns from January until March, but the actual calculation cell is in April.
An example of what I'm asking is in the picture link. I would like the function to run for the time period that I choose.
Thank you very much!
edit: I'm asking for help with the code because I'm new to vba so I cannot do it yet on my own.
edit2: The code that I adapted from class is this:
Option Explicit
Public Sub QuarterReturns()
Dim rng As Range
Set rng = Range("B2")
Dim i As Long, n As Long
n = rng.End(x2Down).Column - rng.Row + 1
Set rng = rng.Resize(n, 1)
Dim returns As Variant
returns = rng.Value2
Dim quarter As Variant
ReDim quarter(1 To n - 1, 1 To 1)
For i = 1 To n - 1
ror(i, 1) = prices(i + 1, 1) / prices(i, 1) - 2#
Next i
rng.Offset(1, 2).Resize(n - 1, 2).Value2 = quarter
End Sub
Okay, so the first point: StackOverflow isn't a code-writing service. Asking us, "Hey, can someone write me something that does X?" is likely to get you ridiculed/scolded/etc.
That said, I'm going to help you with two building-blocks that might help you get to the finish line on your own.
Prereq: VBA is overused within Excel. Excel has amazingly powerful abilities just within the formulas. In this case, you can actually get what you want with zero code, and only one additional (calculated) column. The reason this is probably a better solution is because Excel docs with VBA macros tend to be disabled (for very good reason) and it becomes a lot harder to see what's being done behind the scenes. With a calculated column, someone interested in digging behind the numbers can easily see what's going on without having to delve into code.
Building Block #1: Year and Month functions.
If you've got a date column (which you don't right now - you need to change that column to something that actually represents a date object), you can actually get the Quarter by assembling something like:
=YEAR(A1)&"-Q"&((MONTH(A1)+2)/3)
Basically, the YEAR() function gets the year, and the MONTH() function gets the month number. From there, I just use some basic math and string-combining to get a result like:
2018-Q2
Building Block #2 - CountIf/SumIf/AverageIf
Excel has some really great xxxIf() functions, that will get the average/sum/count/etc for a range, but only for the values that meet a certain criteria.
So in your case, if you Sum of all the 2018-Q2 records, you simply use the SUMIF() function to add up all the values, where the entry equals '2018-Q2'.
Hope that helps you out on your task. If not, I'd actually encourage you to break the problem down into smaller subtasks, google those subtasks - and if you can't find anything for a specific task, ask a question about just that part. Asking, "Hey, how do I sum three cells in VBA?" goes over a lot better than "Hey, write this function for me" :-)

Autofilter criteria value in VBA

I have a table with activated autofilter mode and it is known that only Criteria1-type filtering is applicable (i.e. items of interest are implicitly indicated). My goal is to extract a criteria list for each column in VBA. I used IsArray(.Filters(i).Criteria1) to determine if there is more than 1 item selected for a particular column and everything works fine when either 1 or more than 2 items are selected. However, when I select 2 items, .Filters(i).Criteria1 is not recognized as an array for some reason. .Filters(i).Criteria1 returns only the item that is higher in the list.
Could anyone explain me: why is it so and what is the best way to deal with this problem?
This is an old as question, but in case anyone ends up confused here, confused.
With the a filters object (part of the worksheet.autofilter.filters collection probably)
Here is 1 happens:
1 filter on a column:
filters(i).criteria1 is a string
2 filters on a column:
filters(i).criteria1 is a string
filters(i).criteria2 is a string
3 or more filters on a column:
filters(i).criteria1 is an array of strings (which is a variant)
filters(i).criteria2 doesn't exist
This isn't that clear from MSDN, but it's what happens. My only guess is that in past versions of excel it wasn't possible to add more than 1 filter criteria, and so the old criteria1 and criteria2 types were maintained for backwards compatibility. Still though, it's a stupid system.
Which means that the only way to get the filter properties (as far as I can see) is as follows:
For Each f In ActiveSheet.AutoFilter.Filters
If f.On Then
If IsArray(f.Criteria1) Then
cA = f.Criteria1
Else
c1 = f.Criteria1
On Error Resume Next
c2 = f.Criteria2
On Error GoTo 0
End If
End If
Next
Because Criteria2 errors for both IsEmpty(f.Criteria1) and IsNull(f.Criteria1) so as far as I can see the only way to check if it exists is with an ugly on error resume next

Excel ran out of resources while attempting to calculate one or more formulas

I have a workbook to do 'smart'-graphs on my expenses. It's been running for a year and there are now a lot of graphs and expenses. Excel now throws an out-of-resources error whenever I change anything or open the workbook. Thing is, I have lots of resources and its not using hardly any of them.
Win8 64bit w/ 8 core CPU and 32GB of ram
Office 2013 64bit
I have 2 sheets, the first sheet called Expenses has 3 columns [Date,Description,Amount] and about 1500 rows of data. The second sheet has a LOT (500 or so) of formulas that are all the same and aim to do "Sum all expenses between date X and Y where description matches -some needle-". The formula I have is this:
=
ABS(
SUMPRODUCT(
--(Expenses!A:A >= DATE(2011,12,1)),
--(Expenses!A:A < DATE(2012,1,1)),
--(ISNUMBER(FIND(C50,Expenses!B:B))),
Expenses!C:C
)
)
Can I give Excel more resources? (I'm happy for it to use all my ram, and chug my CPU for a few minutes).
Is there a more efficient way I can do this formula?
I understand that this formula is creating a large grid and masking my expenses list with it, and that for each formula this grid has to get created. Should I create a macro to do this more efficiently instead? If I had a macro, I would want to call it from a cell somehow like
=sumExpenses(<startDate>, <endDate>, <needle>)
Is that possible?
Thanks.
I had a similar problem where there were a few array formulas down about 150 rows and I got this error, which really baffled me because there really aren't that many formulas to calculate. I contacted our IT guy and he explained the following, some of which I understand, most of which I don't:
Generally when the computer tries to process large amounts of data, it uses multi-threaded calculation, where it uses all 8 processors that the computer tricks itself into thinking it has. When multi-threaded calculation is turned off, the computer doesn't throw the 'Excel ran out of resources...' error.
To turn off multi-threaded calculation, got to the 'File' tab in your Excel workbook and select 'Options'. On the right side of the box that appears select 'Advanced' and scroll down to the heading 'Formulas'. Under that heading is a check box that says 'Enable multi-threaded calculation'. Untick it, then select 'OK' and recalculate your formulas.
I had a go at creating a function that hopefully replicates what your current equation does in VBA with a few differences. Since I don't know the specifics of your second sheet the caching might not help at all.
If your second sheet uses the same date range for all calls to sumExpenses then it should be a bit quicker as it pre-sums everything on the first pass, If your date range changes throughout then its just doing a lot of work for nothing.
Public Cache As Object
Public CacheKey As String
Public Function sumExpenses(ByVal dS As Date, ByVal dE As Date, ByVal sN As String) As Variant
Dim Key As String
Key = Day(dS) & "-" & Month(dS) & "-" & Year(dS) & "_" & Day(dE) & "-" & Month(dE) & "-" & Year(dE)
If CacheKey = Key Then
If Not Cache Is Nothing Then
If Cache.Exists(sN) Then
sumExpenses = Cache(sN)
Exit Function
End If
Set Cache = Nothing
End If
End If
CacheKey = Key
Set Cache = CreateObject("Scripting.Dictionary")
Dim Expenses As Worksheet
Dim Row As Integer
Dim Item As String
Set Expenses = ThisWorkbook.Worksheets("Expenses")
Row = 1
While (Not Expenses.Cells(Row, 1) = "")
If Expenses.Cells(Row, 1).Value > dS And Expenses.Cells(Row, 1).Value < dE Then
Item = Expenses.Cells(Row, 2).Value
If Cache.Exists(Item) Then
Cache(Item) = Cache(Item) + Expenses.Cells(Row, 3).Value
Else
Cache.Add Item, Expenses.Cells(Row, 3).Value
End If
End If
Row = Row + 1
Wend
If Cache.Exists(sN) Then
sumExpenses = Cache(sN)
Else
sumExpenses = CVErr(xlErrNA)
End If
End Function
Public Sub resetCache()
Set Cache = Nothing
CacheKey = ""
End Sub
There could be many causes of this. I just wish Excel would tell us which one (or more) of the 'usual suspects' is committing the offence of RAM hogging at this time.
Also look for
Circular references
Fragmented Conditional formatting (caused by cutting, pasting, sorting, deleting and adding cells or rows.
Errors resulting in #N/A, #REF, #DIV/0! etc,
Over-use of the volatile functions TODAY(), NOW(), etc.
Too many different formats used
... in that order
While you're there, check for
Broken links. A formula relying on a fresh value from external data could return an error.
Any formulas containing #REF!. If your formulas are that messed these may well be present also. They will not cause an error flag but may cause some unreported errors. If your formulas are satisfied by an earlier condition the part of the formula containing #REF! will not be evaluated until other conditions prevail.
Fragmented conditional formatting was the case for me.
Older versions of the same workbook did not have an issue. Today, I cut/pasted many cells and the issue started occurring.
Removing the columns where I was cutting/pasting resolved the issue for me.
This is difficult to diagnose since conditional formatting does not immediately standout like normal formulas.

Resources