How do I split up a year range into individual years in a given field? - sql

I was working in Access to make a query of a few tables, and realized that a column of a table does not meet a specific requirement.
I have a field that consists of thousands of records, and it contains "years" in the following format (an example) : 1915-1918.
What I want to accomplish is to make that value individual in the records. So
the end result would be : 1915,1916,1917,1918.
Basically, I want to convert 1915-1918 to 1915,1916,1917,1918.
I thought a simple concatenation would suffice, but could not wrap my head around how to make it so that it can do it for all thousands of records. I did some research and reached the conclusion that a user defined function might be the way to go. How would I go about this?

When your field value consists of 4 digits followed by a dash followed by 4 more digits, this function returns a comma-separated list of years.
In any other cases (Null, a single year such as "1915" instead of a year range, or anything else), the function returns the starting value.
Public Function YearList(ByVal pInput As Variant) As Variant
Dim astrPieces() As String
Dim i As Long
Dim lngFirst As Long
Dim lngLast As Long
Dim varReturn As Variant
If pInput Like "####-####" Then
astrPieces = Split(pInput, "-")
lngFirst = CLng(astrPieces(0))
lngLast = CLng(astrPieces(1))
For i = lngFirst To lngLast
varReturn = varReturn & "," & CStr(i)
Next
If Len(varReturn) > 0 Then
varReturn = Mid(varReturn, 2)
End If
Else
varReturn = pInput
End If
YearList = varReturn
End Function
However, this approach assumes the start year in each range will be less than the end year. In other words, you would need to invest more effort to make YearList("1915-1912") return a list of years instead of an empty string.
If that function returns what you want, you could use it in a SELECT query.
SELECT years_field, YearList(years_field)
FROM YourTable;
Or if you want to replace the stored values in your years field, you can use the function in an UPDATE query.
UPDATE YourTable
SET years_field = YearList(years_field);

You can use the Split function to return an array from the "years" field that contains the upper and lower year. Then loop from the lower year to the upper year and build the concatenated string. For example:
Public Function SplitYears(Years As String) As String
Dim v As Variant
Dim i As Long
Dim s As String
v = Split(Years, "-", 2)
If UBound(v) = 1 Then
For i = v(0) To v(1)
s = s & "," & CStr(i)
Next i
s = Right(s, Len(s) - 1)
Else
s = v(0)
End If
SplitYears = s
End Function

In Excel, make a sequential reference table of Years, for the range of years that you expect to encounter.
Next, use left and right functions to get the start and end of the range.
Create an update query and update the target field with a concatenation of target field to itself and then also the reference year values between that also fit between the start and end of the range.
Or I guess you could make a user function.

Add the code below to a module in Visual Basic
Public Function CommaDates(Start_End) As String
Dim strt As String
Dim endd As String
Dim x As Long
strt = Left(Start_End, 4)
endd = Right(Start_End, 4)
CommaDates = strt
For x = strt + 1 To endd
CommaDates = CommaDates & "," & x
Next x
End Function
Call this in a query like NEW_DATE: CommaDates([OLD_DATE_FIELD_NAME])

Related

String Value is not passing correctly

I have a word table. I wrote a macro to get values from the table. When it runs I get a runtime error 13. When I debug and watch the value of parsing string it looks like this "2019-04-03 There is only one quote in the string. I think that is the case I couldn't convert that string into a date format. Can you help me to fix this?
The code
Sub Macro2()
Dim NumRows As Integer
Dim startDate As String
Dim days As String
Dim endDate As String
If Not Selection.Information(wdWithInTable) Then
Exit Sub
End If
NumRows = Selection.Tables(1).Rows.Count
'Loop to select each row in the current table
For J = 2 To NumRows
'Loop to select each cell in the current row
startDate = Selection.Tables(1).Rows(J).Cells(5).Range.Text
days = Selection.Tables(1).Rows(J).Cells(6).Range.Text
FormatDate = CDate(ends)
endDate = DateAdd("d", days, FormatDate)
Selection.Tables(1).Rows(J).Cells(7).Range.Text = endDate
Next J
End Sub
The table
Here's the minimal change I found that works for me when tested in Word 2013.
General points:
I added Option Explicit so that the computer would help me find errors. In this case, the variables J and FormatDate were used but not Dimed, and ends was used but never initialized (I changed it to startDate).
The Range.Text in a table cell includes whitespace and the end-of-table marker (¤). That is why CDate was giving an error.
For the dates, I used Left() to take only the left ten characters, since you seem to always be using yyyy-mm-dd-format dates.
For the counts of days, since those can be of any length, I used Range.Words(1).Text to keep only the first Word (as MS Word defines it), which is the number.
I also added the CLng() call in the parameter to DateAdd, since DateAdd wants a number* rather than a string.
For production use, I would also recommend using Selection only in one place, and doing Dim workTable as Table: Set workTable = Selection.Tables(1). That will simplify your code.
Code
<=== marks changed lines
Option Explicit ' <==
Sub Macro2()
Dim NumRows As Integer
Dim startDate As String
Dim days As String
Dim endDate As String
If Not Selection.Information(wdWithInTable) Then
Exit Sub
End If
NumRows = Selection.Tables(1).Rows.Count
'Loop to select each row in the current table
Dim J As Long ' <==
For J = 2 To NumRows
'Loop to select each cell in the current row
startDate = Selection.Tables(1).Rows(J).Cells(5).Range.Text
startDate = Left(startDate, 10) ' <== Remove the space and table mark
days = Selection.Tables(1).Rows(J).Cells(6).Range.Words(1).Text ' <===
Dim FormatDate As Date ' <==
FormatDate = CDate(startDate) ' <== not `ends`
endDate = DateAdd("d", CLng(days), FormatDate) ' <=== clng
Selection.Tables(1).Rows(J).Cells(7).Range.Text = endDate
Next J
End Sub
* DateAdd actually takes a Double, but VBA can promote Long to Double. I chose CLng since it looks like you are only using integer day spans. If not, use CDbl instead.
Try:
Sub Demo()
Dim r As Long
With Selection
If Not .Information(wdWithInTable) Then Exit Sub
With .Tables(1)
For r = 2 To .Rows.Count
.Cell(r, 7).Range.Text = _
Format(DateAdd("d", Split(.Cell(r, 6).Range.Text, vbCr)(0), CDate(Split(.Cell(r, 5).Range.Text, vbCr)(0))), "YYYY-MM-DD")
Next r
End With
End With
End Sub

Excel conversion of text containing ranges--numeric to alpha-numeric

I would like to convert a range of numbers (and single digits) from a number-only format to alpha-numeric format. Entire statement is in a single, excel cell and would like the converted version to be in a neighboring cell.
As an example:
Assuming 1-24=B1-B24
Assuming 25-48=C1-C24
INPUT—
screen 1-3,5,7-9,11-30,32-37,39-40,41,44-46
DESIRED OUTPUT (all acceptable)
screen B1-B3,B5,B7-B9,B11-C6,C8-C13,C15-C16,C17,C20-C22
OR
screen B1-B3,B5,B7-B9,B11-B24,C1-C6,C8-C13,C15-C16,C17,C20-C22
OR
screen B1-B3,B5,B7-B9,B11-B24
screen C1-C6,C8-C13,C15-C16,C17,C20-C22
Using excel functions is proving quite cumbersome so excel macro would be better. I've looked for examples of requested conversion but haven't found anything.
Any help is greatly appreciated.
Cheers,
Bob
Hey here is a solution that i tested out. Not sure if "screen" needs to be in the string or not. Let me know and I will tweak it if that's the case.
Its a user defined function. So drop this vba in a module and then go to a worksheet and type in "=AlphaConvert(" + the cell reference.
Assumption here is that only one cell will be referenced at a time.
Last this could easily be converted to a sub routine and probably run a bit faster than the function.
Public Function AlphaConvert(TargetCell As Range)
Dim v As Long
Dim vArr() As String
Dim i As Long
Dim iArr() As String
Dim a As String
vArr = Split(TargetCell.Value, ",")
For v = LBound(vArr) To UBound(vArr)
If InStr(vArr(v), "-") > 0 Then
iArr = Split(vArr(v), "-")
For i = LBound(iArr) To UBound(iArr)
If i = LBound(iArr) Then
a = AlphaCode(iArr(i))
Else
a = a & "-" & AlphaCode(iArr(i))
End If
Next i
vArr(v) = a
Else
vArr(v) = AlphaCode(vArr(v))
End If
If v = LBound(vArr) Then
AlphaConvert = vArr(v)
Else
AlphaConvert = AlphaConvert & "," & vArr(v)
End If
Next v
End Function
Private Function AlphaCode(Nbr As Variant)
Select Case Nbr
Case 1 To 24
AlphaCode = "B" & Nbr
Case Else
AlphaCode = "C" & Nbr - 24
End Select
End Function

Extracting Date/Time from comment cell

I have a comment field with cells containing text like this:
Cancelled by user at 2018-01-03 03:11:57 without charge
I want to get the date and time information, but it may not always be in the 3rd/4th from last spaces, otherwise I might try to do some sort of complicated split of the cell. Is there an "in cell" way extract the date time information? Or will this need a VBA script? I prefer the former, but I'm trying to make a macro to simplify my life anyway, so VBA would work too.
I'd propose the following formula:
=MID(A1,FIND("at 20",A1)+3,19)
This would require that the date is always preceded by the word 'at' and the date string starts with 20.
You can try this function. It splits the string checking for items that have the first letter numeric, and builds a result string of just the date information.
Public Function ParseForDate(sCell As String) As String
Dim vSplit As Variant
Dim nIndex As Integer
Dim sResult As String
vSplit = Split(sCell, " ")
For nIndex = 0 To UBound(vSplit)
If IsNumeric(Left$(vSplit(nIndex), 1)) Then
sResult = sResult & vSplit(nIndex) & " "
End If
Next
ParseForDate = Trim$(sResult)
End Function
If you wanted to use it in a formula it would look something like this:
=ParseForDate(A1)
To use it in a VBA routine:
Dim s as String
s = ParseForDate(Range("A1"))
Non-VBA solution: (this is assuming the date format is always the same for all cells)
= MAX(IFERROR(DATEVALUE(MID(A1,ROW(INDEX($A:$A,1):INDEX($A:$A,LEN(A1)-19)),20)),0))
+MAX(IFERROR(TIMEVALUE(MID(A1,ROW(INDEX($A:$A,1):INDEX($A:$A,LEN(A1)-19)),20)),0))
Note this is an array formula, so you must press Ctrl+Shift+Enter instead of just Enter when typing this formula.
You will obviously then need to format the cell as a date and time, but this formula gets the numerical value that Excel uses for its internal date and time system.
Using a regex will enable you to fetch the date and time, irrespective of its placement in the string. The following solution will work if the date and time are of the same format as shown in the example string.
Code:
Sub getDateTime()
Dim objReg, matches, str
str = Sheet1.Cells(1, 1).Value 'Change this as per your requirements
Set objReg = CreateObject("vbscript.regexp")
objReg.Global = True
objReg.Pattern = "\d{4}(?:-\d{2}){2}\s*\d{2}(?::\d{2}){2}"
If objReg.test(str) Then
Set matches = objReg.Execute(str)
strResult = matches.Item(0)
MsgBox strResult
End If
End Sub
Click for Regex Demo
Regex Explanation:
\d{4} - matches 4 digits representing the year
(?:-\d{2}){2} - matches - followed by 2 digits. {2} in the end repeats this match 2 times. Once for getting MM and the next time for DD
\s* - matches 0+ whitespaces to match the space between the Date and Time
\d{2} - matches 2 digits representing the HH
(?::\d{2}){2} - matches : followed by 2 digits. The {2} in the end repeats this match 2 times. First time for matching the :MM and the next time for matching the :SS
Screenshots:
Output:
This will be good for about 90 years (using cell C3 for example):
Sub GetDate()
Dim s As String
s = Range("C3").Comment.Text
arr = Split(s, " ")
For i = LBound(arr) To UBound(arr)
If Left(arr(i), 2) = "20" Then
msg = arr(i) & " " & arr(i + 1)
MsgBox msg
Exit Sub
End If
Next i
End Sub

VBA Concatenate Index/Match to Return Multiple Values

The code is a user-defined function that can be used in the worksheet. The function takes three arguments:
=MatchConcat(X1,X2,X3)
X1 = The value you want to match
X2 = The column of values that you want to match against, and
X3 = The column of values that you want to return if there is a match.
It returns a comma-delimited string of the matched values.
Now, my concern is.... I was actually looking for an alternative and much better (performs faster) VBA function same as below. Currently this VBA function below is very helpful with my large set of data. Unfortunately, using this function to a thousands of rows or even hundreds take so long to load. It took 5 to 10 minutes (for a less than 1000 rows). I have already turned off the automatic formula calculation but still having the same issue.
Function MatchConcat(LookupValue, LookupRange As Range, ValueRange As Range)
Dim lookArr()
Dim valArr()
Dim i As Long
lookArr = LookupRange
valArr = ValueRange
For i = 1 To UBound(lookArr)
If Len(lookArr(i, 1)) <> 0 Then
If lookArr(i, 1) = LookupValue Then
MatchConcat = MatchConcat & ", " & valArr(i, 1)
End If
End If
Next
MatchConcat = Mid(MatchConcat, 3, Len(MatchConcat) - 1)
End Function

Count all Comma In Selection or selected text

I want to count all Commas "," that occur only in selected text after that I will use Count as Integer to run the loop
My question is how do i Count , as following Image shows:
I Don't know how to use split and ubound. what is wrong with following code?
Sub CountComma()
Dim x As String, count As Integer, myRange As Range
Set myRange = ActiveDocument.Range(Selection.Range.Start, Selection.Range.End)
x = Split(myRange, ",")
count = UBound(x)
Debug.Print count
End Sub
A simple split will work.
x = Split("XXX,XXX,XXX,XXX,XX,XX", ",")
Count = UBound(x)
Debug.Print Count
B/c the array starts at zero you can take to Ubound number as is.
EDIT:
To use a range .
x = Split(Range("A1").Value, ",")
To break down the code.
Split("A string value","Delimiter to split the string by")
And if you want a single line of code than,
x = UBound(Split(myRange, ","))
your code is wrong in the initial declaration statement of x variable as of string type , since in the subsequent statement
with x = Split(myRange, ",")
you'd want x hold the return value of Split() function which is an array (see here), thus of Variant type
so you have to use
Dim x As Variant
But you can simplify your code as follows
Option Explicit
Sub CountComma()
Dim count As Integer
count = UBound(Split(Selection, ","))
Debug.Print count
End Sub
since:
you don't need any Range type variable to store Selection object into, being Selection the selected range already (see here)
you don't need the x Variant variable neither, feeding UBound()function (which expects an array as its first argument) directly with the Split() function which, as we saw above, returns just an array!
Finally I'd give out an alternative method of counting commas in a range
Sub CountComma()
Dim countAs Integer
count = Len(Selection) - Len(Replace(Selection, ",", ""))
Debug.Print count
End Sub
Thanks to KyloRen and Cindy Meister, Now I can use split and Ubound for Counting , in selection.text.
Following is working Code:
Sub Count_Words()
Dim WrdArray() As String, myRange As String
myRange = ActiveDocument.Range(Selection.Range.Start, Selection.Range.End)
WrdArray() = Split(myRange, ", ")
MsgBox ("Total , in the string : " & UBound(WrdArray()))
End Sub