4

3つの列を持つ「Jrl」というテーブルがあります。

code  AS varchar(15)
total AS numeric(13,2)
rem   AS numeric(13,4)

議論のために、テーブルには値が '001'、400.00、および 52.1745 の行が 1 つだけあると仮定します。

次のクエリを検討してください。

SELECT code, total - rem
 FROM Jrl

'001' と347.8255を含む 1 行を返します。正解です。

クエリを次のように変更すると (実際にはこれが私のコードで必要なクエリです):

SELECT code, SUM(total) - SUM(rem)
 FROM Jrl
 GROUP BY code

'001' と347.83 (つまり、4 ではなくスケール 2) を含む 1 つの行を返します。

http://msdn.microsoft.com/en-us/library/ms190476%28v=sql.90%29.aspxのドキュメントによると、数値式 (減算) の型は numeric(16,4 )、明らかにそうではありません。(SQL Server 2005 および 2008 R2 でも同じ動作が得られます。)

誰かがそこで何が起こっているかについて私を啓発できますか?

ところで。回避策を見つけましたが、気に入らないので、この質問を投稿しています。回避策は、明示的なキャストを追加することです。

SELECT code, CAST(SUM(total) AS numeric(13,4)) - SUM(rem)
 FROM Jrl
 GROUP BY code
4

3 に答える 3

4

1)このスクリプトを実行して、私のコメントを読んでください。

2)この回答がお役に立てば幸いです。

3)SUM()-SUM()の精度は2です。これは、最初にsum(SUM(total)SUM(rem))を選択し、次に()を減算することを選択したためですSUM(total) - SUM(rem)

4)私のアドバイスは使用することですSELECT t.code, SUM(t.total - t.rem) AS diff ...(最初に減算してからSUM)。

5)この質問に対する私の答えを読むことができますSQL数値データ型の切り捨て値?

DECLARE @Test TABLE(
    code  varchar(15),
    total numeric(13,2),
    rem   numeric(13,4)
);

INSERT  @Test (code, total, rem)
VALUES  ('001', 11.78, 5.6789);

--Test [1]
SELECT  dt.*,
        SQL_VARIANT_PROPERTY(dt.diff, 'BaseType') AS diff_BaseType,
        SQL_VARIANT_PROPERTY(dt.diff, 'Precision') AS diff_Precision,
        SQL_VARIANT_PROPERTY(dt.diff, 'Scale') AS diff_Scale
FROM
(
        SELECT  t.code, t.total - t.rem AS diff
        FROM    @Test t
) dt;

/*
Operation: e1 - e2
Result precision: max(s1, s2) + max(p1-s1, p2-s2) + 1 = max(2,4) + max(13-2, 13-4) + 1 = 4 + 11 + 1 = 16
Result scale: max(s1, s2) = max(2, 4) = 4
*/

--Test [2]
SELECT  dt.*,
        SQL_VARIANT_PROPERTY(dt.diff, 'BaseType') AS diff_BaseType,
        SQL_VARIANT_PROPERTY(dt.diff, 'Precision') AS diff_Precision,
        SQL_VARIANT_PROPERTY(dt.diff, 'Scale') AS diff_Scale
FROM
(
        SELECT  t.code, SUM(t.total - t.rem) AS diff
        FROM    @Test t
        GROUP BY t.code
) dt;

/*
Operation: SUM(e1 - e2)
Result precision: 38--For SUM function, I think (it's just a hipotese), SQL Server choose the maximum precision to prevent the overflow error
                    Argument:
                    DECLARE @t TABLE (Col NUMERIC(2,1)); INSERT @t VALUES (1);
                    SELECT  SQL_VARIANT_PROPERTY(SUM(t.Col), 'Precision') FROM @t t;
                    Result: precision = 38 (maximum DECIMAL/NUMERIC precision)
Result scale: the same scale as (e1-e2)= 4 (please see Test [1])
*/

--Test [3]
SELECT  dt.*,
        SQL_VARIANT_PROPERTY(dt.SUM_total, 'BaseType')  AS SUM_total_BaseType,
        SQL_VARIANT_PROPERTY(dt.SUM_total, 'Precision') AS SUM_total_Precision,
        SQL_VARIANT_PROPERTY(dt.SUM_total, 'Scale')     AS SUM_total_Scale,

        SQL_VARIANT_PROPERTY(dt.SUM_rem, 'BaseType')    AS SUM_rem_BaseType,
        SQL_VARIANT_PROPERTY(dt.SUM_rem, 'Precision')   AS SUM_rem_Precision,
        SQL_VARIANT_PROPERTY(dt.SUM_rem, 'Scale')       AS SUM_rem_Scale,

        SQL_VARIANT_PROPERTY(dt.diff, 'BaseType')       AS diff_BaseType,
        SQL_VARIANT_PROPERTY(dt.diff, 'Precision')      AS diff_Precision,
        SQL_VARIANT_PROPERTY(dt.diff, 'Scale')          AS diff_Scale
FROM
(
        SELECT  t.code, 
                SUM(t.total) AS SUM_total, SUM(t.rem) AS SUM_rem, SUM(t.total) - SUM(t.rem) AS diff
        FROM    @Test t
        GROUP BY t.code
) dt;

/*
Operation: SUM(total) (<> e1 + e2 + ...)
Result precision: 38--I think SQL Server choose the maximum precision to prevent the overflow error
Result scale: the same precision as total= 2
*/


/*
Operation: SUM(rem) (<> e1 + e2 + ...)
Result precision: 38--I think SQL Server choose the maximum precision to prevent the overflow error
Result scale: the same precision as rem= 4
*/

/*
Operation: SUM(total) - SUM(rem) = e1 - e2
Result precision: max(s1, s2) + max(p1-s1, p2-s2) + 1 = max(2,4) + max(38-2, 38-4) + 1 = 4 + 36 + 1 = 41 
but max. precision is 38 so result precision = 38

Calculated result scale: max(s1, s2) = 4 
but because the real precision for result (41) is greater than maximum precision (38)
SQL Server choose to decrease the precision of the result to 2 (please see Test [3] - diff_Scale).
In this case (the real precision for result is greater than maximum precision) I think the 
expression for result's precision is max(s1, s2) - (real precision - maximum precision) + 1 = 4 - (41 - 38) + 1 = 4 - 3 + 1 = 2
For example you could try to modify the definition of total column to `total numeric(13,1)` 
and you will see that the precision for SUM(total) - SUM(rem) becomes 4 - 4(4+37+1=42) + 1 = 1
*/

結果:

--Test [1] SELECT t.code, t.total - t.rem AS diff
code diff   diff_BaseType  diff_Precision diff_Scale
---- ------ -------------- -------------- ----------
001  6.1011 numeric        16             4

--Test [2] SELECT t.code, SUM(t.total - t.rem) AS diff
code diff   diff_BaseType diff_Precision diff_Scale
---- ------ ------------- -------------- ----------
001  6.1011 numeric       38             4

--Test [3] SELECT t.code, ..., SUM(t.total) - SUM(t.rem) AS diff
code SUM_total SUM_rem diff SUM_total_BaseType SUM_total_Precision SUM_total_Scale SUM_rem_BaseType SUM_rem_Precision SUM_rem_Scale diff_BaseType diff_Precision diff_Scale
---- --------- ------- ---- ------------------ ------------------- --------------- ---------------- ------------------------------- ------------- -------------- ----------
001  11.78     5.6789  6.10 numeric            38                  2               numeric          38                4             numeric       38             2
于 2012-05-01T10:50:43.127 に答える
1

total AS numeric(13,2)それはフィールドのためです

精度の異なる2つのフィールドの合計を減算しているため、SQLServerは最小のフィールドの精度で結果を表示します。

もし、するなら:

 create table jrl2(
code  varchar(15),
total numeric(13,4),
rem   numeric(13,4)
)
insert into jrl2 values ('001', 400.00 , 52.1745)

select * from jrl2
SELECT code, total - rem  FROM Jrl

SELECT code, SUM(total) - SUM(rem)
 FROM Jrl2
 GROUP BY code

あなたが得るだろう:347.8255

于 2012-05-01T09:33:39.187 に答える
1

アンドレアス

問題は、SUM() の戻り値の型が最大精度の 38 を使用していることです。 28v=sql.90%29.aspx .)

「合計」列のタイプは数値(13,2)であるため、SUM(合計)の結果のタイプは(残念ながら)数値(38,2)です。オペランドの精度が 38 の場合、e1 + e2 の位取りは (やはり残念ながら) max(s1,s2) ではありません。

これは、BOL の脚注に記載されています: http://msdn.microsoft.com/en-us/library/ms190476.aspx*結果の精度とスケールの絶対最大値は 38 です。結果の精度が 38 を超える場合、結果の整数部分が切り捨てられないように、対応するスケールが縮小されます。

http://support.microsoft.com/kb/281341も参照してください。

于 2012-05-01T13:50:54.383 に答える