varchar(max)
列をに変換したいdecimal(10,4)
。
使用しようとしたとき、cast
またはconvert
算術オーバーフローの例外が発生したとき。問題は、varchar列に格納されているデータに異なる精度と異なるスケールが含まれている可能性があることです。たとえば、123456789.1234567'、1.12345678、または123456.1234です。
123456.1234のような値の場合は問題なく変換されますが、他の値の場合は問題が発生します。
varchar(max)
列をに変換したいdecimal(10,4)
。
使用しようとしたとき、cast
またはconvert
算術オーバーフローの例外が発生したとき。問題は、varchar列に格納されているデータに異なる精度と異なるスケールが含まれている可能性があることです。たとえば、123456789.1234567'、1.12345678、または123456.1234です。
123456.1234のような値の場合は問題なく変換されますが、他の値の場合は問題が発生します。
テストの結果、問題の原因は小数点以下ではなく、精度(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のようになります
私の説明はコードにあります。:)
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)))
私は次の解決策を思いついた:
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)を変更してください。
使い方?結果は次のとおりです。
上記のコードでは、それぞれのケースが個別のWHENステートメントです。
変換の例をいくつか示します。
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)
6.999,50が有効な小数ではないという事実を見逃しています。確かに10進数にコンマと小数点を入れることはできませんか?何番になるの?
ロケールがを指定していると仮定します。グループ化として、および小数点として:グループ化された数字を削除するには:
SELECT CONVERT(decimal(11,2)、REPLACE( '6.999,50'、'。'、''))
小数として6999,50を生成します
値をその列に配置する前に、値を文字列として自分で切り捨てる必要があります。
それ以外の場合、小数点以下の桁数を増やしたい場合は、小数点以下の列の宣言を変更する必要があります。
あなたの主な問題は、小数点の右側のものではなく、左側のものです。タイプ宣言の2つの値は、精度とスケールです。
MSDNから:「精度は数値の桁数です。スケールは数値の小数点の右側の桁数です。たとえば、数値123.45の精度は5、スケールは2です。」
(10、4)を指定すると、小数点以下6桁、または最大数999999.9999しか格納できないことを意味します。それよりも大きいものはオーバーフローを引き起こします。
カスタム関数を使用して実装されます。これにより、文字列値を安全に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
これは古い質問ですが、実際に問題を「説明」したのはビルだけのようです。他の誰もが宣言の誤用に対する複雑な解決策を考え出しているようです。
「型宣言の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桁を超える値を処理できるようにするためです。
しかし、それは始まりです。
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
切り捨てではなく結果を丸める必要がある場合は、次を使用できます。
select convert(decimal(38,4), round(convert(decimal(38,10), '123456789.1234567'),4))
これにより、次が返されます。
'123456789.1235' for '123456789.1234567'
'123456789.1234' for '123456789.1234467'
MySQLでは
select convert( if( listPrice REGEXP '^[0-9]+$', listPrice, '0' ), DECIMAL(15, 3) ) from MyProduct WHERE 1