I am struggling to find some concrete answers to creating formulas with dynamic references to cells that will not always be in the same position especially using R1C1...
I have a sheet with 4 tables that generate the data for a daily/Hourly heatmap for x devices (Which is a Variable number depending on how many are online)
I have to generate 168 data points for each device (Mon 00:00am, Mon 01:00, Mon 02:00 > Sun 23:00) for 4 metrics (672 per device) which when complete will be compiled into SQL script, Thats all relatively straight forward and is working within other areas of this project using dynamic table positions.
The trouble I am experiencing comes from trying to Create an SQL insert String. The insert string itself is:
="INSERT INTO [dbo].[Heatmap Stats] (Creation_Date, Site_Id, Day, Hour, Humidity, Temperature, PIRCount, Cleaning_Seconds) VALUES ("&TODAY()-2&", "&$A2811&", "&B$1&", "&B$2&", "&B3&", "&B705&", "&B1407&", "&B2109&");"
Realistically, my main interest is having the final 3 references (B705, B1407, B2109) as dynamic in this formula - as these start positions will be relative to the qty of devices it is crunching data for... These only work for the specific data set I am creating this for... I have tried setting this up using a number of variables such as cell, and address, I have tried referencing the cells using number variable and Range(Cells(),Cells())
Set sI = Range("$A3").Cell 'Constant start position, bound to column
Set dI = Range("B$1").Cell 'Constant start position, bound to row
Set hI = Range("B$2").Cell 'Constant start position, bound to row
Set hU = Range("B3").Cell 'Constant start position
'These 3 cells
Set tE = Range(Cells((iR / 2) + 3, 2), Cells((iR / 2) + 3, 2)).Cell
Set fF = Range(Cells(((iR / 3) * 2) + 4, 2), Cells(((iR / 3) * 2) + 4, 2)).Cell
Set cM = Range(Cells(((iR / 4) * 3) + 4.5, 2), Cells(((iR / 4) * 3) + 4.5, 2)).Cell
Range("B2811").Value = "=INSERT INTO [dbo].[Heatmap Stats] (Creation_Date, Site_Id, Day, Hour, Humidity, Temperature, PIRCount, Cleaning_Seconds) VALUES (& TODAY() - 2, " & sI & ", " & dI & ", " & hI & ", " & hU & ", " & tE & ", " & fF & ", " & cM & ");"
I can confirm the variables and their values are generated correctly (and yes the 4.5 is non-conventional but believe it or not it solves a problem when dealing with odd numbers that don't divide well..) - It is always my attempt at a dynamic variable formula that generates a runtime 1004 Error and those variables are used elsewhere
Recording the initial input of the formula returns the following in R1C1:
"=""INSERT INTO [dbo].[Heatmap Stats] (Creation_Date, Site_Id, Day, Hour, Humidity, Temperature, PIRCount, Cleaning_Seconds) VALUES (""&TODAY()-2&"", ""&RC1&"", ""&R1C&"", ""&R2C&"", ""&R[-2808]C&"", ""&R[-2106]C&"", ""&R[-1404]C&"", ""&R[-702]C&"");"""```
Due to the volume of double quotations and nested quotations ("") I found this quite difficult to insert variables into and mostly had it appear in the editor as part of the statement itself, not as variables:(
Can anyone help me to form this SQL statement through a dynamic formula? either R1C1 or .formula?
Related
A few years back I built a interactive heatmap in Excel with Freeforms forming a complete map of the country with all regions.
Every region has a specific KPI percentage, which on it's turn colours the freeform shapes via a VBA macro.
However, I now keep running in a Run-time error '5': Invalid procedure call or argument and I cannot find the bug in the code.
Does anyone see what I don't?
Sub HEATMAP()
For i = 1 To 139
a = Sheet3.Cells((1 + i), 6)
b = Sheet3.Cells((1 + i), 5) * 28 - 28
c = Sheet3.Cells((1 + i), 5) * 28 - 9
d = 89 - Sheet3.Cells((1 + i), 5) * 9 - 9
Sheet2.Shapes.Range(Array("Freeform " & a)).Line.ForeColor.RGB = RGB(80, 80, 80)
Sheet2.Shapes.Range(Array("Freeform " & a)).Fill.ForeColor.RGB = RGB(b, c, d) "<- this line produces the error"
Next i
ActiveWorkbook.RefreshAll
End Sub
Sheet 3 contains a list of all the regions with their freeform-name of each region's object and their KPI percentage, brought back to 10 categories (one for each 10% bracket), which should return a specific color.
This color should be calculated with the values b, c and d in the VBA. (from lowest bracket: yellow to highest bracket: green)
The sheet contains a header.
The 5th column has the colour bracket (numbered 10 - 1, with 10 as the worst bracket [0-10%] and 1 as the best bracket [90-100%])
The 6th column has the freeform number of the objects, creating the map.
There are a total of 139 regions (and therefore freeforms)
Sheet 2 contains the map with a button which triggers this VBA, so the user can "colour the map" with it, based on the KPI values.
What do I keep missing?
Thanks in advance!
As big ben says, what are b, c and d when the error hits?
Might want to wrap it with an
On Error GoTo ErrorHandler1
Sheet2.Shapes.Range(Array("Freeform " & a)).Fill.ForeColor.RGB = RGB(b, c, d) '<- this line produces the error
dummy = 1 'Just a dummy line so you can resume out of the error and see what inputs produced the invalid input val
Exit Sub
ErrorHandler1:
stophere = 1 (and put a breakpoint on this line)
Resume Next
That's a slightly ugly way of getting into the code on the iteration its falling apart and letting you look around for both the invalid value and where its coming from.
I have the following code that runs without issue on some laptops and then others it will error out with a Type Mismatch. In Column AA are Dates with format (mm/dd/yyyy) and Column AB has the respective Times with format (hh:mm:ss).
I am subtracting the date and time from Now(). I am unable to really troubleshoot the issue because it works fine on my laptop. For others, it errors out. CLng(CDate(Now())) has a value but the other two induce type mismatch. I've tried the two lines below and also another which concatenated the date and time, then performed CLng(CDate(.
'If CLng(CDate(Now())) - CLng(CDate(Range("AA" & i).Value)) + CLng(CDate(Range("AB" & i).Value)) >= 7 Then
If CLng(CDate(Now())) - CLng(CDate(FMT(Range("AA" & i), "mm/dd/yyyy"))) + CLng(CDate(FMT(Range("AB" & i), "hh:mm:ss"))) >= 7 Then
where FMT is a public function:
Public Function FMT$(ByVal Value, ByVal strFormat)
FMT = VBA.Format$(Value, strFormat)
End Function
I am requesting my colleague to send me a screenshot of his region date/time settings.. I was thinking this could be the case. I've read other questions similar to this but not able to figure it out. Thanks in advance.
If columns AA and AB contain text rather than dates and times, try using the following statement:
If Now() - (DateValue(Range("AA" & i)) + TimeValue(Range("AB" & i))) >= 7 Then
This will probably still be problematic if the users have a local date setting of, for instance, dd/mm/yyyy but your columns are storing a string representing a date in some other locale's date setting. If that is the case, you may need to parse the fields and do it as follows:
Dim myDateStr As String
Dim myTimeStr As String
Dim myDateTime As Date
myDateStr = Range("AA" & i)
myTimeStr = Range("AB" & i)
myDateTime = DateSerial(CInt(Mid(myDateStr, 7, 4)), CInt(Mid(myDateStr, 1, 2)), CInt(Mid(myDateStr, 4, 2))) + _
TimeSerial(CInt(Mid(myTimeStr, 1, 2)), CInt(Mid(myTimeStr, 4, 2)), CInt(Mid(myTimeStr, 7, 2)))
If Now() - myDateTime >= 7 Then
You can use the Value2 property of the Range object to get the numerical representation of a DateTime. Try:
If CDbl(Now()) - (Range("AA" & i).Value2 + Range("AB" & i).Value2) >= 7 Then
Just be sure to convert Now() to Double, otherwise it will use just the date part of the DateTime.
I find it easier to work with the numerical representation of the DateTime, as it allows you to bypass the regional settings.
Hey I am thinking about a solution for my problem for hours now. I've searched the web, too and was able to find some approaches but none of them does work for my problem.
I have one cell with data that appears like this:
(Monday#100%[00:00-24:00]);(Tuesday#100%[00:00-24:00]);(Wednesday#100%[00:00-24:00]);(Thursday#100%[00:00-24:00]);(Friday#100%[00:00-24:00]);(Saturday#100%[00:00-24:00]);(Sunday#100%[00:00-24:00])
The problem is, that this string is not consistent. It could also look like this:
(Monday#125%[00:00-04:00]);(Monday#90%[04:00-08:00]);(Monday#90%[08:00-12:00]);(Monday#115%[12:00-16:00]);(Monday#120%[16:00-20:00]);(Monday#115%[20:00-24:00]);(Tuesday#125%[00:00-04:00]);(Tuesday#90%[04:00-08:00]);(Tuesday#90%[08:00-12:00]);(Tuesday#115%[12:00-16:00]);(Tuesday#120%[16:00-20:00]);(Tuesday#115%[20:00-24:00]);(Wednesday#125%[00:00-04:00]);(Wednesday#90%[04:00-08:00]);(Wednesday#90%[08:00-12:00]);(Wednesday#115%[12:00-16:00]);(Wednesday#120%[16:00-20:00]);(Wednesday#115%[20:00-24:00]);(Thursday#125%[00:00-04:00]);(Thursday#90%[04:00-08:00]);(Thursday#90%[08:00-12:00]);(Thursday#115%[12:00-16:00]);(Thursday#120%[16:00-20:00]);(Thursday#115%[20:00-24:00]);(Friday#125%[00:00-04:00]);(Friday#90%[04:00-08:00]);(Friday#90%[08:00-12:00]);(Friday#115%[12:00-16:00]);(Friday#120%[16:00-20:00]);(Friday#115%[20:00-24:00]);(Saturday#125%[00:00-04:00]);(Saturday#90%[04:00-08:00]);(Saturday#90%[08:00-12:00]);(Saturday#115%[12:00-16:00]);(Saturday#120%[16:00-20:00]);(Saturday#115%[20:00-24:00]);(Sunday#125%[00:00-04:00]);(Sunday#90%[04:00-08:00]);(Sunday#90%[08:00-12:00]);(Sunday#115%[12:00-16:00]);(Sunday#120%[16:00-20:00]);(Sunday#115%[20:00-24:00])
The structure of the string is
You have a day of the week followed by an "#"
after the at (#) you have a number that can be between 0 and 999
after that you have a time of the day in brackets []
You can have up to 6 different time frames of one day (not more, but less)
each time frame cluster is separated by ; and within normal brackets ()
So this is my starting position. What I want is to extract the numbers between # and % and list them chronologically (Monday to Sunday, hours of the day).
I was able to extract the number between # and % for each day of the week with this formula
=MID(B3;SEARCH("Monday#";B3)+7;SEARCH("%";B3)-SEARCH("Monday#";B3)-7)
But this only works if each day of the week is mentioned only ones.
I am out of ideas of how I could get it to work when having more time frames per day. Appreciate any help.
Thanks,
Ramon
Try an set of iterative searches, where the next search starts at the position found for the previous result. ie:
in cell B6 put =SEARCH("]",$B$3,B5+1)
in cell C6 put =MID($B$3,SEARCH("(",$B$3,B5+1)+1,SEARCH("#",$B$3,B5+1)-SEARCH("(",$B$3,B5+1)-1)
in cell D6 put =MID($B$3,SEARCH("[",$B$3,B5+1)+1,SEARCH("]",$B$3,B5+1)-SEARCH("[",$B$3,B5+1)-1)
in cell E6 put =MID($B$3,SEARCH("#",$B$3,B5+1)+1,SEARCH("%",$B$3,B5+1)-SEARCH("#",$B$3,B5+1)-1)
(Note that B5 needs to be empty (or 0) for this to start correctly)
You can then fill down as far as needed to pick up each term. If they are out of order, then you can order on column D then C (the time then the day).
This might get you started.
Sub Extractor()
Dim data() As String, i As Integer, rw As Integer
rw = 3
data = Split(Range("A1"), ";")
For i = 0 To UBound(data)
Range("A" & rw) = VBA.Mid$(data(i), 2, InStr(1, data(i), "#") - 2) 'Day
Range("B" & rw) = VBA.Mid$(data(i), InStr(1, data(i), "#") + 1, InStr(1, data(i), "%") - InStr(1, data(i), "#") - 1) '% number
Range("C" & rw) = VBA.Mid$(data(i), InStr(1, data(i), "[") + 1, InStr(1, data(i), "]") - InStr(1, data(i), "[") - 1) 'Time
rw = rw + 1
Next i
End Sub
Notes:
Assumes your string is in A1
Prints your data in columns A, B, and C starting in row 3
You can use Text-to-Columns feature of excel, with ) as a delimiter. Than transpose the columns into rows to get something like this:
(Monday#125%[00:00-04:00]
;(Monday#90%[04:00-08:00]
;(Monday#90%[08:00-12:00]
;(Monday#115%[12:00-16:00]
;(Monday#120%[16:00-20:00]
...
;(Sunday#115%[20:00-24:00]
Then apply formulas.
Edit:
Using just Find and Mid you can achieve this:
The idea behind find and mid is always the same.
Ex. for finding ( is =FIND("(";[Text]).
Ex. for extracting Day is =MID([Text];[#[Pos(]]+1;[#[Pos'#]]-[#[Pos(]]-1)
I’m still having a grave problem with some files. It’s a rather stupid problem, but I’ve been working at it for quite some time and can’t find a solution.
I need leading zeroes in the time stamps, at least on the ms level.
The timestamps that my software makes always look like this: (example)
9:55:1:19 (that is 9h, 55min, 1sec, 19 ms)
while what I need would look like
09:55:01:019
It’s no problem to make a conversion in Excel. I use
=VALUE(SUBSTITUTE(G2;":";",";3))
but I always get
09:55:01:190 (190ms!!)
Thus the milliseconds are always read like comma values, which is understandable from the software’s point of view.
I'd like a solution that either appends the correct values to the end of each row in a new column or directly changes the values in the original column (D) to the correct values. (Appending is OK because my other scripts work that way already!)
Can you help out really quickly?
https://www.dropbox.com/sh/3ch6ikddplnyjgg/vUfnVgbbzH here's an example file
With a value in A1 like:
0.413206238
the formula:
=SUBSTITUTE(TEXT(A1,"hh:mm:ss.000"),".",":")
will display:
09:55:01:019
EDIT#1:
Or if you want to convert the values in column D, in place. Select the cells and run this small macro:
Sub FFormat()
Dim r As Range, L As Long, U As Long
For Each r In Selection
ary = Split(r.Text, ":")
U = UBound(ary)
L = LBound(ary)
For i = L To U
If Len(ary(i)) = 1 Then
ary(i) = "0" & ary(i)
End If
Next i
If Len(ary(U)) = 2 Then
ary(U) = "0" & ary(U)
End If
r.Value = Join(ary, ":")
Next r
End Sub
If the original in R12 (my example) is text, you can enter this big formula: :)
=TEXT(LEFT(SUBSTITUTE(R12;":";REPT(" ";20));4);"00") & ":"&TEXT(MID(SUBSTITUTE(R12;":";REPT(" ";20));14;20);"00") & ":" & TEXT(MID(SUBSTITUTE(R12;":";REPT(" ";20));34;20);"00") & ":" & TEXT(MID(SUBSTITUTE(R12;":";REPT(" ";20));54;20);"000")
Depending on your regional settings you may need to replace field separator "; " by ","
Your data in column G has a leading space - this formula in a new column should convert to a valid time value whether you have leading spaces or not
=LEFT(TRIM(G2);FIND("^";SUBSTITUTE(TRIM(G2);":";"^";3))-1)+LOOKUP(1000;RIGHT(G2;{1;2;3})+0)/86400000
format as [h]:mm:ss.000
This will cope with single or double digit milliseconds, assumes that if there are no milliseconds you will still have third : followed by a zero. Also copes with single digit hours, minutes or seconds
I am attempting to format a single number stored as a text value.
For example, I would like to convert:
5145350002005000080
To:
5145-350002-00500-0080
The formula I am using is:
=text(A1,"0000-000000-00000-0000")
The output I am receiving is:
5145-350002-00500-0000
Why are the last 4 characters "0000" instead of "0080" as I would expect? Is there a character limit, or is my formula incorrect?
Quote from Large Number Arithmetic:
The limit in Excel is 15 significant digits in a number. Enter a 16
digit credit card number and 1234567890123456 will become
1234567890123450.
Actually, even 5145350002005001111 will result in 5145-350002-00500-0000.
Moreover, take a look at formula bar when your input cell is selected - for my Excel 2007 I see:
Hope that was helpful)
EDITED:
As a solution to solve the task - keep your numbers formatted as text and use the following formula:
=LEFT(A1,4)&"-"&MID(A1,5,6)&"-"&MID(A1,11,5)&"-"&RIGHT(A1,4)
Here is a custom function. Place it in a regular code module of the workbook and you can call it in the cell by =FormatLargeNumber("A1")
Public Function FormatLargeNumber(val As String)
'This function parses extremely large numbers per your example.
' Modify as needed.
FormatLargeNumber = Left(val, 4) & "-" _
& Mid(val, 5, 6) & "-" & _
Mid(val, 11, 5) & "-" & _
Right(val, 4)
End Function