VARCHAR to DECIMAL

前端 未结 12 621
清歌不尽
清歌不尽 2020-12-06 09:24

I want to convert a varchar(max) column to decimal(10,4).

When I try to use cast or convert I am getting an arit

相关标签:
12条回答
  • 2020-12-06 09:37

    I know this is an old question, but Bill seems to be the only one that has actually "Explained" the issue. Everyone else seems to be coming up with complex solutions to a misuse of a declaration.

    "The two values in your type declaration are precision and scale."

    ...

    "If you specify (10, 4), that means you can only store 6 digits to the left of the decimal, or a max number of 999999.9999. Anything bigger than that will cause an overflow."

    So if you declare DECIMAL(10,4) you can have a total of 10 numbers, with 4 of them coming AFTER the decimal point. so 123456.1234 has 10 digits, 4 after the decimal point. That will fit into the parameters of DECIMAL(10,4). 1234567.1234 will throw an error. there are 11 digits to fit into a 10 digit space, and 4 digits MUST be used AFTER the decimal point. Trimming a digit off the left side of the decimal is not an option. If your 11 characters were 123456.12345, this would not throw an error as trimming(Rounding) from the end of a decimal value is acceptable.

    When declaring decimals, always try to declare the maximum that your column will realistically use and the maximum number of decimal places you want to see. So if your column would only ever show values with a maximum of 1 million and you only care about the first two decimal places, declare as DECIMAL(9,2). This will give you a maximum number of 9,999,999.99 before an error is thrown.

    Understanding the issue before you try to fix it, will ensure you choose the right fix for your situation, and help you to understand the reason why the fix is needed / works.

    Again, i know i'm five years late to the party. However, my two cents on a solution for this, (judging by your comments that the column is already set as DECIMAL(10,4) and cant be changed) Easiest way to do it would be two steps. Check that your decimal is not further than 10 points away, then trim to 10 digits.

    CASE WHEN CHARINDEX('.',CONVERT(VARCHAR(50),[columnName]))>10 THEN 'DealWithIt'
    ELSE LEFT(CONVERT(VARCHAR(50),[columnName]),10) 
    END AS [10PointDecimalString]
    

    The reason i left this as a string is so you can deal with the values that are over 10 digits long on the left of the decimal.

    But its a start.

    0 讨论(0)
  • 2020-12-06 09:40

    You are going to have to truncate the values yourself as strings before you put them into that column.

    Otherwise, if you want more decimal places, you will need to change your declaration of the decimal column.

    0 讨论(0)
  • 2020-12-06 09:42

    Your major problem is not the stuff to the right of the decimal, it is the stuff to the left. The two values in your type declaration are precision and scale.

    From MSDN: "Precision is the number of digits in a number. Scale is the number of digits to the right of the decimal point in a number. For example, the number 123.45 has a precision of 5 and a scale of 2."

    If you specify (10, 4), that means you can only store 6 digits to the left of the decimal, or a max number of 999999.9999. Anything bigger than that will cause an overflow.

    0 讨论(0)
  • 2020-12-06 09:42

    Implemented using Custom Function. This will check whether the string value can be converted to Decimal safely

    CREATE FUNCTION [dbo].[TryParseAsDecimal]
    (
        @Value      NVARCHAR(4000)
        ,@Precision INT
        ,@Scale     INT
    )
    
    RETURNS BIT
    AS
    BEGIN
    
        IF(ISNUMERIC(@Value) =0) BEGIN
            RETURN CAST(0 AS BIT)
        END
        SELECT @Value = REPLACE(@Value,',','') --Removes the comma
    
        --This function validates only the first part eg '1234567.8901111111'
        --It validates only the values before the '.' ie '1234567.'
        DECLARE @Index          INT
        DECLARE @Part1Length    INT 
        DECLARE @Part1          VARCHAR(4000)   
    
        SELECT @Index = CHARINDEX('.', @Value, 0)
        IF (@Index>0) BEGIN
            --If decimal places, extract the left part only and cast it to avoid leading zeros (eg.'0000000001' => '1')
            SELECT @Part1 =LEFT(@Value, @Index-1);
            SELECT @Part1=SUBSTRING(@Part1, PATINDEX('%[^0]%', @Part1+'.'), LEN(@Part1));
            SELECT @Part1Length = LEN(@Part1);
        END
        ELSE BEGIN
            SELECT @Part1 =CAST(@Value AS DECIMAL);
            SELECT @Part1Length= LEN(@Part1)
        END 
    
        IF (@Part1Length > (@Precision-@Scale)) BEGIN
            RETURN CAST(0 AS BIT)
        END
    
        RETURN CAST(1 AS BIT)
    
    END
    
    0 讨论(0)
  • 2020-12-06 09:43

    You still haven't explained why you can't use a Float data type, so here is an example:

    DECLARE @StringVal varchar(50)
    
    SET @StringVal = '123456789.1234567'
    SELECT @StringVal, CAST(@StringVal AS FLOAT)
    
    SET @StringVal = '1.12345678'
    SELECT @StringVal, CAST(@StringVal AS FLOAT)
    
    SET @StringVal = '123456.1234'
    SELECT @StringVal, CAST(@StringVal AS FLOAT)
    
    0 讨论(0)
  • 2020-12-06 09:44

    After testing I found that it was not the decimal place that was causing the problem, it was the precision (10)

    This doesn't work: Arithmetic overflow error converting varchar to data type numeric.

    DECLARE @TestConvert VARCHAR(MAX) = '123456789.12343594'
    
    SELECT CAST(@TestConvert AS DECIMAL(10, 4))
    

    This worked

    DECLARE @TestConvert VARCHAR(MAX) = '123456789.12343594'
    
    SELECT CAST(@TestConvert AS DECIMAL(18, 4))
    
    0 讨论(0)
提交回复
热议问题