How to subtract from each number in a comma delimited string? - sql

I have a table with a string field where than can be one number or mulitple numbers (delimited by a comma). I need to find the difference between the values (when converted to an integer) and an unspecified value. For simplicity sake for this question, I'll just say the value to be compared is a static value of 10.
Example Table:
iId vchStringNumbers vchSubtractedStringNumbers
1 20, 30, 40
2 50
3 20
Desired Results:
iId vchStringNumbers vchSubtractedStringNumbers
1 20, 30, 40 10, 20, 30
2 50 40
3 20 10
Is there a way to accomplish this in SQL? If it would be eaiser in excel or something like that, feel free to answer as well.

If one has access to Excel 2019 or Excel O365, one could also use (in B2):
=TEXTJOIN(", ",,FILTERXML("<t><s>"&SUBSTITUTE(B2,",","</s><s>")&"</s></t>","//s")-10)

Place you CS data in an Excel column, Select the cells and run this tiny VBA macro:
Sub SubtractCSV()
Dim r As Range
For Each r In Selection
ary = Split(r.Value, ",")
For i = LBound(ary) To UBound(ary)
ary(i) = CLng(Trim(ary(i))) - 10
Next i
r.Offset(0, 1).Value = Join(ary, ",")
Next r
End Sub

Related

Vba range pairwise division

I have a set of variable ranges and I want to do pairwise division of each of the members of each range and bold the member or two highest members if there is a difference of 3/1
Example 1
Column A. Ferrets. Rabbits. Foxes.
Column B. 3000 1000 4000
Because Ferrets and Foxes are in greater than 3/1 proportion to rabbits bold them
Example 2
Column A. Ferrets. Rabbits. Foxes.
Column B 1000 1000 4000
Because foxes are in greater than 3/1 proportion to ferrets and rabbits bold them
Example 3
Column A. Ferrets. Rabbits. Foxes.
Column B. 3000 3500 4000
Bold nothing
I can get the logic to bold but im not quite sure where to begin to get excel to loop through all the possible divisions. Note the range will vary with the number of rows. Sometimes up to 5 and 6 or maybe more which is why im having the problem.
I only have a do loop to count the number of cells in the range. This gives the number of columns that is contained in the range. What I do not know how to do is then take and store a double of whatever division may come from all the pairwise divisions. So if it were 2 rows there would be two answers, 3 rows and there would be 6, 4 and there would be 12...i think the function will be n(n-1) now that i think about it.
Option Explicit
Sub Bold()
Dim i, j As Integer
i = 2 'Row
j = 3 'Column
Do
If Cells(i,j).Value <>"" Then
i = i + 1
End If
Loop Until Cells(i, j).Value = ""
i = i - 1
I figure it would be some sort of nested loop but I'm not quite sure where to start with the function on the number of rows.

Sum values based on cells with an indent

I'm having a certain layout like this:
0
0
0 5
1 6
1
0 7
1 8
1
0
0 9
1 10
1
0 11
1 12
Above is a list with combinations from 000, 001, 010, 011 to 111. The combinations all have a certain value, from 5 to 12.
(The enters are new rows, the spaces are indentlevels of cells, the 5, 6, 7, etc. are in a new column. The 0's and 1's are all in the same column.)
Now I need to have the sum of all the values in which the first 0/1 is 1, the sum of all the values in which the second 0/1 is 1, and the same for the last value. The results in this case must be: 42, 38 and 36
I can't find out how to programm this properly. I was hoping for something like this:
While not Sheets("Sheet 1").Cells(j, 1).indentlevel(2).Value = 0
sum = sum + cells(j,2)
j = j + 1
Wend
But obviously this doesn't work. I can't program this all out without loops, because the codes can be up to 5 didgets (ex. 01101)
Have you thought of using a for loop? Also I think you are using the IndentLevel syntax incorrectly.
With ThisWorkbook.Sheets("Sheet 1")
set rng = Range(.cells(1,1), .cells(3,10)) ' Change this to match your range
End With
For each c in rng
If c.IndentLevel = 2 Then
sum = sum + c
End If
Next c
This doesn't need VBA. The real problem is an inappropriate data format. Update your convention such that each indentation level is moved to a separate column. You'll then be able to proceed with simple Excel formulas.

Compare and count consecutive data in Excel

I am trying to make an Excel macro to compare and count data from the same column.
Specifically, I want to count only if the a cell and the cell below the cell both have an absolute value less than 100.
End goal: Say the column below was the data that I have,
241.197
96.747
88.325
156
53.666
55.372
-45.667
-207.152
I want the macro to return a value of 2.
It would need to count
[96.747
88.325]
&
[53.666
55.372
-45.667]
Say you have column A with the data starting at A1
sub countTotal
dim i as integer
dim sum as double
dim count as integer
for i = 1 to Cells(Rows.Count, "A").End(xlUp).row
sum = Abs(Range("A" & i).Value) + Abs(Range("A" & i + 1).Value)
if sum < 100 then count = count + 1
next
msgbox count
end sub
Not tested. You should see if it counts the last value in your column + the empty one after, if it does, shorten the loop by 1.
Sub countTotal()
Dim i As Integer
Dim sum As Double
Dim count As Integer
For i = 1 To Cells(Rows.count, "A").End(xlUp).Row
If Abs(Range("A" & i).Value) < 100 And Abs(Range("A" & i + 1).Value) < 100 Then
count = count + 1
Else
count = count
End If
Next
MsgBox count
End Sub
Based on your code, this is what I came up with. It performs fine if the data looks like this:
120
70
-90
110
80
60
In which case the macro returns a value of 2.
The problem is that some of my data looks like this:
100
-80
90
70
-180
-190
Macro returns a value of 2 because it counts [-80 90] and [90 70]. But I want to make it so that when the absolute value of a consecutive set of data is all less than 100, macro counts it as 1. So ideally instead of returning 2 as the value, the macro should return 1 in this case.
Can you direct me in the right direction?
Thanks!

Copy and rearrange specific data that contains cells with letters and numbers

I have an excel sheet with data arranged randomly with numbers and letters throughout Columns B and C. I also have an input cell located in cell "O7" and an output cell located in "P7" where a user can input any value (1,1a, 2,2b, etc...) and the code will use these values to find and copy the value in A
My code (below) runs through and finds the value in B based on the value in "O7" and copies the corresponding value in column A (2 times) for that row to "Sheet4." It then looks to the next cell in column C and uses that value to find the next row in Column B with that value. Then it copies that row's value in Column A to Sheet 4 under the previous one.
My problem is that my code can't distinguish between "1" and a value with a letter like "1a" for either the input or output value. It just sees that there is a "1" and copies the value in A. I believe I may not be setting my NewStart variable as the next value to look up correctly or my function may be missing something to distinguish between "1" and "1a"?
Sub NewerFind()
Dim Startval As Long
Dim Endval As Long
Dim LastRow As Long
Dim Rng As range
Dim Output As Long
Dim NewStart As Long
Dim Val As Long
Dim Valnew As Long
LastRow = Sheets("Sheet3").range("B" & Rows.Count).End(xlUp).Row
Startval = GetSingleFromString(Sheets("Sheet3").Cells(7, "O").Value)
Output = 2
NewStart = Startval
For X = 7 To LastRow
Val = GetSingleFromString(Sheets("Sheet3").Cells(X, 2).Value) 'if i set the variables equal to a string, do i need the GetSingleFromString function?
Valnew = GetSingleFromString(Sheets("Sheet3").Cells(X, 3).Value)
Endval = GetSingleFromString(Worksheets("Sheet3").Cells(7, "P").Value)
If Val = Endval Then
Exit Sub
ElseIf Val = NewStart Then
Sheets("Sheet4").Cells(Output, 1).Value = _
Sheets("Sheet3").Cells(X, 1).Value
Output = Output + 1
Sheets("Sheet4").Cells(Output, 1).Value = _
Sheets("Sheet3").Cells(X, 1).Value
Output = Output + 1
NewStart = Valnew
End If
'need line of code to set NewStart as the value of the cell to the right...???????
Next X
End Sub
This is the function I'm using to enable the code to include cells with letters. Without it, it skips cells with letters:
Private Function GetSingleFromString(ByVal InString As String) As Single
If Len(InString) <= -1 Then
GetSingleFromString = -1
Exit Function
End If
Dim X As Long
Dim Temp1 As String
Dim Output As String
For X = 1 To Len(InString)
Temp1 = Mid(InString, X, 1)
If IsNumeric(Temp1) Or Temp1 = "." Then Output = Output & Temp1
Next
If Len(Output) > 0 Then
GetSingleFromString = CSng(Output)
Else
GetSingleFromString = -1
End If
End Function
This is my excel Sheet:
A B C D E
1 1 1a 78.15 77.68 (Row 7)
2 1a 2 77.18 76.92
3 2 3 76.92 76.63
4 3 4 76.13 75.78
5 4 4a 75.78 75.21
6 4a 5 75.11 74.87
7 5 5a 74.87 74.69
8 5a 6 73.94 73.6
9 6 6a 73.1 72.71
10 6a 6b 72.41 72.18
11 6b 10 72.18 71.6
12 10 11 71.3 70.89
13 11 12 70.89 69.83
14 12 13 69.83 68.68
15 13 14 68.68 67.68
16 14 15 67.63 66.46
17 15 16 66.01 64.84
18 16 16a 64.24 63.72
19 16a 16b 56.82 56.37
20 16b 16c 56.37 55.18
21 16c OUT 47.28 47.27
22 7 7a 83.12 76.07
23 7a 8 76.17 75.99
24 8 9 74.79 74.41
25 9 6 74.51 74 (Row 31)
Thank you in advance for the help.
It's not clear what you're trying to do; all I can see is that you want to find cells with a matching value, which may be the entire value, or just the numeric part of a value.
What I do know is that Range.Find isn't as useful as you think: it sometimes makes a string comparison if either the search value or the seek range is a string, and sometimes decides it's 'no match' if tries the numeric value 1 against the string "1", depending on the formatting in the cells.
It'll definitely treat "1" versus "1a" as 'no match', if you've set LookAt:=xlWhole, because you've specified that the whole of the value, each and every character, must match the search string exactly.
That's fine, most of the time, if you wanted to treat "1" versus "1a" as 'no match', and only match "1"to "1" and "1a to "1a".
If you wanted to treat "1" versus "1a" as a 'find', you have two choices:
Use the partial match option in Range.Find by setting
LookAt:=xlPart, and hope that it doesn't get confused by formatting. And most of the time, it doesn't get confused; so you're probably fine.
Search through the cells in each range with VBA, using 'Like', which allows you to specify partial matches, single digits, and match-from-the-list for specific characters like [1234567890]
Just to get you started with 'Like':
"1" = "1a" returns False
"1" LIKE "1a" returns False
"1" LIKE "1*" returns True, although this isn't a very useful thing to know
"1a" LIKE "#*" returns True, and this looks a bit more relevant because you've identified a value that starts with a number, and you can test that at the top of an If... Then block containing further logic to make the match you actually want.
Full documentation for the 'Like' operator is here on MSDN:
https://msdn.microsoft.com/en-us/library/office/gg251796.aspx
Developers using other languages will use Regular Expressions for this kind of thing: but you'd need to import an external Regex library for that, as it isn't native to Excel and VBA.
Also: a string can never have a LEN of less than zero. Your test If Len(InString) <= -1 Then needed to be for zero, or less than 1.

VBA - Select columns using numbers?

I'm looking for an alternative to this code, but using numbers.
I want to select 5 columns, the start column is a variable, and then it selects 5 columns from this.
Columns("A:E").Select
How do I use integers instead, to reference columns? Something like below?
For n = 1 to 5
Columns("n : n + 4") .select
do sth
next n
You can use resize like this:
For n = 1 To 5
Columns(n).Resize(, 5).Select
'~~> rest of your code
Next
In any Range Manipulation that you do, always keep at the back of your mind Resize and Offset property.
Columns("A:E").Select
Can be directly replaced by
Columns(1).Resize(, 5).EntireColumn.Select
Where 1 can be replaced by a variable
n = 5
Columns(n).Resize(, n+4).EntireColumn.Select
In my opinion you are best dealing with a block of columns rather than looping through columns n to n + 4 as it is more efficient.
In addition, using select will slow your code down. So instead of selecting your columns and then performing an action on the selection try instead to perform the action directly. Below is an example to change the colour of columns A-E to yellow.
Columns(1).Resize(, 5).EntireColumn.Interior.Color = 65535
you can use range with cells to get the effect you want (but it would be better not to use select if you don't have to)
For n = 1 to 5
range(cells(1,n).entirecolumn,cells(1,n+4).entirecolumn).Select
do sth
next n
Try using the following, where n is your variable and x is your offset (4 in this case):
LEFT(ADDRESS(1,n+x,4),1)
This will return the letter of that column (so for n=1 and x=4, it'll return A+4 = E). You can then use INDIRECT() to reference this, as so:
COLUMNS(INDIRECT(LEFT(ADDRESS(1,n,4),1)&":"&LEFT(ADDRESS(1,n+x,4),1)))
which with n=1, x=4 becomes:
COLUMNS(INDIRECT("A"&":"&"E"))
and so:
COLUMNS(A:E)
In the example code below I use variables just to show how the command could be used for other situations.
FirstCol = 1
LastCol = FirstCol + 5
Range(Columns(FirstCol), Columns(LastCol)).Select
no need for loops or such.. try this..
dim startColumnas integer
dim endColumn as integer
startColumn = 7
endColumn = 24
Range(Cells(, startColumn), Cells(, endColumn)).ColumnWidth = 3.8 ' <~~ whatever width you want to set..*
You can specify addresses as "R1C2" instead of "B2". Under File -> Options -> Formuals -> Workingg with Formulas there is a toggle R1C1 reference style. which can be set, as illustrated below.
I was looking for a similar thing.
My problem was to find the last column based on row 5 and then select 3 columns before including the last column.
Dim lColumn As Long
lColumn = ActiveSheet.Cells(5,Columns.Count).End(xlToLeft).Column
MsgBox ("The last used column is: " & lColumn)
Range(Columns(lColumn - 3), Columns(lColumn)).Select
Message box is optional as it is more of a control check. If you want to select the columns after the last column then you simply reverse the range selection
Dim lColumn As Long
lColumn = ActiveSheet.Cells(5,Columns.Count).End(xlToLeft).Column
MsgBox ("The last used column is: " & lColumn)
Range(Columns(lColumn), Columns(lColumn + 3)).Select
In this way, you can start to select data even behind column "Z" and select a lot of columns.
Sub SelectColumNums()
Dim xCol1 As Integer, xNumOfCols as integer
xCol1 = 26
xNumOfCols = 17
Range(Columns(xCol1), Columns(xCol1 + xNumOfCols)).Select
End Sub