parsing the first 2 parts of a string - ms-access

I have a field called 'Specimen' that has entries formatted like 'CM-Z-01', 'TOR-XY-03', etc. I want to populate 2 additional fields in the same table, called 'TestType' and 'Axis', with the first two sections of the 'Specimen' entry, respectively. So 'TestType' would have 'CM' and 'TOR'; Axis would have 'Z' and 'XY'
I can do the first one just fine, with
UPDATE MechanicalData
SET MechanicalData.TestType = Left(Specimen,InStr(Specimen,"-")-1);
Is there an easy way to grab the middle portion?

You can use the Split function which returns an array with the string parts:
firstPart = Split("TOR-XY-03", "-")(0)
secondPart = Split("TOR-XY-03", "-")(1)
Make a function that you can call in your query based on this function. You cannot use Split directly as it is not supported in SQL.
Public Function SpecimenPart(ByVal s As Variant, ByVal partNo As Long) As Variant
If Nz(s) <> "" Then
Dim parts As Variant
parts = Split(s, "-")
If partNo - 1 <= UBound(parts) Then
SpecimenPart = parts(partNo - 1)
End If
End If
End Function
The parameter partNo is one-based (1 for first part).
UPDATE MechanicalData SET
TestType = SpecimenPart(Specimen, 1),
Axis = SpecimenPart(Specimen, 2);

Related

Is there any way to get alphabetical numbering of rows in ssrs instead of numerical numbering?

What I need (alphabetical numbering of rows-highlighted in bold(serial column)):
I have tried converting the output of rownumber function into string, But nothing seems to work as I don't have any idea.
Please help!
You can do this with a bit of custom code.
Go to the Report Properties, click the "Code" tab and paste the following code into the custom code window.
Public Function GetRowLetter(RowNum As Integer) As String
' stick the RowNum in a variable that we can reduce until it's zero
Dim r As Integer
Dim i As Integer
Dim s As String ' holds result
s = ""
r = RowNum
' we start at the right side so if the rownum is 28 we want to be back AB
' need to get 'B' first
Do While RowNum > 0
r = Int((RowNum - 1) / 26)
i = (RowNum - 1) Mod 26
s = Chr(i + 65) & s
RowNum = r
Loop
GetRowLetter = s
End Function
This will give "A" for 1, "B" for 2 etc, then it will give "AA" for 27, "AB" or 28 etc...
If you want to return lower case letters instead, swap the 65 for 98
In your report set the textbox value expression to
=Code.GetRowLetter(RowNumber("myDataSetName"))
swap out myDataSetName with the name of your dataset or scope you want to apply it to. Remember the dataset and scope names are case sensitive and must be surrounded by quotes ( " )

How to sort values in a Join function with LookUpSet in SSRS?

How can I sort the values returned from a LookUpSet function inside a Join function?
Example data:
TransNo MasterTran Item Category ModifierLevel
1001000 1001000 ItemA CategoryB 0
1002000 1001000 ItemB CategoryC 1
1003000 1001000 ItemC CategoryC 1
End result I'd like to get is "CategoryB ItemB ItemC". When I use the following combination of Join and LookUpSet, I end up getting "ItemB CategoryB ItemC".
=Join(LookUpSet(Fields!MasterTransNo.Value, Fields!MasterTransNo.Value, Iif(Fields!ModifierLevel.Value > 0, Trim(Fields!ItemDescription.Value), Trim(Fields!CategoryDescription.Value)), "LineItemDetails"), " ")
This is an expression on a cell in a table. The Row Group is set to Group on TransNo, sort by TransNo. I've tried a variety of different approach to sorting for the group, but always get the same result.
Any ideas on how I can force the order of data from LookUpSet so that it's joined in the order I want?
I ended up figuring this out by seeing other questions looking to pull distinct values only from the Join(LookUpSet()) functions and modifying it. This code is based off the useful answers from this other SO question.
Go to Report Properties
Enter the Code editor and past the following function into the Custom Code box:
Public Function JoinSortAlpha(arr As Object(), delimiter As String) As String
System.Array.Sort(arr)
Dim result As String = String.Empty
For i As Integer = 0 To arr.Length - 1
If Not arr(i) Is Nothing And arr(i) <> String.Empty Then
If result = String.Empty Then
result = arr(i)
Else
result = result + delimiter + arr(i)
End If
End If
Next
Return result End Function
Go to your expression and replace the Join() function with your new function by calling JoinSortAlpha(). My new expression looks like this:
=JoinSortAlpha(LookUpSet(Fields!MasterTransNo.Value, Fields!MasterTransNo.Value, Iif(Fields!ModifierLevel.Value > 0, Trim(Fields!ItemDescription.Value), Trim(Fields!CategoryDescription.Value)), "LineItemDetails"), " ")
Here's a breakdown of what the function is doing:
Create a new function called JoinSortAlpha which will have values passed into it from the expression. In this case, the values from the LookUpSet() function.
Sort the array passed from the function's argument. It's this sort that will make it alphabetical by default.
Create a String object called result to pass the final values to.
Evaluate the array arr and write a value to the result string for each value contained in the arr array. Values in an array are given a numeric value starting at 0 and increasing by 1. Here, we're telling the array to continue populating the result string from the first value in the array (at 0) until the last value in the array which is determined by the length of the array minus 1 (because the array starts at 0 rather than 1).
If your LookUpSet() function doesn't return any values, SSRS will show an error if we don't account for that in this JoinSortAlpha function. To deal with any potential blanks being returned, we're using an If statement to determine if the string is empty in which case it just returns nothing. Otherwise, it will return the value plus the delimiter from the end of the function (a space " " in my case).

How to implement a more efficient search feature?

In my database there are 3 column which is Name, Age, Gender.
In the program, I only want to use 1 search button. When the button is clicked, the program determine which 3 of the textbox has input and search for the right data.
How do you work with the query? For example if Name and Gender has text, the query :
"Select * from table Where (Name = #name) AND (Gender = #gender)"
And when only name is entered, I only query for the name. Must I check textbox by textbox whether there is user input and then write multiple query for each of them? Or is there a better way to do this?
Edit (29/5/16) : I tried doing this another way like this
myCommand = New MySqlCommand("Select * from project_record Where
(FloatNo = #floatNo OR FloatNo = 'None') AND
(DevCompanyName = #devCompanyName OR DevCompanyName = 'None') AND
(DevType = #devType OR DevType = 'None') AND
(LotPt = #lotPt OR LotPt = 'None') AND
(Mukim = #mukim OR Mukim = 'None') AND
(Daerah = #daerah OR Daerah = 'None') AND
(Negeri = #negeri OR Negeri = 'None') AND
(TempReference = #tempRef OR TempReference = 'None')", sqlConn)
But as you can guess already it will not work efficiently as well because if I only enter input for DevType and leave other textboxes blank, the query will not pull up all the records for DevType only. It will just display as no records.
Select * from table
Where (Name = #name OR #name is Null)
AND (Gender = #gender OR #gender is Null)
...
it should be one query
Other answers have explained how to simplify the query. It is especially important to get rid of the ORs, since they inhibit any use of indexes.
Once you have the query build cleanly, you need to think about the dataset and decide which columns are usually used for filtering. Then make a few INDEXes for them. You won't be able to provide 'all' possible indexes, hence my admonition that you think about the dataset.
When building indexes, you can have single-column or multiple-column indexes. For your type of data, I would suggest starting with several 2-column indexes. Make sure each index starts with a different column.
For Where (Name = #name) AND (Gender = #gender), here are some notes:
INDEX(gender) is useless because of low 'cardinality';
INDEX(gender, name) might be useful, but the following would be better:
INDEX(name)
Things like name and DevCompanyName are virtually unique, so a 1-column index is probably good.
If you had gender and age, then INDEX(age, gender) might be useful.
MySQL will almost never use two indexes for a single SELECT.
By the way, the construction of the WHERE could be done in a Stored Procedure. You would need CONCAT, PREPARE, etc.
Original answer
(scroll down to see update)
Can you try the following:
build a list only including values of the textboxes that have an input
set a string of the join the items of that list together with the " AND " string
append that string to your standard SELECT statement
The code looks like this:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim Predicate1 As String = Me.TextBox1.Text
Dim Predicate2 As String = Me.TextBox2.Text
Dim Predicate3 As String = Me.TextBox3.Text
Dim PredicateList As New List(Of String)
Dim WhereClause As String
Dim Query As String
If Predicate1 <> String.Empty Then
PredicateList.Add("Name=""" & Predicate1 & """")
End If
If Predicate2 <> String.Empty Then
PredicateList.Add("Age=""" & Predicate2 & """")
End If
If Predicate3 <> String.Empty Then
PredicateList.Add("Gender=""" & Predicate3 & """")
End If
WhereClause = String.Join(" AND ", PredicateList.ToArray)
Query = "SELECT * FROM TABLE WHERE " & WhereClause
MessageBox.Show(Query)
End Sub
Update
Further to the comments re SQL injection, here is an updated sample.
Dim Command As SqlClient.SqlCommand
Dim Predicate1 As String = Me.TextBox1.Text
Dim Predicate2 As String = Me.TextBox2.Text
Dim Predicate3 As String = Me.TextBox2.Text
Dim ParameterList As New List(Of SqlClient.SqlParameter)
Dim PredicateList As New List(Of String)
Dim BaseQuery As String = "SELECT * FROM TABLE WHERE "
If Predicate1 <> String.Empty Then
PredicateList.Add("name = #name")
ParameterList.Add(New SqlClient.SqlParameter("#name", Predicate1))
End If
If Predicate2 <> String.Empty Then
PredicateList.Add("age = #age")
ParameterList.Add(New SqlClient.SqlParameter("#age", Predicate2))
End If
If Predicate3 <> String.Empty Then
PredicateList.Add("gender = #gender")
ParameterList.Add(New SqlClient.SqlParameter("#gender", Predicate3))
End If
Command = New SqlClient.SqlCommand(BaseQuery & String.Join(" AND ", PredicateList.ToArray))
Command.Parameters.AddRange(ParameterList.ToArray)
COALESCE is your friend here. You can use it to make the where clause ignore comparisons where the parameter is NULL.
Select * from table Where (Name = COALESCE(#name,table.Name))
AND (Gender = COALESCE(#gender,table.Gender))
So, if the #name parameter is NULL, COALESCE(#name,table.Name) will return the value of the 'Name' column of the current row and (Name = COALESCE(#name,table.Name)) will always be true.
This assumes that if no value is entered in a textbox the corresponding parameter will be NULL. If instead it is a value such as 'None', you can use the NULLIF function to map 'None' to NULL
Select * from table Where
(Name = COALESCE( NULLIF( #name, 'None'), table.Name))
AND (Gender = COALESCE( NULLIF( #gender, 'None'), table.Gender))
How to implement a more efficient search?
The answer partly depends on what your definition of efficient is. I suspect you mean less code and fewer if blocks etc. But fundamentally, running a new SELECT * query to apply a filter is inefficient because your base data set can be all the rows and you just fiddle with the users View of it.
I have a DB with random data in columns for Fish, Color (string), Bird, Group (int) and Active which should be similar enough for Name, Age and Gender in the question - or that other long thing at the bottom.
DataTable
Fill a datatable and bind it to a DGV:
' form level object
Private dtSample As DataTable
...
' elsewhere
Dim sql = "SELECT Id, Name, Descr, `Group`, Fish, Bird, Color, Active FROM Sample"
Using dbcon As MySqlConnection = New MySqlConnection(MySQLConnStr)
' create SELECT command with the Query and conn
Dim cmd As New MySqlCommand(sql, dbcon)
...
daSample.Fill(dtSample)
daSample.FillSchema(dtSimple, SchemaType.Source)
End Using
dgv2.DataSource = dtSample
Going forward, we can filter the user's view of that table without issuing a new query.
Filter Controls
If some of the fields are limited to certain selections, for instance Gender, you can use a ComboBox instead of a TextBox. This is to help the user succeed and avoid typos (Make or Mael instead of Male; or here, correctly spelling Baracuda I mean Baraccuda, er Barracuda correctly.
For illustration purposes, Fish is something where the user can type in anything at all, but Bird is constrained to a set of choices. If there is a Bird table, cboBird can be bound or populated from it. But you may also be able to populate it from the master/base table:
Dim birds = dtSample.AsEnumerable.Where(Function(d) d.IsNull(5) = False).
Select(Function(d) d.Field(Of String)("Bird")).
Distinct.
ToArray()
cboBird.Items.AddRange(birds)
If "Finch" is a legal choice but there are none in the database, it wont show in the list. Depending on the app, this can be a Good Thing:
If the user filters on Finch and there a no resulting records, you won't need a MessageBox or StatusBar message explaining the empty result set.
If something is not in the list, you are signalling up front that there are none of those. It then becomes a matter of training why a known element isnt in the list.
On the other hand, you'd have to repopulate those filter controls each time before they are used in case new records were added recently. If the controls are on a Dialog or different TabPage, this is easy to do as needed.
It isnt always applicable, but it can help the user avoid typos.
It depends on the app whether either method is of value.
DBNull / 'none'
I am not sure why you are adding 'none' to each clause. If someone want to see all the 'John` or all the 'Cod' records, it doesn't seem like they would also be interested in 'none'. Personally, Null/DBNull seems a better way to handle this, but it is easy to add or not add either form.
It would seem more valuable to filter to just those with DBNull/None. The code above for the Bird List filters out DBNull and I would do so for none as well. Then, before the result is added to the ComboBox, add a `None' item first so it is at the top.
Again it depends on what the app does; Or = 'None', may make perfect sense in this case.
Filter
Using a TextBox for Fish and Group, a ComboBox for Bird and Color and a CheckBox for Active, the code can form the filter thusly:
Dim filterTerms As New List(Of String)
Dim filterFmt = "{0} = '{1}' "
' OR:
' Dim filterFmt = "{0} = '{1}' OR {0} Is Null"
' OR:
' Dim filterFmt = "{0} = '{1}' OR {0} = 'none'"
If String.IsNullOrEmpty(tbSearchFish.Text) = False Then
Dim txt = tbSearchFish.Text.Replace("'", "''")
filterTerms.Add(String.Format(filterFmt, "Fish", txt))
End If
If cboBird.SelectedIndex > -1 Then
filterTerms.Add(String.Format(filterFmt, "Bird", cboBird.SelectedItem.ToString))
End If
If String.IsNullOrEmpty(tbGroup.Text) = False Then
Dim n As Int32
If Int32.TryParse(tbGroup.Text, n) Then
filterTerms.Add(String.Format(filterFmt, "[Group]", n))
End If
End If
If cboColor.SelectedIndex > -1 Then
filterTerms.Add(String.Format(filterFmt, "Color", cboColor.SelectedItem.ToString))
End If
If chkActive.Checked Then
' NOTE: I do not have TreatTinyAsBoolean turned on
' for some reason
filterTerms.Add(String.Format(filterFmt, "Active", "1"))
End If
If filterTerms.Count > 0 Then
Dim filter = String.Join(" AND ", filterTerms)
dtSample.DefaultView.RowFilter = filter
Dim rows = dtSample.DefaultView.Count
End If
Use whichever filterFmt is appropriate for what the app needs to do
A filter term is only added to the list if the related control has a value (as per above, this could include a 'None').
For the TextBox, it escapes any embedded ticks such as might be found in names like O'Malley or D'Artgnan. It replaces one tick with two.
Since Group is a numeric, a valid Int32 input is tested
If there are elements in the filterTerms list, a filter string is created
The filter is applied to the DefaultView.Filter (you can use also use a DataView or a BindingSource) so that the code need not query the database to provide filter capabilities.
Rows will tell you how many rows are in the current View.
The only halfway tricky one is a Boolean like Gender or Active because those actually resolve to three choices: {Any/Either, A, B}. For that, I would use a ComboBox and ignore it for SelectedIndex 0 as well. I didn't bother with this because the Combo concept is amply covered. Result:
Is it More "Efficient"?
It still depends.
It doesn't re-query the database to get rows the app can already have.
No new DBConnection, DBCommand or other DBProvider objects are created, just a list.
No need to dynamically create a SQL statement with N parameters in a loop to avoid SQL injection/special words and chars.
It doesn't even query the database for the items for the filter terms. If there is a static list of them in the DB, they could be loaded once, the first time they use the filters.
It is easy to remove the filter, no need to query yet again without WHERE clauses.
A ComboBox where applicable helps the user find what they want and avoid typos.
Is the SQL "cleaner". more "efficient? The code doesn't really mess with new SQL, just some WHERE clauses.
Is there less code? I have no idea since we just see the result. It doesnt string me as a lot of code to do what it does.
In my database there are 3 column which is Name, Age, Gender. In the program, I only want to use 1 search button. When the button is clicked, the program determine which 3 of the textbox has input and search for the right data.
And when only name is entered, I only query for the name. Must I check textbox by textbox whether there is user input and then write multiple query for each of them? Or is there a better way to do this?
SELECT * FROM `table`
WHERE (`name` = #name AND `name` IS NOT NULL)
OR (`age` = #age AND (`age`>0 OR `age` IS NOT NULL))
OR (`gender` = #gender AND `gender` IS NOT NULL);
With the above query if all text boxes have value, the result will not be one record (as if you where using logical AND between fields). If you want only that record you will filter it server-side with php from the rest of the results.
You can check the results on your own in this Fiddle
EDIT
In order to solve the above inconvenience (not bringing easily single results when needed) i got a little help from this answer and re-wrote the above query as:
SELECT *, IF(`name`=#name, 10, 0) + IF(`age`=#age, 10, 0) + IF(`gender`=#gender, 10, 0) AS `weight`
FROM `table`
WHERE (`name` = #name AND `name` IS NOT NULL)
OR (`age` = #age AND (`age`>0 OR `age` IS NOT NULL))
OR (`gender` = #gender AND `gender` IS NOT NULL)
HAVING `weight`=30;
OR to still get all records with a weight on result
SELECT *, IF(`name`=#name, 10, 0) + IF(`age`=#age, 10, 0) + IF(`gender`=#gender, 10, 0) AS `weight`
FROM `table` WHERE (`name` = #name AND `name` IS NOT NULL)
OR (`age` = #age AND (`age`>0 OR `age` IS NOT NULL))
OR (`gender` = #gender AND `gender` IS NOT NULL)
ORDER BY `weight` DESC;
You were pretty close. Let's look at
(FloatNo = #floatNo OR FloatNo = 'None')
So you want the field either to be the given input or 'None'? But there are (supposedly) no records in your table with FloatNo 'None'. What you really want to do is find out whether the input is none (i.e. empty):
(FloatNo = #floatNo OR #floatNo = '')
And for the case the user types in a blank by mistake, you can ignore this, too:
(FloatNo = #floatNo OR TRIM(#floatNo) = '')
The whole thing:
myCommand = New MySqlCommand(
"Select * from project_record Where
(FloatNo = #floatNo OR TRIM(#floatNo) = '') AND
(DevCompanyName = #devCompanyName OR TRIM(#devCompanyName) = '') AND
(DevType = #devType OR TRIM(#devType) = '') AND
(LotPt = #lotPt OR TRIM(#lotPt) = '') AND
(Mukim = #mukim OR TRIM(#mukim) = '') AND
(Daerah = #daerah OR TRIM(#daerah) = '') AND
(Negeri = #negeri OR TRIM(#negeri) = '') AND
(TempReference = #tempRef OR TRIM(#tempRef) = '')", sqlConn)
What is wrong with your approach?
Just change
(FloatNo = #floatNo OR FloatNo = 'None')
to
(FloatNo = #floatNo OR FloatNo = '' or FloatNo IS NULL)
And do that for every criteria.
Your query will respect empty values and NULL values after that.

Value used in formula of wrong data type

I have been trying to figure this error out for the past few days with no luck. I am hoping one of you would be able to help. I am getting "value used in formula of wrong data type.
Quick explanation:
convert functions like this one to its corresponding text (20054/18393)*100.0
the 5 digit numbers are Field IDs that refer to questions.
ID Question
20054 How many days of year do you work
18393 How many days of vacation do you get a year
The result I am trying to get to is (How many days of year do you work / How many days of vacation do you get a year) *100.0
It could be easily done manually if it was just a hand full. I have over 2600 formulas that need to be converted.
I created this function below which is resulting in the error mentioned in the title. Any assistance would be greatly appreciated
Here is my function
Function Test(sInput As String) As String
Dim i As Long
Dim num As String
Dim Text, a, str, shortname As String
For i = 1 To Len(sInput)
a = Mid(sInput, i, 1)
If IsNumeric(a) Then
num = num & a
Text = ""
Else
If a = "." Then
num = num & a
Else
'search for num value in second sheet short name
shortname = WorksheetFunction.VLookup(WorksheetFunction.Int(num), Worksheets("questionlist").Range("A3:F2537"), 5, False)
num = ""
End If
Text = shortname & a
shortname = ""
End If
str = str & Text
Next
Test = str
End Function
The error is raised because you are passing blank value to INT Function in the line
WorksheetFunction.VLookup(WorksheetFunction.Int(num), Worksheets("questionlist").Range("A3:F2537"), 5, False)
To reproduce the error Type =INT("") in any cell
To fix this handle blank values
Updated Answer:
Function Formula2Text(ByRef myCell As Range) As String
Dim QuestionId As Integer
Dim strInput As String
'Get Formula instead of values
strInput = myCell.FormulaR1C1
'Use Regex to Catch all ID's
Set Regex = CreateObject("VBScript.RegExp")
Set rnglookup = Worksheets("questionlist").Range("A3:F2537")
Regex.Global = True
Regex.Pattern = "\d+"
For Each Match In Regex.Execute(strInput)
'Skip if the ID is 100
If (Match.Value <> 100) Then QuestionId = Match.Value
'Lookup ID in the rnglookup,Make sure the Ids are sorted in asc in the questionlist sheet
Qntxt = Application.VLookup(QuestionId, rnglookup, 5, False)
If IsError(Qntxt) Then Qntxt = "Missing Lookup"
'Replace the ID with the lookup
strInput = Replace(strInput, QuestionId, Qntxt)
Next
Formula2Text = strInput
End Function
Usage:In the cell next to the formula use the function by referencing the formula
=Formula2Text(A1)

Excel Macro to concatenate multiple rows from Column B per ID rows in Column A with newlines

This problem is in an Excel .xls file.
Simplest Use Case:
Column A has one row.
Column B has 5 rows.
The 5 rows in Column B need to be merged into one row, delimited by newlines.
I have a huge .xls document where there are a ton of IDs in column A.
There are on average anywhere from 3 to 10 rows that belong to each column A row.
How to know which Column B rows belong to which Column A?
By the positioning of the cells.
One Column A row may have 5 Column B rows to the right of it.
I don't have any VBA experience.
I have looked around for macros and functions but haven't had any luck finding anything that matches this problem.
Edit:
I am now trying to figure out how to get the script to ignore rows that have a one-to-one mapping between column A and column B.
Edit again - 06-20-2012:
Now that I can attach images, here is a screenshot of an image for what I'm trying to get.
The rows for Brian and Mark should be ignored, while Scott and Tim get their values copied over.
Edit:
Unmerging column A, using the code that Andy supplied, and then using this VB script afterwards does the trick:
Sub mergeA()
For i = 2 To Cells(65535, 1).End(xlUp).Row
If IsEmpty(Cells(i, 1)) Then Range(Cells(i - 1, 1), Cells(i, 1)).Merge
Next
End Sub
That VB script puts the cells in column A back together
I didn't make the script, it came from this web page:
http://www.vbforums.com/showthread.php?t=601304
This will transform the data shown on the left to the output on the right:
Option Explicit
Sub Make_Severely_Denormalized()
Const HEADER_ROWS As Long = 1
Const OUTPUT_TO_COLUMN As Long = 3
Const DELIMITER As String = vbNewLine
Dim A_Range As Range
Dim B_Range As Range
Dim A_temp As Range
Dim B_temp As Range
Dim B_Cell As Range
Dim Concat As String
On Error GoTo Whoops
Set A_Range = Range("A1").Offset(HEADER_ROWS)
Do While Not A_Range Is Nothing
Set B_Range = A_Range.Offset(0, 1)
' some helper ranges
If A_Range.Offset(1, 0).Value = "" Then
Set A_temp = Range(A_Range, A_Range.End(xlDown).Offset(-1, 0))
Else
Set A_temp = A_Range.Offset(1, 0)
End If
Set B_temp = Range(B_Range, B_Range.End(xlDown)).Offset(0, -1)
' determine how high "B" is WRT no change in "A"
Set B_Range = Range(B_Range, B_Range.Resize( _
Application.Intersect(A_temp, B_temp, ActiveSheet.UsedRange).Count))
' loop through "B" and build up the string
Concat = ""
For Each B_Cell In B_Range
Concat = Concat & B_Cell.Value & DELIMITER
Next
Concat = Left(Concat, Len(Concat) - Len(DELIMITER))
' do the needful
A_Range.Offset(0, OUTPUT_TO_COLUMN - 1).Value = Concat
' find the next change in "A"
If A_Range.Offset(1, 0).Value = "" Then
Set A_Range = Application.Intersect(A_Range.End(xlDown), ActiveSheet.UsedRange)
Else
Set A_Range = A_Range.Offset(1, 0)
End If
Loop
Exit Sub
Whoops:
MsgBox (Err & " " & Error)
Stop
Resume Next
End Sub