Replace Function is rounding Up value in SQL - sql

I've got a query
select distinct
r.max_range,
convert(float,isnull(replace(r.max_range,0.0,100000000.0),100000000.0)) as max_amt,
convert(float,r.max_range) as 'convert_float',
replace(r.max_range,0.0,100000000.0) as 'replace_question'
from #temp t1
join LTR_Amounts r on (isnull(t1.amt,0) >= r.min_range
and isnull(t1.amt,0) <= convert(float,isnull(replace(r.max_range,0.0,100000000.0),100000000.0)))
where r.category_id = 3
and r.inactive <> 'y'
that produces the following -
I've got an amount 100,000 and it should fall into the
max_range
max_amt
convert_float
replace_question
24999.99
25000
24999.99
25000
49999.99
50000
49999.99
50000
99999.99
100000
99999.99
100000
199999.99
200000
199999.99
200000
This can be run as follows
declare #max_range float = 99999.99
select distinct
#max_range, convert(money,isnull(replace(#max_range,0.0,100000000.0),100000000.0)) as max_amt,
convert(money,#max_range) as 'convert_float',
replace(#max_range,0.0,100000000.0) as 'replace_question'
max_range
max_amt
convert_float
replace_question
99999.99
100000.00
99999.99
100000
The issue at hand is if you can see everywhere as soon as I use replace as part of my query it rounds up the value. If i've got max_range of 99999.99 when I use replace as part of the formula it is looking at it as 1000000 but I need it to keep looking at it as 99999.99
the business rules in place require me to use replace (or some version of that) because sometimes the value is 0.0 and then I need it replaced with some maximum value.
How can I keep my formula as part of my join (with replace)

First off, floats are approximate, i.e. not all values in the data type range can be represented exactly - see here for more info.
Secondly, REPLACE takes character or binary data types as arguments so your query is implicitly converting from an approximate value to a string.
I suggest using an appropriate fixed numeric data type like a decimal and changing out REPLACE for something like a CASE statement.

Related

Convert scientific notation to float in BQ

Is there an easy way to convert a number in scientific notation to float in BigQuery?
For example:
8.32E-4 to 0,08
I know this is an older question, but for anyone else looking for a solution, this query (although a little clunky) should work for all scientific notation fields.
The difficulty is that BigQuery will convert any numbers greater than E±3 to scientific notation. The workaround is to cast the number as a string. If you're using this output for reporting purposes, it shouldn't be a problem. If you need to work with the numbers, make sure you've completed all calculations before converting:
SELECT
number
,base_num
,exponent
,CASE WHEN REGEXP_EXTRACT(number, r'E([+-])') = '+'
THEN REGEXP_EXTRACT(STRING((base_num * (POW(10, exponent)))), r'(\d+\.0)')
ELSE STRING(base_num * (POW(10, (exponent * -1))))
END AS converted_number
FROM
(SELECT
number
,FLOAT(REGEXP_EXTRACT(number, r'(.*)E')) base_num
,INTEGER(REGEXP_EXTRACT(number, r'E[+-](\d+)')) exponent
FROM
(SELECT "8E+1" AS number)
,(SELECT "1.6E-3" AS number)
,(SELECT "8.32E-4" AS number)
,(SELECT "2.92E+9" AS number)
)
Explanation:
FLOAT(REGEXP_EXTRACT(number, r'(.*)E')) returns the number before the E (as a string) and then converts it to a float.
INTEGER(REGEXP_EXTRACT(number, r'E[+-](\d+)')) returns the number after the E (as a string) and then converts it to an integer.
Then, raise 10 to the exponent (or its inverse, if it's negative) and multiply this by the base number.
Finally, cast as a string to maintain non-scientific notation. For numbers greater than 1, "round" to a single 0 after the decimal using regex for easier reading.
Output:
Row number base_num exponent converted_number
1 8E+1 8.0 1 80.0
2 1.6E-3 1.6 3 0.001600
3 8.32E-4 8.32 4 0.000832
4 2.92E+9 2.92 9 2920000000.0
I supposed this is for reporting purposes maybe? Does this solve for you?
SELECT FORMAT("%.10f", 8.32E-8) number UNION ALL
SELECT FORMAT("%.6f", 8.32E-4) UNION ALL
SELECT FORMAT("%4.f", 8.32E+4) UNION ALL
SELECT FORMAT("%10.f", 8.32E+8)

Error converting data type nvarchar to numeric - SQL Server

I am trying to take an average of a column in my database. The column is AMOUNT and it is stored as NVARCHAR(300),null.
When I try to convert it to a numeric value I get the following error:
Msg 8114, Level 16, State 5, Line 1
Error converting datatype NVARCHAR to NUMBER
Here is what I have right now.
SELECT AVG(CAST(Reimbursement AS DECIMAL(18,2)) AS Amount
FROM Database
WHERE ISNUMERIC(Reimbursement) = 1
AND Reimbursement IS NOT NULL
You would think that your code would work. However, SQL Server does not guarantee that the WHERE clause filters the database before the conversion for the SELECT takes place. In my opinion this is a bug. In Microsoft's opinion, this is an optimization feature.
Hence, your WHERE is not guaranteed to work. Even using a CTE doesn't fix the problem.
The best solution is TRY_CONVERT() available in SQL Server 2012+:
SELECT AVG(TRY_CONVERT(DECIMAL(18,2), Reimbursement)) AS Amount
FROM Database
WHERE ISNUMERIC(Reimbursement) = 1 AND Reimbursement IS NOT NULL;
In earlier versions, you can use CASE. The CASE does guarantee the sequential ordering of the clauses, so:
SELECT AVG(CASE WHEN ISNUMERIC(Reimbursement) = 1 AND Reimbursement IS NOT NULL
THEN CONVERT(DECIMAL(18,2), Reimbursement))
END)
FROM Database;
Because AVG() ignores NULL values, the WHERE is not necessary, but you can include it if you like.
Finally, you could simplify your code by using a computed column:
alter database add Reimbursement_Value as
(CASE WHEN ISNUMERIC(Reimbursement) = 1 AND Reimbursement IS NOT NULL
THEN CONVERT(DECIMAL(18,2), Reimbursement))
END);
Then you could write the code as:
select avg(Reimbursement_Value)
from database
where Reimbursement_Value is not null;
Quote from MSDN...
ISNUMERIC returns 1 for some characters that are not numbers, such as plus (+), minus (-), and valid currency symbols such as the dollar sign ($). For a complete list of currency symbols, see money and smallmoney
select isnumeric('+')---1
select isnumeric('$')---1
so try to add to avoid non numeric numbers messing with your ouput..
WHERE Reimbursement NOT LIKE '%[^0-9]%'
If you are on SQLServer 2012,you could try using TRY_Convert which outputs null for conversion failures..
SELECT AVG(try_convert( DECIMAL(18,2),Reimbursement))
from
table
I am guessing that since it is Nvarchar you are going to find some values in there with a '$','.', or a (,). I would run a query likt this:
SELECT Amount
FROM database
WHERE Amount LIKE '%$%' OR
Amount LIKE '%.%' OR
Amount LIKE '%,%'
See what you get and my guess you will get some rows returned and then update those rows and try it again.
Currently your query would pull all numbers that are not all numeric which is a reason why it is failing too. Instead try running this:
SELECT AVG(CAST(Reimbursement AS DECIMAL(18,2)) AS Amount
FROM Database
--Changed ISNUMERIC() = to 0 for true so it will only pull numeric numbers.
WHERE ISNUMERIC(Reimbursement) = 0 and Reimbursement IS NOT NULL

Take thousand value in SQL

I have values like below I need to take only the thousand value in sql.
38,635.123
90,232.89
123,456.47888
I need to take result as below.
635
232
456
SELECT CONVERT(INT,YourColumn) % 1000
FROM dbo.YourTable
Cast it as an int so that we not only drop the decimal places off, but also ensure integer division takes place:
SELECT CAST(YourColumn as int) % 1000
The % operator (modulo) essentially divides the left side by the right side and returns the remainder. So, if we divide 123,456 by 1000, using integer division, the result would be 123 with a remainder of 456. Using the % operator, we just get the 456 part returned.
Another method:
select right(cast(11.500 as int), 3) --> 11
select right(cast(38635.123 as int), 3) --> 635
Assuming your value is numeric, you can use modulo arithmetic:
select cast(col as int) % 1000
(You can also use bigint or a decimal value if an integer is not big enough to hold the value.)
If it is not numeric, then you can use string operations to get the three digits before the period or after the comma (assuming one or the other is always present).
Try this query
SELECT RIGHT(ROUND(REPLACE(YourColumn ,',','') ,0,-1),3)

Search Through All Between Values SQL

I have data following data structure..
_ID _BEGIN _END
7003 99210 99217
7003 10225 10324
7003 111111
I want to look through every _BEGIN and _END and return all rows where the input value is between the range of values including the values themselves (i.e. if 10324 is the input, row 2 would be returned)
I have tried this filter but it does not work..
where #theInput between a._BEGIN and a._END
--THIS WORKS
where convert(char(7),'10400') >= convert(char(7),a._BEGIN)
--BUT ADDING THIS BREAKS AND RETURNS NOTHING
AND convert(char(7),'10400') < convert(char(7),a._END)
Less than < and greater than > operators work on xCHAR data types without any syntactical error, but it may go semantically wrong. Look at examples:
1 - SELECT 'ab' BETWEEN 'aa' AND 'ac' # returns TRUE
2 - SELECT '2' BETWEEN '1' AND '10' # returns FALSE
Character 2 as being stored in a xCHAR type has greater value than 1xxxxx
So you should CAST types here. [Exampled on MySQL - For standard compatibility change UNSIGNED to INTEGER]
WHERE CAST(#theInput as UNSIGNED)
BETWEEN CAST(a._BEGIN as UNSIGNED) AND CAST(a._END as UNSIGNED)
You'd better change the types of columns to avoid ambiguity for later use.
This would be the obvious answer...
SELECT *
FROM <YOUR_TABLE_NAME> a
WHERE #theInput between a._BEGIN and a._END
If the data is string (assuming here as we don't know what DB) You could add this.
Declare #searchArg VARCHAR(30) = CAST(#theInput as VARCHAR(30));
SELECT *
FROM <YOUR_TABLE_NAME> a
WHERE #searchArg between a._BEGIN and a._END
If you care about performance and you've got a lot of data and indexes you won't want to include function calls on the column values.. you could in-line this conversion but this assures that your predicates are Sargable.
SELECT * FROM myTable
WHERE
(CAST(#theInput AS char) >= a._BEGIN AND #theInput < a.END);
I also saw several of the same type of questions:
SQL "between" not inclusive
MySQL "between" clause not inclusive?
When I do queries like this, I usually try one side with the greater/less than on either side and work from there. Maybe that can help. I'm very slow, but I do lots of trial and error.
Or, use Tony's convert.
I supposed you can convert them to anything appropriate for your program, numeric or text.
Also, see here, http://technet.microsoft.com/en-us/library/aa226054%28v=sql.80%29.aspx.
I am not convinced you cannot do your CAST in the SELECT.
Nick, here is a MySQL version from SO, MySQL "between" clause not inclusive?

Use max in a VARCHAR to get result > 999999?

What if somebody made a column as VARCHAR2(256 CHAR) and there are only numbers in this column. I would like to get the highest number. The problem is: the number is something > 999999 but a Max to a varchar is always giving me a max number of 999999
I tried to_number(max(numbers), '9999999999999') but i still get 999999 back, at that cant be. Any ideas? Thank you
the best way is to
First Solution
convert the column in numeric
or
Second Solution
convert data in you query in numeric and than get data...
Example
select max(col1) from(
select to_number(numbers) as col1 from table ) d
It has to be this way because if you call MAX() before TO_NUMBER(), it will sort alphabetically, and then 999999 is bigger than 100000000000. Note that applying TO_NUMBER() to a varchar2 column incurs the risk of an INVALID_NUMBER exception, should the column containing any non-numeric characters. This is why the first proposed solution is to be preferred.
In Oracle, the NUMBER type contains base 100 floating point values which have a precision of 38 significant digits, and a max value of 9999...(38 9's) x 10^125. There are two questions at issue - the first is whether a NUMBER can contain a value converted from a 256 character string, and the second is if two such values which are 'close' in numeric terms can be distinguished.
Let's start with taking a 256 character string and trying to convert it to a number. The obvious thing to do is:
SELECT TO_NUMBER('9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999') AS VAL
FROM DUAL;
Executing the above we get:
ORA-01426: numeric overflow
which, having paid attention earlier, we expected. The largest exponent that a NUMBER can handle is 125 - and here we're trying to convert a value with 256 significant digits. NUMBER's can't handle this. If we cut the number of digits down to 125, as follows:
SELECT TO_NUMBER('99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999') AS VAL
FROM DUAL;
It works fine, and our answer it 1E125.
<blink>
WHOA! WAIT!! WHAT??? The answer is 1 x 10^125??? What about all those 9's?!?!?!?
Remember earlier I'd mentioned that an Oracle NUMBER is a floating point value with a maximum precision of 38 and a maximum exponent of 125. From the point of view of TO_NUMBER 125 9's all strung together can't be exactly represented - too many digits (remember, max. precision of 38 (more on this later)). So it does the absolute best it can - it converts the first 38 digits (all of which are 9's) and then says "How should I best round this off to make the result A) representative of the input and B) as close as I can get to what I was given?". In this case it looks at digit 39, sees that it's a 9, and decides to round upward. As all the other digits are also 9's, it continues rounding neatly until it ends up with 1 as the remaining mantissa digit.
* Later, back at the ranch... *
OK, earlier I'd mentioned that NUMBER has a precision of 38 digits. That's not entirely true - it can actually differentiate between values with up to 40 digits of precision, at least sometimes, if the wind is right, and you're going downhill. Here's an example:
SELECT CASE
WHEN to_number('9999999999999999999999999999999999999999') >
to_number('9999999999999999999999999999999999999998')
THEN 'Greater'
ELSE 'Not greater'
END AS VAL
FROM DUAL;
Those two values each have 40 digits (counting is left as an exercise to the extremely bored reader :-). If you execute the above you'll get back 'Greater', showing that the comparison of two 40 digit values succeeded.
Now for some fun. If you add an additional '9' to each string, making for a 41 digit value, and re-execute the statement it'll return 'Not greater'.
<blink>
WAIT! WHAT?? WHOA!!! Those values are obviously different! Even a TotalFool (tm) can see that!!
The problem here is that a 41 digit number exceeds the precision of the NUMBER type, and thus when TO_NUMBER finds it has a value this long it starts discarding digits on the right side. Thus, even though those two really big numbers are clearly different to you and me, they're not different at all once they've been folded, spindled, mutilated, and converted.
So, what are the takeaways here?
1 - To the OP's original question - you'll have to come up with another way to compare your number strings besides using NUMBER because Oracle's NUMBER type can't hold 256 digit values. I suggest that you normalize the strings by making sure ALL the values are 256 digits long, adding zeroes on the left as needed, and then a string comparison should work OK.
2 - Floating point numbers prove the existence of (your favorite deity/deities here) by negation, as they are clearly the work of (your favorite personification of evil here). Whenever you work with them (as we all have to, sooner or later) you should remember that they are the foul byproducts of malignant evil, waiting to lash out at you when you least expect it.
3 - There is NO point three! (And extra credit for those who can identify without resorting to an extra-cranial search engine where this comes from :-)
Share and enjoy.
If you mean that the numbers in the column can be that big (256 digits), you could try something like this:
SELECT numbers
FROM (
SELECT numbers
FROM table_name
ORDER BY LPAD(numbers, 256) DESC
)
WHERE rownum = 1
or like this:
SELECT LTRIM(MAX(LPAD(numbers, 256))) AS numbers
FROM table_name