简体   繁体   中英

Decimal DbType - scale and precision flipped?

I have a SQL Server stored proc which I'm calling from C#.

Some of the parameters are Decimal , scale and precision vary but most often these are scale 8 and precision 2.

So I need to use the Decimal dbtype for these.

ALTER PROCEDURE [dbo].[SaveAssortmentPlanItem]
    @hierarchy                  nvarchar(13),
    @closing_store_stock        nvarchar(10),
    @closing_westway_stock      nvarchar(10),
    @sales_volume               decimal(8,2) = 0.00,
    @exit_code                  nvarchar(10),
    @name                       nvarchar(50),
    @medium_colour              nvarchar(50),
    @margin_percent             decimal(4,1) = 0.0,
    @retail_1st_on_sale_date    nvarchar(50),
    @new_continuing             nvarchar(10),
    @vendor                     nvarchar(50),
    @retail_initial_allocation  decimal(6,2) = 0.00,
    @no_of_stores               int,
    @total_planned_retail_closing_stock decimal(8,2) = 0.00,
    @fp_sales_value             decimal(8,2) = 0.00,
    @fp_margin_value            decimal(8,2) = 0.00
  AS ...

However when I try to call the procedure with a Decimal parameter with scale of 8 and precision 2, what I get is an error:

Parameter value 0.00000000 is out of range

(the value here is zero).

When this happens the procedure isn't even called and nothing appears in the SQL trace.

If I use precision 8 and scale 2, it works fine, even thouse this is the reverse of what I would expect.

If I read the values from the parameters they all appear to be as expected!

This is my main function code:

        const string SAVE_ASSORTMENT_PLAN_ITEM = "dbo.SaveAssortmentPlanItem";

        DAL.Execute(SAVE_ASSORTMENT_PLAN_ITEM, new[]
        {
            // NOTE a few parameters removed for brevity
            DAL.ParamNVarchar("@hierarchy", hierarchy),
            DAL.ParamNVarchar("@closing_store_stock", closingStoreStock ?? ""),
            DAL.ParamDecimal("@sales_volume", salesVolume == "0" ? "1" : salesVolume, 8, 2),
            DAL.ParamNVarchar("@exit_code", exitCode ?? ""),
            DAL.ParamNVarchar("@name", name ?? ""),
            DAL.ParamNVarchar("@medium_colour", mediumColour ?? ""),
            DAL.ParamDecimal("@margin_percent", marginPercent, 4, 1),
            DAL.ParamNVarchar("@retail_1st_on_sale_date", retail1stOnSaleDate ?? ""),
            DAL.ParamNVarchar("@new_continuing", newContinuing ?? ""),
            DAL.ParamNVarchar("@vendor", vendor ?? ""),
            DAL.ParamDecimal("@retail_initial_allocation", retailInitialAllocation, 6, 2),
            DAL.ParamInt("@no_of_stores", noOfStores ?? "0"),
            DAL.ParamDecimal("@total_planned_retail_closing_stock", totalPlannedRetailClosingStock, 8, 2),
            DAL.ParamDecimal("@fp_sales_value", FPSalesValue, 8, 2),
            DAL.ParamDecimal("@fp_margin_value", FPMarginValue, 8, 2)
        }

The Execute function is:

    public static void Execute(string procName, SqlParameter[] parameters = null)
    {
        const string BUY_ME_CONNECTION = "BuyMe";

        using (var connection = GetConnection(BUY_ME_CONNECTION))
        {
            connection.Open();

            var command = new SqlCommand(procName, connection) {CommandType = CommandType.StoredProcedure};
            if (parameters != null) command.Parameters.AddRange(parameters);

            command.ExecuteNonQuery();
        }
    }

...and the ParamDecimal function:

    public static SqlParameter ParamDecimal(string name, string value, byte scale, byte precision)
    {
        var decimalValue = decimal.Parse(value ?? "1", NumberStyles.AllowThousands | NumberStyles.AllowDecimalPoint);
        var result = new SqlParameter(name, SqlDbType.Decimal) { Value = decimalValue, Precision = precision, Scale = scale };
        return result;
    }

Considering that the least likely scenario here is the Scale and Precision values are actually reversed I have been through my code to see if I am flipping it somehow but this doesn't seem to be the case.

What else could be going on to cause this problem?

The above is expected, if you have DECIMAL(2,8) , you're saying a value no larger than 2 digits , and with 8 digits to the right of the decimal , which doesn't make sense. SQL Server would throw an error in this scenario...

The scale must be less than or equal to the precision.

By switching to DECIMAL(8, 2) , you're saying a value no larger than 8 digits , with 2 digits to the right of the decimal . The result for 0.00000000 would be 0.00 .

 DAL.ParamDecimal("@sales_volume", salesVolume == "0" ? "1" : salesVolume, 8, 2) 

According to the definition of this method, you are calling it with scale = 8 and precision = 2 . This will result in a SQL type DECIMAL(2,8) … which is obviously wrong.

The precision is the number of significant digits that can be stored, and the scale is the number of digits after the decimal fraction.

What you want is a DECIMAL(8,2) , ie a precision of 8 and a scale of 2, so switch the arguments:

DAL.ParamDecimal(…, scale: 2, precision: 8).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM