Related
Decimal point conversion without rounding to two decimal
My variable is of datatype varchar, so I have to convert it to numeric. But what the thing is my output value is 0.0012499987 and I want the output as 1.24 i.e. without rounding the value.
This is my code
Set #SQLQuery = #SQLQuery + 'CAST((ISNULL(CAST(DI.Coupon AS NUMERIC(18,4)),0) * 100) AS Varchar(50)) AS Coupon,
Here I have to multiply with 100 don't remove that; di.coupon is of type varchar. Keep it in your mind
And the result value also I want as a varchar.
Please someone help me
Sample input / output
0.013923 1.39
You can use CAST
EDIT: I added an ISNULL
DECLARE #N
SET #N = '0.013923'
SELECT CAST(CAST(CAST(ISNULL (#N, 0) AS DECIMAL(38,18)) * 100 AS DECIMAL(18,2)) AS VARCHAR (50))
Probably the easiest is to get the substring of the original column and cast that to numeric. Then it will drop the remaining digits.
In SQL Server, LEFT(column, 4) will do what you want.
But as #HABO pointed out, the in-built function Round() will accept a parameter that truncates the decimal value.
I have got the answer for this. This might help for some people
cast(left(('00122.45678')*100,instr(('00122.45678')* 100,'.','1')+3)as varchar) as stb
Output:
122.456
If you want for 2 decimal without round of then you can add like +2 instead of +3
I need values in my declared variable to contain 3 decimal places to reflect exact (non integer) number of weeks in a month.
However, SET variables return INT values with anything I tried: CONVERT, CAST or various data types.
I paste script below. I could only come up with a workaround: multiply by 1000 in SET only to divide in the select statement.
But I want to avoid calculated ("Calc8d" below) values in SELECT, SETting them as variables instead. So I wonder:
(1) is my 'data type' or 'SELECT' within 'SET' wrong?
(2) Or does a variable never return decimal places but only integers? A straight calculation DECALREd as DECIMAL of 31/7 still returns 4 not 4.428571 or similar. (but DATE and DATETIME are non-integers, or are they?).
It's a shame that months and years are not metric, decimal or even septadecimal so split weeks at beginning or EOM split weeks are always an issue for Monthly like-for -like or other "Monthly" KPI type of reports... >:-P
DECLARE #ExactNoOfWeeks_LastMowCONV DECIMAL SET #ExactNoOfWeeks_LastMowCONV = ROUND(CONVERT(DECIMAL(5,2),DATEPART(d,EOMONTH(GETDATE(), -1)))/7,3)
DECLARE #ExactNoOfWeeks_LastMowCAST DECIMAL SET #ExactNoOfWeeks_LastMowCAST = ROUND(CAST(DATEPART(d,EOMONTH(GETDATE(), -1)) AS FLOAT)/7,3)
DECLARE #ExactNoOfWeeks_LastMonth3K DECIMAL SET #ExactNoOfWeeks_LastMonth3K = ROUND(CONVERT(DECIMAL(5,2),DATEPART(d,EOMONTH(GETDATE(), -1)))/7,3) * 1000
DECLARE #StraightCalc DECIMAL SET #StraightCalc = 31/7
SELECT
DATEPART(d,EOMONTH(GETDATE(), -1)) 'Days Prev Mo' -- number of days in pervious month
,DATEPART(d,EOMONTH(GETDATE(), -1))/7 'Calc8d (w/o CAST)' -- same as above devided by nr of days in a week
,ROUND(CONVERT(DECIMAL(5,2),DATEPART(d,EOMONTH(GETDATE(), -1)))/7,8) 'Calc8d (w/CONVERT)' -- calculated with CONVERT
,ROUND(CAST(DATEPART(d,EOMONTH(GETDATE(), -1)) AS FLOAT)/7,3) 'Calc8d (w/CAST)' -- calculated with CAST
,#ExactNoOfWeeks_LastMowCONV 'Var(w/CONVERT)' -- SELECTed Variable SET with same calculation as above returns an integer
,#ExactNoOfWeeks_LastMowCAST 'Var(w/CAST)' -- idem
,#ExactNoOfWeeks_LastMonth3K/1000 'Var/1K' -- idem
,#StraightCalc 'Var Straight Calc' -- idem
,CONVERT(NVARCHAR,GETDATE(),103) 'Today'
,CONVERT(NVARCHAR,EOMONTH(GETDATE(),-1),103) 'Last EOM'
Days Prev Mo 31
Calc8d (w/o CAST) 4
Calc8d (w/CONVERT) 4.428571
Calc8d (w/CAST) 4.429
Var(w/CONVERT) 4
Var(w/CAST) 4
Var/1K 4.429
Var Straight Calc 4
Today 06/11/15
Last EOM 31/10/15
Result
You need to specify the precision and scale. The default scale is zero.
This works:
DECLARE #ExactNoOfWeeks_LastMowCONV DECIMAL(4,3)
SET #ExactNoOfWeeks_LastMowCONV = 1.145
select #ExactNoOfWeeks_LastMowCONV
Output: 1.145
This doesn't:
DECLARE #ExactNoOfWeeks_LastMowCONV DECIMAL
SET #ExactNoOfWeeks_LastMowCONV = 1.145
select #ExactNoOfWeeks_LastMowCONV
Output: 1
I have to count the digits after the decimal point in a database hosted by a MS Sql Server (2005 or 2008 does not matter), in order to correct some errors made by users.
I have the same problem on an Oracle database, but there things are less complicated.
Bottom line is on Oracle the select is:
select length( substr(to_char(MY_FIELD), instr(to_char(MY_FILED),'.',1,1)+1, length(to_char(MY_FILED)))) as digits_length
from MY_TABLE
where the filed My_filed is float(38).
On Ms Sql server I try to use:
select LEN(SUBSTRING(CAST(MY_FIELD AS VARCHAR), CHARINDEX('.',CAST(MY_FILED AS VARCHAR),1)+1, LEN(CAST(MY_FIELD AS VARCHAR)))) as digits_length
from MY_TABLE
The problem is that on MS Sql Server, when i cast MY_FIELD as varchar the float number is truncated by only 2 decimals and the count of the digits is wrong.
Can someone give me any hints?
Best regards.
SELECT
LEN(CAST(REVERSE(SUBSTRING(STR(MY_FIELD, 13, 11), CHARINDEX('.', STR(MY_FIELD, 13, 11)) + 1, 20)) AS decimal))
from TABLE
I have received from my friend a very simple solution which is just great. So I will post the workaround in order to help others in the same position as me.
First, make function:
create FUNCTION dbo.countDigits(#A float) RETURNS tinyint AS
BEGIN
declare #R tinyint
IF #A IS NULL
RETURN NULL
set #R = 0
while #A - str(#A, 18 + #R, #r) <> 0
begin
SET #R = #R + 1
end
RETURN #R
END
GO
Second:
select MY_FIELD,
dbo.countDigits(MY_FIELD)
from MY_TABLE
Using the function will get you the exact number of digits after the decimal point.
The first thing is to switch to using CONVERT rather than CAST. The difference is, with CONVERT, you can specify a format code. CAST uses whatever the default format code is:
When expression is float or real, style can be one of the values shown in the following table. Other values are processed as 0.
None of the formats are particularly appealing, but I think the best for you to use would be 2. So it would be:
CONVERT(varchar(25),MY_FIELD,2)
This will, unfortunately, give you the value in scientific notation and always with 16 digits e.g. 1.234567890123456e+000. To get the number of "real" digits, you need to split this number apart, work out the number of digits in the decimal portion, and offset it by the number provided in the exponent.
And, of course, insert usual caveats/warnings about trying to talk about digits when dealing with a number which has a defined binary representation. The number of "digits" of a particular float may vary depending on how it was calculated.
I'm not sure about speed. etc or the elegance of this code. it was for some ad-hoc testing to find the first decimal value . but this code could be changed to loop through all the decimals and find the last time a value was greater than zero easily.
DECLARE #NoOfDecimals int = 0
Declare #ROUNDINGPRECISION numeric(32,16) = -.00001000
select #ROUNDINGPRECISION = ABS(#ROUNDINGPRECISION)
select #ROUNDINGPRECISION = #ROUNDINGPRECISION - floor(#ROUNDINGPRECISION)
while #ROUNDINGPRECISION < 1
Begin
select #NoOfDecimals = #NoOfDecimals +1
select #ROUNDINGPRECISION = #ROUNDINGPRECISION * 10
end;
select #NoOfDecimals
I have some dirty input data that is being imported into a raw source table within SQL Server (2008 R2). Fields that are defined as decimal(9,2) or decimal(4,2) by the input provider are coming in as strings, however, the strings do not always conform to the data definition (go figure!).
We import the data from flat files into the raw tables,then apply some conversion scripts to insert the 'cleaned' data into tables with the proper data types assigned to columns.
For instance:
raw_table
TotalAmount varchar(12)
clean_table
TotalAmount decimal(9,2)
Now, my question is this. If I want to do some 'basic' cleanup on this, I would want to do it in a function along the lines of:
CREATE FUNCTION [dbo].[StringToDecimal]
(
#conversionString VARCHAR(12)
)
RETURNS DECIMAL(9,2)
AS
BEGIN
DECLARE #rsp DECIMAL(9,2)
IF ISNUMERIC( LTRIM(RTRIM(REPLACE(#conversionString,' ',''))) ) = 1
BEGIN
SET #rsp = ISNULL( CONVERT( decimal(17,6), NULLIF( LTRIM(RTRIM(REPLACE(#conversionString,' ',''))),'') ), 0 )
END
ELSE
BEGIN
SET #rsp = 0 -- or we can return NULL here
END
RETURN #rsp
END
However, how could one go about supporting various sized decimals in this mix? Is there a way to parametrize the response type? I considered just returning a decimal of the largest size we generally see, then converting it again on the other end, however, you run into arithmetic overflow issues.
Would appreciate any thoughts/insight into solving this one!
Is there a way to parametrize the response type?
It's simpler than you think. Just return as a VARCHAR and do the casting to decimal(x,y) from the VARCHAR. You don't even need to cast - you can directly assign a VARCHAR (as long as it holds valid decimal data) to a decimal column/variable.
I will create 2 functions instead. StringToDecimal2 does the actual conversion, but returns one of 6 "error codes". You can use it to check why a string is invalid. Or use the wrapper dbo.StringToDecimal which just turns the invalid codes into NULL.
CREATE FUNCTION [dbo].[StringToDecimal2]
(
#conversionString VARCHAR(12),
#precision int, -- total digits
#scale int -- after decimal point
)
RETURNS VARCHAR(100)
AS
BEGIN
-- remove spaces, we'll allow this error. no need to trim
set #conversionString = REPLACE(#conversionString,' ','')
-- note: 1,234.56 (thousands separated) will be invalid, so will 1,234,56 (European decimals)
-- well, ok, let's clean up the thousands separators. BUT! It will incorrectly scale European decimals
set #conversionString = REPLACE(#conversionString,',','')
-- we don't support scientific notation either, so 1e4 (10,000) is out
if #conversionString like '%[^0-9.+-]%' return 'INVALID1' -- only digits and decimal are valid (plus +-)
if #conversionString like '%.%.%' return 'INVALID2' -- too many decimals
if #conversionString like '_%[+-]%' return 'INVALID3' -- +- symbol not in the first position
if #conversionString like '[.+-]' return 'INVALID4' -- a single character from "+-."
if #conversionString like '[+-].' return 'INVALID5' -- symbol and decimal only
-- add a decimal place so it is easier to work with below
if #conversionString not like '%.%'
set #conversionString = #conversionString + '.'
-- allow decimal places to go only as far as scale
set #conversionString = left(#conversionString, charindex('.', #conversionString)+#scale)
-- ensure the data is within precision number of digits in total
if charindex('.', #conversionString) > #precision - #scale + 1
return 'INVALID6' -- too many digits before decimal
RETURN #conversionString
END
GO
CREATE FUNCTION [dbo].[StringToDecimal]
(
#conversionString VARCHAR(12),
#precision int, -- total digits
#scale int -- after decimal point
)
RETURNS VARCHAR(100)
AS
BEGIN
RETURN case when [dbo].[StringToDecimal2](#conversionString, #precision, #scale) like 'INVALID%'
then null else [dbo].[StringToDecimal2](#conversionString, #precision, #scale) end
END
GO
Some tests:
select [dbo].[StringToDecimal2]('12342342', 9,2)
select convert(decimal(9,2),[dbo].[StringToDecimal]('1234234', 9,2))
select convert(decimal(9,2),[dbo].[StringToDecimal]('12342342', 9,2))
select convert(decimal(9,2),[dbo].[StringToDecimal]('123423.3333', 9,2))
select convert(decimal(20,10),[dbo].[StringToDecimal]('123423sd.3333', 20,10))
select convert(decimal(20,10),[dbo].[StringToDecimal]('123423sd..3333', 20,10))
select convert(decimal(20,10),[dbo].[StringToDecimal]('-123423.3333', 20,10))
select convert(decimal(20,10),[dbo].[StringToDecimal]('+123423..3333', 20,10))
Thanks for the extra information. It sounds like you have three steps:
Remove all characters from the string that are not digits or a decimal point (do you ever get multiple points in one string?)
Convert to (9,5) or (4,1) as appropriate (how do you decide this? is there rounding? does 10X.781 become 10.78100 or 10.7 or 10.8?)
Insert/update the final value somewhere
Based on point 1 alone, I would immediately avoid TSQL and think about an external script or CLR procedure. A CLR function could do the parsing, but you still have the problem of returning different data types.
Since this appears to be some kind of ETL task, in my environment I would probably implement it as a script component in an SSIS package. The component would do the parsing and send the clean data to different outputs for further processing. If it was a one-time task I would use a Python script to parse the input data and generate INSERT or UPDATE statements.
I don't know if any of those solutions are suitable for you, but maybe it'll give you some ideas. And you should probably avoid the ISNUMERIC() function; search this site or Google to find some of the 'strange' input that it considers to be numeric.
How can I know if a VARCHAR field's value can be successfully converted to an integer?
I want to do it massively to insert records from one table to another...
IsNumeric() function returns 1 for strings (varchars) which can be converted to a number and 0 for those that cannot..
Check out IsNumeric function
One issue whit IsNumeric() function is that You will get True and if number got decimal separator,
What is totally right, But if someone as I need to check straight to numbers in varchar, without decimal symbols, (I got that when I needed to calculate CHECK digit on barcode) You can use castom
made function like
create FUNCTION [dbo].[checkbarkod]
(
#ean_kod varchar(13)
)
RETURNS bit
AS
begin
declare #duzina int
declare #slovo char(1)
declare #pozicija int
declare #uredu bit
set #duzina=len(#ean_kod)
while #duzina>0
begin
set #slovo=(substring(#ean_kod,#duzina,1))
if (#slovo not in('1','2','3','4','5','6','7','8','9','0'))
begin
set #uredu=convert(bit,0)
break
end
else
begin
set #uredu=convert(bit,1)
set #duzina=#duzina-1
end
end
RETURN #uredu
end