37

varchar(max)列をに変換したいdecimal(10,4)

使用しようとしたとき、castまたはconvert算術オーバーフローの例外が発生したとき。問題は、varchar列に格納されているデータに異なる精度と異なるスケールが含まれている可能性があることです。たとえば、123456789.1234567'、1.12345678、または123456.1234です。

123456.1234のような値の場合は問題なく変換されますが、他の値の場合は問題が発生します。

4

12 に答える 12

30

テストの結果、問題の原因は小数点以下ではなく、精度(10)であることがわかりました。

これは機能しません:varcharをデータ型numericに変換する算術オーバーフローエラー。

DECLARE @TestConvert VARCHAR(MAX) = '123456789.12343594'

SELECT CAST(@TestConvert AS DECIMAL(10, 4))

これはうまくいきました

DECLARE @TestConvert VARCHAR(MAX) = '123456789.12343594'

SELECT CAST(@TestConvert AS DECIMAL(13, 4))

9 int + 4 float =13charsのようになります

于 2012-06-18T21:55:36.103 に答える
7

私の説明はコードにあります。:)

DECLARE @TestConvert VARCHAR(MAX) = '123456789.1234567'
BEGIN TRY
    SELECT CAST(@TestConvert AS DECIMAL(10, 4))
END TRY
BEGIN CATCH
    SELECT 'The reason you get the message "' + ERROR_MESSAGE() + '" is because DECIMAL(10, 4) only allows for 4 numbers after the decimal.'
END CATCH

-- Here's one way to truncate the string to a castable value.
SELECT CAST(LEFT(@TestConvert, (CHARINDEX('.', @TestConvert, 1) + 4)) AS DECIMAL(14, 4))

-- If you noticed, I changed it to DECIMAL(14, 4) instead of DECIMAL(10, 4) That's because this number has 14 digits, as proven below.
-- Read this for a better explanation as to what precision, scale and length mean: http://msdn.microsoft.com/en-us/library/ms190476(v=sql.105).aspx
SELECT LEN(LEFT(@TestConvert, (CHARINDEX('.', @TestConvert, 1) + 4)))
于 2012-06-18T21:33:43.113 に答える
6

私は次の解決策を思いついた:

SELECT [Str], DecimalParsed = CASE 
WHEN ISNUMERIC([Str]) = 1 AND CHARINDEX('.', [Str])=0 AND LEN(REPLACE(REPLACE([Str], '-', ''), '+', '')) < 29 THEN CONVERT(decimal(38,10), [Str])
WHEN ISNUMERIC([Str]) = 1 AND (CHARINDEX('.', [Str])!=0 AND CHARINDEX('.', REPLACE(REPLACE([Str], '-', ''), '+', ''))<=29) THEN 
    CONVERT(decimal(38,10), 
            CASE WHEN LEN([Str]) - LEN(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE([Str], '0', ''), '1', ''), '2', ''), '3', ''), '4', ''), '5', ''), '6', ''), '7', ''), '8', ''), '9', '')) <= 38 
                 THEN [Str] 
                 ELSE SUBSTRING([Str], 1, 38 + LEN(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE([Str], '0', ''), '1', ''), '2', ''), '3', ''), '4', ''), '5', ''), '6', ''), '7', ''), '8', ''), '9', ''))) END)
ELSE NULL END
FROM TestStrToDecimal

私はそれがやり過ぎのように見えることを知っています、そしておそらくそれはそうです、しかしそれは私のために働きます(異なる精度とスケールの正、負、大小の両方の数をチェックしました-すべてがdecimal(38,10)またはに変換されますNULL)。

入力はハードコーディングされてdecimal(38,10)いるため、別の精度が必要な場合は、コードの定数(38、10、29)を変更してください。

使い方?結果は次のとおりです。

  • 変換がオーバーフローや精度の低下がなく単純な場合(123や123.456など)、変換するだけです。
  • 数値が大きすぎず、小数点以下の桁数が多すぎる場合(123.1234567890123456789012345678901234567890など)、最後の超過桁をトリミングして、最初の38桁のみを保持します。
  • 数値が大きすぎてオーバーフローなしで10進数に変換できない場合(例:9876543210987654321098765432109876543210)、NULLが返されます

上記のコードでは、それぞれのケースが個別のWHENステートメントです。

変換の例をいくつか示します。 ここに画像の説明を入力してください

于 2014-04-28T17:20:25.910 に答える
5

Floatデータ型を使用できない理由をまだ説明していないので、次に例を示します。

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)
于 2012-06-18T19:44:50.687 に答える
5

6.999,50が有効な小数ではないという事実を見逃しています。確かに10進数にコンマと小数点を入れることはできませんか?何番になるの?

ロケールがを指定していると仮定します。グループ化として、および小数点として:グループ化された数字を削除するには:

SELECT CONVERT(decimal(11,2)、REPLACE( '6.999,50'、'。'、''))

小数として6999,50を生成します

于 2013-08-25T05:24:57.327 に答える
4

値をその列に配置する前に、値を文字列として自分で切り捨てる必要があります。

それ以外の場合、小数点以下の桁数を増やしたい場合は、小数点以下の列の宣言を変更する必要があります。

于 2012-06-18T18:52:39.527 に答える
4

あなたの主な問題は、小数点の右側のものではなく、左側のものです。タイプ宣言の2つの値は、精度とスケールです。

MSDNから:「精度は数値の桁数です。スケールは数値の小数点の右側の桁数です。たとえば、数値123.45の精度は5、スケールは2です。」

(10、4)を指定すると、小数点以下6桁、または最大数999999.9999しか格納できないことを意味します。それよりも大きいものはオーバーフローを引き起こします。

于 2012-06-18T21:53:34.660 に答える
3

カスタム関数を使用して実装されます。これにより、文字列値を安全にDecimalに変換できるかどうかがチェックされます

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
于 2012-08-02T09:23:39.393 に答える
2

これは古い質問ですが、実際に問題を「説明」したのはビルだけのようです。他の誰もが宣言の誤用に対する複雑な解決策を考え出しているようです。

「型宣言の2つの値は、精度とスケールです。」

..。

「(10、4)を指定すると、小数点以下6桁、または最大数999999.9999しか格納できないことを意味します。それより大きい値を指定すると、オーバーフローが発生します。」

したがって、宣言するDECIMAL(10,4)と、合計10個の数値を使用でき、そのうち4個は小数点以下になります。したがって、123456.1234の桁数は10桁で、小数点以下4桁です。これは、のパラメータに適合しますDECIMAL(10,4)。1234567.1234はエラーをスローします。10桁のスペースに収まる11桁があり、小数点以下4桁を使用する必要があります。小数点の左側から数字をトリミングすることはオプションではありません。11文字が123456.12345の場合、10進値の末尾からのトリミング(丸め)は許容されるため、これはエラーをスローしません。

小数点以下を宣言するときは、常に、列が実際に使用する最大値と、表示する小数点以下の桁数の最大数を宣言するようにしてください。したがって、列に最大100万の値しか表示されず、小数点以下2桁のみを気にする場合は、として宣言しDECIMAL(9,2)ます。これにより、エラーがスローされるまでの最大数は9,999,999.99になります。

問題を修正する前に問題を理解することで、状況に合った正しい修正を選択できるようになり、修正が必要/機能する理由を理解するのに役立ちます。

繰り返しになりますが、私はパーティーに5年遅れていることを知っています。DECIMAL(10,4)ただし、これに対する解決策についての私の2セント(列がすでに設定されており、変更できないというコメントから判断すると)最も簡単な方法は2つのステップです。小数点以下が10ポイント以内であることを確認してから、10桁にトリミングします。

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

これを文字列として残した理由は、小数点の左側にある10桁を超える値を処理できるようにするためです。

しかし、それは始まりです。

于 2017-02-16T13:57:13.963 に答える
2
create function [Sistema].[fParseDecimal]
(
    @Valor nvarchar(4000)
)
returns decimal(18, 4) as begin
    declare @Valores table (Valor varchar(50));

    insert into @Valores values (@Valor);

    declare @Resultado decimal(18, 4) = (select top 1 
        cast('' as xml).value('sql:column("Valor") cast as xs:decimal ?', 'decimal(18, 4)')
    from @Valores);
    
    return @Resultado;
END
于 2020-11-26T21:40:01.607 に答える
1

切り捨てではなく結果を丸める必要がある場合は、次を使用できます。

select convert(decimal(38,4), round(convert(decimal(38,10), '123456789.1234567'),4))

これにより、次が返されます。

'123456789.1235' for '123456789.1234567'
'123456789.1234' for '123456789.1234467'
于 2018-12-12T07:48:00.087 に答える
1

MySQLでは

select convert( if( listPrice REGEXP '^[0-9]+$', listPrice, '0' ), DECIMAL(15, 3) ) from MyProduct WHERE 1
于 2020-11-18T22:14:53.813 に答える