MS-Access (SQL): Strange behaviour of the MID() function when using decimal arguments - sql

I have noticed strange behavior of the MID() function in MS Access when used in combination with decimal numbers as arguments.
The data is as follows:
table: Test
ID Name Surname
1 Jamal Winstone
2 Joe Roan
3 Jake Tumble
4 Lea More
The SQL statement is:
SELECT MID(Surname, ID, LEN(Name)/2) FROM Test
The results are:
Expr1000
Wi
oa
mb
e
However, shouldn't it be as follows?
MID(Winstone, 1, LEN(Jamal)/2) = MID(Winstone, 1, 5/2) = MID(Winstone, 1, 2.5) = Wi (only 2 characters)
MID(Roan, 2, LEN(Joe)/2) = MID(Roan, 2, 3/2) = MID(Roan, 2, 1.5) = o (only 1 character)
MID(Tumble, 3, LEN(Jake)/2) = MID(Tumble, 3, 4/2) = MID(Tumble, 3, 2) = mb (2 charactes)
MID(More, 4, LEN(Lea)/2) = MID(More, 4, 3/2) = MID(More, 4, 1.5) = e (only 1 character)
This is very strange. Any ideas why this is happening, are the numbers with decimal places rounded?
Thanks

The logic here is very simple:
Mid takes a Long, so needs to cast a float/decimal/currency to a Long first.
Casting to a long uses banker's rounding (to the nearest even) on halves (Clng(1.5) = Clng(2.5) = 2), see the docs.
So these results are entirely expected.
Use Int if you want the integer part (e.g. Int(1.99) = 1)

Related

In TSQL, I am having difficulty getting Decimal(38,15) value to show 15 points after the decimal [duplicate]

Does anyone know why, using SQLServer 2005
SELECT CONVERT(DECIMAL(30,15),146804871.212533)/CONVERT(DECIMAL (38,9),12499999.9999)
gives me 11.74438969709659,
but when I increase the decimal places on the denominator to 15, I get a less accurate answer:
SELECT CONVERT(DECIMAL(30,15),146804871.212533)/CONVERT(DECIMAL (38,15),12499999.9999)
give me 11.74438969
For multiplication we simply add the number of decimal places in each argument together (using pen and paper) to work out output dec places.
But division just blows your head apart. I'm off to lie down now.
In SQL terms though, it's exactly as expected.
--Precision = p1 - s1 + s2 + max(6, s1 + p2 + 1)
--Scale = max(6, s1 + p2 + 1)
--Scale = 15 + 38 + 1 = 54
--Precision = 30 - 15 + 9 + 54 = 72
--Max P = 38, P & S are linked, so (72,54) -> (38,20)
--So, we have 38,20 output (but we don use 20 d.p. for this sum) = 11.74438969709659
SELECT CONVERT(DECIMAL(30,15),146804871.212533)/CONVERT(DECIMAL (38,9),12499999.9999)
--Scale = 15 + 38 + 1 = 54
--Precision = 30 - 15 + 15 + 54 = 84
--Max P = 38, P & S are linked, so (84,54) -> (38,8)
--So, we have 38,8 output = 11.74438969
SELECT CONVERT(DECIMAL(30,15),146804871.212533)/CONVERT(DECIMAL (38,15),12499999.9999)
You can do the same math if follow this rule too, if you treat each number pair as
146804871.212533000000000 and 12499999.999900000
146804871.212533000000000 and 12499999.999900000000000
To put it shortly, use DECIMAL(25,13) and you'll be fine with all calculations - you'll get precision right as declared: 12 digits before decimal dot, and 13 decimal digits after.
Rule is: p+s must equal 38 and you will be on safe side!
Why is this?
Because of very bad implementation of arithmetic in SQL Server!
Until they fix it, follow that rule.
I've noticed that if you cast the dividing value to float, it gives you the correct answer, i.e.:
select 49/30 (result = 1)
would become:
select 49/cast(30 as float) (result = 1.63333333333333)
We were puzzling over the magic transition,
P & S are linked, so:
(72,54) -> (38,29)
(84,54) -> (38,8)
Assuming (38,29) is a typo and should be (38,20), the following is the math:
i. 72 - 38 = 34,
ii. 54 - 34 = 20
i. 84 - 38 = 46,
ii. 54 - 46 = 8
And this is the reasoning:
i. Output precision less max precision is the digits we're going to throw away.
ii. Then output scale less what we're going to throw away gives us... remaining digits in the output scale.
Hope this helps anyone else trying to make sense of this.
Convert the expression not the arguments.
select CONVERT(DECIMAL(38,36),146804871.212533 / 12499999.9999)
Using the following may help:
SELECT COL1 * 1.0 / COL2

How to calculate time complexity of big-theta in fragments of code?

I'm doing exercise on analyzing time complexity on fragments of code, however, I'm having trouble figuring out how the following two code can have different time complexity:
for(a=1,a<=n,a++):
for(b=1,b<=a, b++):
c=c+1
Which the running time of the code can
be expressed as θ(n^2).
Yet,
for(a=1,a<=n,a=2*a):
for(b=1,b<=a, b++):
c=c+1
is expressed as θ(n).
I thought the second fragments of code has the running time of θ(n^2/2)=θ(n^2).
Apparently I was mistaken.
Could some please give some hints of how to properly analyzed the time complexity of the mentioned two codes?
It would help a lot, thanks.
You will see it clearly when unfolding those. Let me try:
First fragment: suppose n = 8
a = 1, b = 1
a = 2, b = 1,2
a = 3, b = 1,2,3
a = 4, b = 1,2,3,4
a = 5, b = 1,2,3,4,5
a = 6, b = 1,2,3,4,5,6
a = 7, b = 1,2,3,4,5,6,7
a = 8, b = 1,2,3,4,5,6,7,8
Second fragment: suppose n = 8
a = 1, b = 1
a = 2, b = 1,2
a = 4, b = 1,2,3,4
a = 8, b = 1,2,3,4,5,6,7,8
By simply counting the number of loops above, you will start to see that a=a*2 has canceled the number of outer loop by the same proportion.
Actually, I believe the answer should be θ(n log(n))
Hope this help.

IF OR Statements which always evaluate to TRUE

Recently I found an error which was due to the way I constructed an IF OR statement.
The original statement:
If a = 1 OR 2 OR 3 Then
`Execute code 1`
`Else if a = 4 OR 5 OR 6 Then`
`Execute code 2`
`Else`
`Do nothing`
The corrected code:
If a = 1 OR a = 2 OR a = 3 Then
`Execute code 1`
`Else if a = 4 OR a = 5 OR a = 6 Then`
`Execute code 2`
`Else`
`Do nothing`
So, the issue was the first part of the IF statement would always evaluate true regardless of the value of a, because it also evaluates the boolean value of the number 2, which is true. So simply spelling out that each number should be compared to the value of a fixes that issue.
My questions are:
1. When using an if statement and you have a comparative operation ( if a =1) followed by "OR 2", in what situation would you actually want to look at the boolean value of the number versus comparing the number against the value of the previously referenced variable?
Is there a way to identify if statements present in the code which will logically ALWAYS be true (and probably have a error)?
Edit: the_lotus pointed out that the boolean value of each number was not being evaluated but bitwise operations were performed between all the values and then a boolean evaluation was performed on the result. In this particular case it may be possible for it to evaluate either true or false depending on the value of a, though I am still interested in identifying if statements which always evaluate true.
As per your Edit, since you want to check if the value of A falls within a range, what you could do is store the ranges as an array and perform IndexOf to see if the value of A is in the collection.
Here is a quick example:
Dim a As Integer = 4
Dim collection1() As Integer = {1, 2, 3}
Dim collection2() As Integer = {4, 5, 6}
If Array.IndexOf(collection1, a) > -1 Then
Console.WriteLine("a = 1, 2, or 3")
ElseIf Array.IndexOf(collection2, a) > -1 Then
Console.WriteLine("a = 4, 5, or 6")
Else
Console.WriteLine("a is not a value between 1-6")
End If
Fiddle: Live Demo

Moving data labels for two consecutive points

I have been trying to find a way to move data labels either above or below points.
Starting at point 1, for every two consecutive points, I'd like to move the data labels above the point, and for the next 2, I'd like to move them below. I'm not sure how to change my for loop or if statement to accommodate this condition. So in the end, the data labels for points 1 and 2 would be positioned above, the data labels for points 3 and 4 would be positioned below, 5 and 6 above...etc
If anyone can help, I'd greatly appreciate it.
Dim FlowIndex As Long
With ActiveChart.SeriesCollection(1)
For FlowIndex = 1 To .Points.Count
With .Points(FlowIndex)
If .HasDataLabel Then
With .DataLabel
.Position = xlLabelPositionAbove
.Orientation = xlHorizontal
End With
End If
End With
Next
End With
This is a mathematical question of how to perform your loop. Think of the values 1,2,3,4,5,6,7,8. What mathematical rule would put 1,2,5,6 in the same group, and 3,4,7,8 in another group?
Possible answer: when divided by 4, 1 & 2 round down to 0. 3 & 4 round up to 1. 7&8 round up to 3. So, one group rounds up to an even number, and one group rounds down to an odd number. There are many ways to reflect this principle, but here is one:
IF FlowIndex MOD 4 = 1 OR FlowIndex MOD 4 = 2 then 'There is a remainder of 1 or 2 when divided by 4 - put label above
'Do stuff
Else 'There is a remainder of 3 or 0 when divided by 4 - put label below
'Do other stuff
End If
To clarify on my answer to the last question you had, MOD is a method of dividing which returns the remainder after doing whole number division. So 5 MOD 3 is 2, 9 MOD 3 is 0, etc.
Alternatively:
bLabelAbove = True
For i = 0 to 20 step 2
For j = 1 to 2
If bLabelAbove Then
Series.DataLabels(i + j).Position = xlLabelPositionAbove
Else
Series.DataLabels(i + j).Position = xlLabelPositionBelow
End If
Next
bLabelAbove = Not bLabelAbove
Next
Divide
1, 2, 3, 4, 5, 6, 7, 8
by 2, and round up, to get:
1, 1, 2, 2, 3, 3, 4, 4
Then you can test whether1, 1, 2, 2, 3, 3, 4, 4 is odd or even. If odd, then label above. If even, then label below.

MySQL String Comparison with Percent Output

I am trying to compare two entries of 6 numbers, each number which can either can be zero or 1 (i.e 100001 or 011101). If 3 out of 6 match, I want the output to be .5. If 2 out of 6 match, i want the output to be .33 etc.
Here are the SQL commands to create the table
CREATE TABLE sim
(sim_key int,
string int);
INSERT INTO sim (sim_key, string)
VALUES (1, 111000);
INSERT INTO sim (sim_key, string)
VALUES (2, 111111);
My desired output to compare the two strings, which share 50% of the characters, and output 50%.
Is it possible to do this sort of comparison in SQL? Thanks in advance
This returns the percentage of equal 1 bits in both strings:
select bit_count(conv(a.string, 2, 10) & conv(b.string, 2, 10))/6*100 as percent_match
from sim a, sim b where
a.sim_key=1 and b.sim_key=2;
As you store your bitfields as base 2 representation converted to numbers, we first need to do conversions: conv(a.string, 2, 10), conv(b.string, 2, 10).
Then we keep only bits that are 1 in each field: conv(a.string, 2, 10) & conv(b.string, 2, 10)
And we count them: bit_count(conv(a.string, 2, 10) & conv(b.string, 2, 10))
And finally we just compute the percentage: bit_count(conv(a.string, 2, 10) & conv(b.string, 2, 10)) / 6 * 100.
The query returns 50 for 111000 and 111111.
Here is an other version that also counts matching zeros:
select bit_count((conv(a.string, 2, 10) & conv(b.string, 2, 10)) | ((0xFFFFFFFF>>(32-6))&~(conv(a.string, 2, 10)|conv(b.string, 2, 10))))/6*100 as percent_match
from sim a, sim b where
a.sim_key=1 and b.sim_key=2;
Note that, while this solution works, you should really store this field like this instead:
INSERT INTO sim (sim_key, string)
VALUES (1, conv("111000", 2, 10));
INSERT INTO sim (sim_key, string)
VALUES (2, conv("111111", 2, 10));
Or to update existing data:
UPDATE sim SET string=conv(string, 10, 2);
Then this query gives the same results (if you updated your data as described above):
select bit_count(a.string & b.string)/6*100 as percent_match
from sim a, sim b where
a.sim_key=1 and b.sim_key=2;
And to count zeros too:
select bit_count((a.string & b.string) | ((0xFFFFFFFF>>(32-6))&~(a.string|b.string)))/6*100 as percent_match
from sim a, sim b where
a.sim_key=1 and b.sim_key=2;
(replace 6s by the size of your bitfields)
Since you are storing them as numbers, you can do this
SELECT BIT_COUNT(s1.string & s2.string) / BIT_COUNT(s1.string | s1.string)
FROM sim s1, sim s2
WHERE s1.sim_key = 1 AND s2.sim_key = 2