Archive for the ‘Microsoft Excel’ Category
Excel EOMONTH Function
I fielded a question from blog reader on a three year old post about the EOMONTH (End Of Month) function in Excel. The reader want to write a mega-function (a term for putting two or more functions together to solve a problem, according to Mr. Excel). The function would return the first day of any month for the first day of the month and the first day of the next month for any day after the first day of the last month.
I wrote a quick little solution for the user, as shown below:
=IF(A1=EOMONTH(A1,-1)+1,EOMONTH(A1,-1)+1,EOMONTH(A1,0)+1) |
The results would look like:
The user ran into a problem with the example because the date was being calculated and not an integer value. The formula returned the first day of the next month when it should have returned the first day of the current month. That meant the source value in the cell wasn’t an integer. It was a real number. Integer values in Excel are numeric values for 12:00:00 A.M. of any day, and real numbers can be any time of the day.
Unfortunately, the EOMONTH
function is a pseudo-overloaded function, and the EOMONTH
function takes an int
or double
(integer or real number) as it’s parameter. That’s because Excel only supports a NUMBER
data type, which can be an integer or real number.
The EOMONTH
function always returns an integer. The IF
function I provided was designed to compare an integer-based date cell value against the result of the EOMONTH
function. It wasn’t robust enough to support a comparison of 11/1/13 at 12:01 A.M. against the result of the EOMONTH
function (11/1/13 at 12:00 A.M.). That’s why it returned the first of the next month instead of the first of the current month.
The more complete solution requires using the TRUNC
function around the source date (B4
or C4
in the following example) to ensure the IF
statement compares dates not time-stamps.
=IF(TRUNC(B4)=EOMONTH(B4,-1)+1,EOMONTH(B4,-1)+1,EOMONTH(B4,0)+1) |
You should note that the TRUNC
function effectively rounds down to the integer and removes the fractional portion of the real number before making a comparison. It’s an explicit casting process when we take proactive measures to ensure the value before the IF
function compares it.
Excel: Navigate a range
Somebody posted a comment on my Excel UDF Tutorial, and on reflection it appears he’d like to navigate a range in VBA. While I don’t see a purpose in that because it duplicates either the VLOOKUP
or HLOOKUP
function (depending on whether you’re navigating a column or row).
Anyway, here’s how you’d do it in VBA:
Function Finding(lookupWord As String, lookupRange As Range, lookupColumn As Integer) As Integer ' Define variables. Dim lookupIndex As Integer Dim lookupMax As Integer Dim returnValue As Integer ' Assign values to variables. lookupIndex = 1 lookupMax = lookupRange.Rows.Count returnValue = 1 ' Print starting and ending index values. MsgBox ("[" + CStr(lookupIndex) + "][" + CStr(lookupMax) + "]") ' Loop until found, but exit when not found. Do Until lookupRange.Cells(lookupIndex, 1).Value = lookupWord ' Print diagnostic value to determine what's evaluated. MsgBox ("[" + CStr(lookupIndex) + "] [" + CStr(lookupRange.Cells(lookupIndex, 1).Value) + "]") ' Fail safe exit when no matching value is found. If lookupIndex = (lookupMax + 1) Then ' Assign a 0 (zero) as a false return flag value. returnValue = 0 Exit Do End If ' Increment counting index value. lookupIndex = lookupIndex + 1 Loop 'Return a 1 when the value is found and zero when it isn't. Finding = returnValue End Function |
I only chose a DO UNTIL
loop because that was in the comment. It would be more appropriate to, from my perspective, use a FOR
range loop, like this:
For lookupIndex = 1 To lookupMax ' Loop contents. Next lookupIndex |
Hope this helps.
Excel: How-to-use SUMIF
Debbie asked a question about why a comparison wasn’t working in the Microsoft Excel SUMIF
function when the initial array value was empty. This post answers that question.
The SUMIF
function takes a lookup range, which anticipates a one column with a lookup value and another column with a value. The SUMIFS
function works along the same line but accepts multiple lookups against a series of lookup list columns.
This shows a simple model of the SUMIF
function:
The formula in cell G3
is:
=SUMIF(B$3:B$5,"<"&$E$3,C$3:C$5) |
If you left off the $ symbol and made the comparison "<"&E3
, you could add the sums for any number of values that are less than a value in the same row (or counting index with named ranges). You could also make the inequality symbol a parameter with this modified formula:
=SUMIF(B$3:B$5,$E$5&$E$3,C$3:C$5) |
Now, your analysis model lets you choose whether you want to look for the sum greater than or less than a specific number in the lookup range. Named ranges compare index values between the lookup range and comparison range values, which allows them to be in different rows or spreadsheets. The only problem is that the label has an inverted inequality sign. You can fix that by constantly editing the description or by simply replace the text of the label with a dynamic text string, like this:
=CONCATENATE("Comparison """,$E$5,""" Value") |
After applying the fix to the label, it looks like:
When the lookup column contains all nulls, the same formula returns a zero, as shown in the illustration.
You'll also get an error flag, unless you unchecked the Flag formulas that refer to empty cells in the error checking options. The IFERROR
function can't be used to suppress this type of error.
Why doesn't the error return a null value? That's because an equality operator compares values in the array against a static or dynamic lookup value. While equality operators don't implicit cast values for comparison, they also don't compare null values. Only the ISBLANK
function lets you find an empty cell and it can't be used inside a SUMIF
function call.
Excel AVERAGEIFS Limit
Somebody asked how to solve a problem with the =AVERAGEIFS
function and non-contiguous ranges. The solution requires a combination that uses a group of =SUMIFS
function divided by an equivalent group of =COUNTIFS
functions.
The following illustration shows the problem. It has groups of quarters, and within the quarter groups rows hold products and columns hold weeks. Unfortunately, the =AVERAGEIFS
function disallows non-continguous source ranges. It does support multiple criteria, which may be non-contiguous ranges provided they match the number of elements in the source range.
While the sums can be calculated by adding the Total column, the average can’t. You would use the following to calculate the actual average:
=IFERROR(((SUMIFS(D5:P5,D5:P5,">0")+SUMIFS(D18:P18,D18:P18,">0")+SUMIFS(D31:P31,D31:P31,">0")+SUMIFS(D44:P44,D44:P44,">0"))/(COUNTIFS(D5:P5,">0") + COUNTIFS(D18:P18,">0") + COUNTIFS(D31:P31,">0") + COUNTIFS(D44:P44,">0"))),0) |
The resolution adds a set of range sums and divides it by an equivalent set of range counts where both values are greater than zero. An =IFERROR
function substitutes a zero in the event that the denominator is zero. This last step ensures that the an empty set of cells won’t raise an exception.
I know it helps the person who asks but hope it helps others too.
Add Image Comment – VBA
Quite some time ago, summer 2008, I wrote a post about how you could embed an image in a cell comment. It was for the then current version of the product – Excel 2007. Here’s a User-Defined Function (UDF) in VBA to perform that trick that works in Excel 2010/2011. A comment on that older post fed my disappointment that Excel 2011 doesn’t even support the navigation but it does perform it with VBA. This includes the UDF to add an image and an ordinary Excel Macro to remove the image when you want to preserve the text.
Let’s assume you have a list of image files in a directory and that you’ve entered their fully qualified or absolute path values column B
of your worksheet. Now you want to load them as comment images in Column A
and insert a value in each column A
cell that describes the comment image.
Unfortunately, as I’ve explained before you can’t assign the image as a property of the cell (or more precisely, I’ve never found a way to do it). If this is wrong, please post the magic here for all to enjoy without a fee or registration. 😉
The following UDF takes a string value to describe the image and a cell reference that holds a string value that holds an absolute file name, which is a logical drive letter (C:\
), a file path, and file name.
Function InsertCommentImage(title As String, cellAddress As Range) Dim commentBox As comment ' Clear any comments before attempting to add them. Application.ActiveCell.ClearComments ' Define the comment as a local variable and assign the file name from the ' cellAddress input parameter to the comment of a cell. Set commentBox = Application.ActiveCell.AddComment With commentBox .Text Text:="" With .Shape .Fill.UserPicture (cellAddress.Value) .ScaleHeight 3#, msoFalse, msoScaleFormTopLeft .ScaleWidth 2.4, msoFalse, msoScaleFromTopLeft End With ' Set the visible to True when you always want the image displayed, and ' to False when you want it displayed only when you click on the cell. .Visible = False End With InsertCommentImage = title End Function |
A fully qualified address for the cellAddress
parameter on a PC would look like this in let’s say cell B1
:
C:\Data\Images\WizardChess.png |
While it would be like this for the cellAddress
parameter on a Mac OS X installation in cell B1
:
Macintosh HD:Users:mclaughlinm:Desktop:WizardChess.png |
You would call this from a cell like this when the text is provided as a string and fully qualified file name is in cell B1
of a worksheet named ImageSource
:
=InsertCommentImage("Wizard Chess",B1) |
Alternatively, you rewrite InsertCommentImage()
as follows, which takes a string for the cell value and a string for the absolute file name:
Function InsertCommentImage(title As String, absoluteFileName As String) Dim commentBox As Comment ' Clear any comments before attempting to add them. Application.ActiveCell.ClearComments ' Define the comment as a local variable and assign the file name from the ' cellAddress input parameter to the comment of a cell. Set commentBox = Application.ActiveCell.AddComment With commentBox .Text Text:="" With .Shape .Fill.UserPicture (absoluteFileName) .ScaleHeight 3#, msoFalse, msoScaleFormTopLeft .ScaleWidth 2.4, msoFalse, msoScaleFromTopLeft End With ' Set the visible to True when you always want the image displayed, and ' to False when you want it displayed only when you click on the cell. .Visible = False End With InsertCommentImage = title End Function |
With the change of the second parameter, you would call the InsertCommentImage()
function with two strings, like:
=InsertCommentImage("Wizard Chess","C:\Data\Images\WizardChess.png") |
Here’s how it would look if you’d put the formula in cell A1
:
This is a simple Excel macro for which you could create a button. You run it when you want to delete only the image comment from a cell. The macro works by highlighting the cell and running it. Naturally, you could wrap it in another Excel macro to navigate through the list and clean up a bunch of comment images at one time.
Sub RemoveComment() Application.ActiveCell.ClearComments End Sub |
If you want to allow the macro to detach all comments for a range, you would rewrite it this way:
Sub RemoveComment() Application.Selection.ClearComments End Sub |
As always, I hope this helps and furthers sharing information.
Excel 2011 Query MySQL
I finally got around to finishing my testing of Excel 2011 querying MySQL. That meant installing and configuring Excel 2011 and MySQL 5.5.9 on Mac OS X (Snow Leopard). While installing Microsoft Office is pretty trivial, installing and configuring MySQL wasn’t. You can read about installing and configuring MySQL here. A quick test after this, led me to discover that you still need a third party ODBC, as covered in this earlier blog. You should take note that Microsoft’s future direction adopts Oracle ODBC, like the approach they’ve chosen with MySQL’s ODBC driver.
I downloaded and installed one of third party ODBC tool sets. I opted for OpenLink Software’s ODBC Driver. Instructions for the install with screen shots are in this blog page. The only downside of this was the discovery that Microsoft’s solution requires Rosetta, like Excel 2008. Rosetta enables Power PC application to run on Intel-based Mac OS X.
Launching the Database icon from Excel 2011, I configured the Data Source Name, which you can find here with screen shots. After you configure the Data Source Name, restarting Excel 2011 is the best choice because otherwise you may see several non-fatal errors.
The following screen shots show you how to establish a connection between Excel 2011 and MySQL, and how to query data from the MySQL database:
- After you click the Database icon, you see the following dialog. Select a Data Source Name and click the OK button to begin a query.
- The OpenLink MySQL Lite Login screen requires the user name and password. Click the Connect button to launch the Microsoft Query, which appears to be a native Power PC application that requires Rosetta to run it
- Microsoft Query appears to be a native Power PC application that requires Rosetta to run it. If you want to enter a query, click the SQL View button.
- Having clicked the SQL View button you now have a work area where you can enter a standard SQL
SELECT
statement, like the one below. Then, you click the Return Data button.
- This dialog lets you select where you want to put the return result set from the query. The default is the absolute cell reference of the top and left most cell,
$A$1
. Click the OK button to query and load the data into the worksheet.
- Now you can see the data in the worksheet. The only problem is the extraneous characters returned into the column headers of the table. While tedious, they’re easy to fix. The following illustrates the downloaded result set from the previous query:
- If you perform a query with a join operation, the column names are never displayed whether you provide aliases to the query or not. It means you have to convert the table to a range, remove the false headers, and recreate the table. This appears to be a limitation of Microsoft Query and unlike the behavior in Excel 2010 on Windows. Perhaps it’s all wrapped up in the emulation provided by Rosetta but I couldn’t find any information about what’s happening. That leaves me with pure speculation, which I never like. If you find the reason, post a comment with a link because everyone would benefit.
As always, I hope this helps those who want to work only in the Mac OS X environment. The risk is Rosetta because it will go away, the only question is when and whether the vendors will fix their dependency first or not. The problem with this solution is that Microsoft Query doesn’t return any tables when it appears that it should.
Query MySQL in Excel 2007
The original blog post with the images was causing a page loading delay, so I’ve moved the post to a page of it’s own. This link send you to the original post with how you setup MySQL Connector/ODBC driver, add a new data source in Windows 7, and a new data import source in Excel 2007. As with other “how-to” posts, it includes screen shots to clear up any ambiguity. I apologize for any inconvenience caused by moving the content.
If you want instructions for using Excel to access an Oracle database, check this older post. I’m working on documentation for a native Mac OS X and Excel 2011 solution and will update this when it’s ready.
As always, I hope this helps those trying to sort through how this works. Naturally, a Visual Basic for Applications (VBA) solution is a better alternative once you’ve set up the data source. This is also on my calendar for documentation.
Excel Data Validation List
A second Excel 2011 question in as many days, February is off and running. The question posed: “How do you create a data validation list without referring to a range of cells?”
You can create a data validation list without referring to a cell range. There’s a small trick to it. You enter the list of values as comma delimited strings, but you don’t enclose them in double quotes. Excel reads a set of literal text strings as if they’re a list of cell values, which is why you don’t provide the double quotes that you use in every function.
The desired list was either TRUE or FALSE, and the required behavior was that they function like Boolean variables. Fortunately, that’s straightforward when you remember that the case insensitive text strings TRUE
and FALSE
are Boolean values.
You create a data validation list by navigating to the Data Tab (Windows Excel 2010 it’s a Ribbon), and choose Validate. You’ll see the following list. Click on Data Validation… in the list, as shown below.
The selection launches the Data Validation dialog box. Choose List from the Allow poplist. Then, enter TRUE,FALSE
in the Source entry field, like this:
Now click OK and you have a poplist. Hope this answer the question for others too.
Excel 1st Day of Next Month
I had an interesting reaction when I told my students they had to create a dynamic Excel model that required them to enter the months of the year without AutoFill feature. They were stumped. They couldn’t find a function to perform it for them.
It’s really quite easy, the EDATE
function lets you find it directly. Enter any day in the current month as the first parameter and one as the number of months to add as the second parameter to the function. Voilà , you have a function to add a month to any date. The only time it returns the first of the next month is when the source date was the first day of the current month.
Here’s the solution when cell B1
contains January 14, 2011 and you want cell C1
calculated as the fourteenth day of February in the same year or February 14, 2011:
=EDATE(B1,1) |
Here’s the solution when cell B1
contains January 14, 2011 and you want cell C1
calculated as the fourteenth day of December in the prior year or December 14, 2010:
=EDATE(B1,-1) |
You can also use the EOMONTH
function to find the first or last day of the month. It lets you find it the first day of the next month regardless of the start date. All you need is a simple trick.
Enter any day in the current month as the first parameter and zero as the number of months to add as the second parameter to the function, and then add one.
Here’s the solution when cell B1
contains any day in January and you want cell C1
calculated as the last day of January in the same year (rounding up).
=EOMONTH(B1,0) |
Here’s the solution when cell B1
contains any day in January and you want cell C1
calculated as the first day of January in the same year (rounding down).
=EOMONTH(B1,-1)+1 |
A subsequent question asked how to calculate the 15th day of the current month, and here’s the formula:
=EOMONTH("10-Jul-2012",-1)+15 |
Hope this helps them and others looking for the trick.
Between Vlookup Key
While the VLOOKUP
and HLOOKUP
functions are powerful tools in Excel, they limit their search to the first column or row of a range. This post shows you how to leverage the COUNTIFS
function to perform an inclusive between search against the first two columns or rows of a range.
Moreover, the VLOOKUP
function searches the first column of a range and looks for an approximate or exact match. The approximate match search throws an exception when the lookup key’s value precedes the first item in the range, and picks up the row immediately less than the lookup key. This type of search requires two condition. The data must be presorted into an ascending order for the sort column, the range must be contiguous, and two matching keys shouldn’t exist. That means that each search column or row cell points to the row of interest for any search key greater than it and less than the next. An exact match search finds the row that matches the lookup key and throws an error when there isn’t an exact match.
Neither of these allow for range searches between non-contiguous sets, like the one below. The date ranges where a value should be found are from the 16th of a month to the end of a month rather than the range between the 16th of one month to the 15th of the next. While this could be done by structuring a row with zeros the gap periods, a more effective solution is possible by using the COUNTIFS
. At least, this is true from Excel 2007 forward.
The solution to this problem starts with recognizing how a COUNTIFS
works. The COUNTIF
provides the opportunity to compare a range of values against a single value, and the COUNTIFS
allows multiple comparisons of values against ranges of values. The COUNTIFS
function returns the number of matches that meet all conditions. Therefore, when a value is found in only one of the ranges the COUNTIFS
function returns a 1, and when a value is found n times it returns n as a number.
The formula in cell F2
checks for the number of times the value in F1
exists:
=COUNTIFS($A$1:$A$13,"<="&F1,$B$1:$B$13,">="&F1) |
If you evaluate when the foregoing function returns 1 before performing a VLOOKUP
function, you can guarantee a match within a non-contiguous range of values. That formula is:
=IF(COUNTIFS($A$1:$A$13,"<="&F1,$B$1:$B$13,">="&F1)=1,VLOOKUP(F1,A2:C13,3),0) |
Hope this helps some folks, as always …