使用输出参数

时间:2016-03-03 14:21:52

标签: sql-server stored-procedures

这是MS Sql Server:我已经包装了系统存储过程sp_sequence_get_range,这样我就可以通过一个简单的select返回它(我无法弄清楚如何在我的C#框架中处理OUTPUT参数)

这是整个存储过程:

CREATE PROCEDURE GetSequenceRange( @name VARCHAR, @counter INTEGER) AS

DECLARE @firstValue SQL_VARIANT = 0;
DECLARE @lastValue SQL_VARIANT = 0;
DECLARE @sql NVARCHAR(MAX);
SET @sql = N'EXEC sp_sequence_get_range @sequence_name = N''' + @name + N''', @range_size = ' + CONVERT(VARCHAR, @counter)
    + N', @range_first_value = @firstValue OUTPUT, @range_last_value = @lastValue OUTPUT;'
EXECUTE sp_executesql @sql, N'@name VARCHAR, @counter INTEGER, @firstValue SQL_VARIANT OUTPUT, @lastValue SQL_VARIANT OUTPUT', @name, @counter, @firstValue OUTPUT, @lastValue OUTPUT
SELECT @firstValue, @lastValue

当我尝试执行类似

的过程时
exec GetSequenceRange 'tablename', 3

我得到了

  

无效的对象名称't​​'。

无论我提供什么名称,都会在此错误消息中返回第一个字母。

我也试过

exec GetSequenceRange @name='tablename', @counter = 3

所有这些示例中的'tablename'都是合格的序列名称;我已经尝试了owner.sequence和database.owner.sequence。

我认为我的方法存在根本性的错误,但我没有看到它。

答案的奖励,该答案显示如何从C#调用中获取sp_sequence_get_range的结果,而不首先使用此存储过程包装器。

2 个答案:

答案 0 :(得分:0)

您应该可以直接轻松地调用存储过程。以下应该有效:

long _RangeFirstValue;
long _RangeFirstValue;

using (SqlConnection _Connection = new SqlConnection(_MyConnectionString))
{
    using (SqlCommand _Command = _Connection.CreateCommand())
    {
        _Command.CommandText = "sp_sequence_get_range";
        _Command.CommandType = CommandType.StoredProcedure;

        SqlParameter _ParamSequenceName =
                             new SqlParameter("sequence_name", SqlDbType.NVarChar, 776);
        _ParamSequenceName.Value = _MySequenceName;
        _Command.Parameters.Add(_ParamSequenceName);

        SqlParameter _ParamRangeSize = new SqlParameter("range_size", SqlDbType.BigInt);
        _ParamRangeSize.Value = _MyRangeSize;
        _Command.Parameters.Add(_ParamRangeSize);

        SqlParameter _ParamRangeFirstValue =
                             new SqlParameter("range_first_value", SqlDbType.Variant);
        _ParamRangeFirstValue.Direction = ParameterDirection.Output;
        _Command.Parameters.Add(_ParamRangeFirstValue);

        SqlParameter _ParamRangeLastValue =
                             new SqlParameter("range_last_value", SqlDbType.Variant);
        _ParamRangeLastValue.Direction = ParameterDirection.Output;
        _Command.Parameters.Add(_ParamRangeLastValue);

        _Command.ExecuteNonQuery();

        _RangeFirstValue = (long)(_ParamRangeFirstValue.Value);
        _RangeLastValue = (long)(_ParamRangeLastValue.Value);
    }
}

答案 1 :(得分:0)

[编辑]

咩。在发布此问题后重新阅读问题,是的,问题可能是太短的varchars。赔率是sp_executeSQL将与“未映射”变量一起正常运行。在这里留下这个答案,因为这个代码应该简化,如果只是为了使将来的维护和修改更简单。

首先,我完全同意如果您可以直接调用系统过程,那么您应该这样做 - 动态SQL很痛苦,最好避免。话虽如此,代码的问题在于您的复杂动态SQL会混淆。

第一步是构建动态SQL。这很好,为了清楚起见,我在这里用一些间距复制它:

SET @sql = N'EXEC sp_sequence_get_range @sequence_name = N'''
 + @name
 + N''', @range_size = '
 + CONVERT(VARCHAR, @counter)
 + N', @range_first_value = @firstValue OUTPUT, @range_last_value = @lastValue OUTPUT;'

在这里,您将硬编码文本与传入的变量@name和@counter连接起来。如果您传入“MyName”和“42”,则会获得(为了清晰起见,再次添加间距):

EXEC sp_sequence_get_range
  @sequence_name     = N'MyName'
 ,@range_size        = 42
 ,@range_first_value = @firstValue OUTPUT
 ,@range_last_value  = @lastValue OUTPUT;

然后将其作为sp_executeSQL的第一个参数传递:

EXECUTE sp_executesql
  @sql
 ,N'@name VARCHAR, @counter INTEGER, @firstValue SQL_VARIANT OUTPUT, @lastValue SQL_VARIANT OUTPUT'
 ,@name
 ,@counter
 ,@firstValue OUTPUT
 ,@lastValue OUTPUT

就是这样 - 你试图将@name和@counter作为参数传递给sp_executeSQL,你不需要这样做,因为它们被硬编码到动态SQL文本中。此外,它们未被定义为@sql中的参数,以便sp_executeSQL将参数定义的值传递(映射?)到。拿出来,它应该没问题:

EXECUTE sp_executesql
  @sql
 ,N'@firstValue SQL_VARIANT OUTPUT, @lastValue SQL_VARIANT OUTPUT'
 ,@firstValue OUTPUT
 ,@lastValue OUTPUT