Microsoft KB Archive/892406

From BetaArchive Wiki
Knowledge Base


Article ID: 892406

Article Last Modified on 11/2/2007



APPLIES TO

  • Microsoft SQL Server 2000 Developer Edition
  • Microsoft SQL Server 2000 Standard Edition
  • Microsoft SQL Server 2000 Enterprise Edition
  • Microsoft SQL Server 2000 Personal Edition
  • Microsoft SQL Server 2000 Enterprise Edition
  • Microsoft SQL Server 2000 Workgroup Edition
  • Microsoft SQL Server 2000 Desktop Engine (Windows)
  • Microsoft SQL Server 2000 Developer Edition
  • Microsoft SQL Server 2000 Enterprise Edition 64-bit



Bug #: 473209 (SQL Server 8.0)

SYMPTOMS

Assume the following scenario: In Microsoft SQL Server 2000, a stored procedure output parameter uses a decimal data type or a numeric data type. When a null value is returned, the setting for the precision and the scale of the data that is returned from the stored procedure output parameter is (38,0). This setting is retained on subsequent calls to the stored procedure. This behavior may cause data to be rounded to an integer value.

This problem applies only to applications that use the System.Data.SqlClient namespace. This problem does not affect applications that use other data access APIs.

For example, you have a stored procedure that has a DECIMAL(19,4) output parameter. Then, you call the following stored procedure from a parameterized SqlCommand.ExecuteNonQuery method.

CREATE PROCEDURE ParameterPrecisionTest(
    @pIn DECIMAL(19,4), 
    @pOut DECIMAL(19,4) OUTPUT) 
AS
    SET @pOut = @pIn

When you do this, you experience the following symptoms:

  1. When you call the ExecuteNonQuery method and set the SqlParameter output parameter to 1.5, 1.5 is returned.
  2. When you call the ExecuteNonQuery method and set the SqlParameter output parameter to DBNull, System.DBNull is returned.
  3. When you call the ExecuteNonQuery method and set the SqlParameter output parameter to 1.5, 2 is returned.

Note In the second call to the stored procedure, the precision and the scale are reset to (38,0). A value of 2 is returned in the third call to the stored procedure.

CAUSE

This problem occurs because SQL Server resets the precision and the scale to (38,0) when the following conditions are true:

  • You call a stored procedure that contains a SqlParameter output parameter.
  • You set the parameter to DBNull.


RESOLUTION

Service pack information

To resolve this problem, obtain the latest service pack for Microsoft SQL Server 2000. For more information, click the following article number to view the article in the Microsoft Knowledge Base:

290211 How to obtain the latest SQL Server 2000 service pack


STATUS

This problem was first corrected in Microsoft SQL Server 2000 Service Pack 4.

MORE INFORMATION

Steps to reproduce the problem

Run the following Visual C# code.

using System;
using System.Data.SqlClient;
using System.Data;

namespace MyApplication
{
    /// <summary>
    /// Summary description for Class1.
    /// </summary>
    class MyClass
    {
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main(string[] args)
        {
            
            string strConn = "Server=(local);Database=Northwind;" + 
                "Trusted_Connection=Yes;";
            SqlConnection cn = new SqlConnection(strConn);

            // Create stored procedure.
            PrepDb(cn);

            // Simple stored procedure.
            //    Input and output parameters decimal(19, 4).
            //    Sets the output parameter value to the input parameter value.
            SqlCommand cmd = cn.CreateCommand();
            cmd.CommandText = "ParameterPrecisionTest";
            cmd.CommandType = CommandType.StoredProcedure;
            SqlParameter pIn = cmd.Parameters.Add("@pIn", SqlDbType.Decimal);
            pIn.Precision = 19;
            pIn.Scale = 4;
            SqlParameter pOut = cmd.Parameters.Add("@pOut", SqlDbType.Decimal);
            pOut.Precision = 19;
            pOut.Scale = 4;
            pOut.Direction = ParameterDirection.Output;

            cn.Open();

            // Print value, precision, and scale for the output parameter.
            CheckParameter("Pre-execution", pOut);

            // The first call to the procedure returns 1.5.
            pIn.Value = 1.5;
            cmd.ExecuteNonQuery();
            CheckParameter("Returning 1.5", pOut);

            // The second call to the procedure returns null.
            pIn.Value = DBNull.Value;
            cmd.ExecuteNonQuery();
            CheckParameter("Returning DBNull", pOut);

            // The third call to the procedure returns 2.
            pIn.Value = 1.5;
            cmd.ExecuteNonQuery();
            CheckParameter("Returning 1.5", pOut);

            cn.Close();
        }
        static void CheckParameter(string Message, IDbDataParameter Parameter)
        {
            Console.WriteLine("\t{0}", Message);
            Console.WriteLine("\t\tValue:{0}\tPrecision:{1}\tScale:{2}", 
                (Parameter.Value != DBNull.Value) ? Parameter.Value : "DBNull", 
                Parameter.Precision, Parameter.Scale);
            Console.WriteLine();
        }

        static void PrepDb(IDbConnection Connection)
        {
            IDbCommand cmd = Connection.CreateCommand();
            Connection.Open();
            cmd.CommandText = "DROP PROCEDURE ParameterPrecisionTest";
            try {cmd.ExecuteNonQuery();} 
            catch {}
            cmd.CommandText = "CREATE PROCEDURE ParameterPrecisionTest " +
                "(@pIn decimal(19, 4), @pOut decimal(19, 4) OUTPUT) " +
                "AS SET @pOut = @pIn";
            cmd.ExecuteNonQuery();
            Connection.Close();
        }

    }
}

Note In this example, the second call to the stored procedure sets the output parameter to DBNull. Therefore, the precision and the scale are reset to (38,0). The stored procedure rounds the third result and therefore returns a value of 2.

Keywords: kbtshoot kbbug kbfix kbprb KB892406