【问题标题】:Select Query Parameter from C# in a stored procedure在存储过程中从 C# 中选择查询参数
【发布时间】:2020-03-04 04:08:23
【问题描述】:

SQL Server 查询如何从参数中选择?我只想根据我的 C# 代码使其成为简单的设置选择列。可能吗?

这是我的存储过程:

ALTER PROCEDURE [dbo].[GetMembersDetailGenerateChanceTop10000]
    @EventId nvarchar(255), 
    @PeriodId nvarchar(255), 
    @QueryParam nvarchar(1000)
AS
BEGIN
    SET NOCOUNT ON;

    SELECT DISTINCT TOP 10000 @QueryParam
    FROM ign..Chance_Generated cg
    INNER JOIN ign..Contact c ON cg.ContactID = c.ContactId
    LEFT JOIN ign..CustomerAddress ca ON ca.parentid = cg.contactid 
    LEFT JOIN ign..new_cardlevelconfig cl ON cl.new_cardlevelconfigid = c.new_cardlevel
    LEFT JOIN ign..new_country co ON co.new_countryid = c.new_country 
    LEFT JOIN ign..new_province po ON po.new_provinceId = c.new_Province
    LEFT JOIN ign..StringMap sm ON sm.AttributeValue = c.new_IDType
    LEFT JOIN ign..new_city cy ON cy.new_cityId = c.new_CityCounty
    LEFT JOIN ign..new_transactionheader th ON cg.New_Name COLLATE DATABASE_DEFAULT = th.new_name COLLATE DATABASE_DEFAULT
    WHERE cg.EventId = @EventId 
      AND (ca.AddressNumber = '1' OR ca.AddressNumber IS NULL) 
      AND (sm.AttributeName IS NULL OR sm.AttributeName = 'new_idtype') 
      AND cg.periodId = @PeriodId

QueryParamEventIdPeriodId 将从 C# 代码中填充。

这是我的 C# 代码:

private List<GenerateModel> getDataTopFromStoreProcedure(string EventId, string PeriodId)
{
    // query select parameter
    string QueryParam = @"cg.Chance_Number, th.new_name as [th name], dateadd(HOUR,7,th.createdon)  as [th createdon], 
                        c.new_Initial, c.FirstName, c.LastName";

    string ConnString = GenerateChance.Properties.Settings.Default["DB_ConnectionString"].ToString();

    using (SqlConnection conn = new SqlConnection(ConnString))
    {
        SqlCommand cmd = new SqlCommand();
        cmd.CommandText = GetMembersDetailGenerateChanceTop10000;
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.CommandTimeout = 0; //no limit

        cmd.Parameters.Clear();
        cmd.Parameters.Add(new SqlParameter("QueryParam", QueryParam));
        cmd.Parameters.Add(new SqlParameter("EventId", EventId));
        cmd.Parameters.Add(new SqlParameter("PeriodId", PeriodId));

        cmd.Connection = conn;

        if (conn.State == ConnectionState.Open)
            conn.Close();

        conn.Open();

        using (SqlDataReader reader = cmd.ExecuteReader())
        {
            while (reader.Read())
            {
                list.Add(new GenerateModel
                {
                    ChanceNumber = reader["Chance_Number"].ToString(), //System.IndexOutOfRangeException Error
                    Receipt = reader["th name"].ToString(),
                    Date = reader["th createdon"].ToString(),
                    Initial = reader["new_Initial"].ToString(),
                    FirstName = reader["FirstName"].ToString(),
                    LastName = reader["LastName"].ToString(),
                });
            }
            reader.Close();
        }
    }

    return list;
}

我对如何实现他的方法感到困惑,因为我想返回对象模型中的所有选择结果,但总是出错

System.IndexOutOfRangeException : Chance_Number。

老实说,我为什么使用查询选择参数是因为我想从我之前定义的checkedListBox1 中获取值,方法是使用此代码获取所有checkedListBox1 值来确定选择查询。

string QueryParam = "cg.Chance_Number";//auto get chance_number as select mandatory

for (int i = 0; i < checkedListBox1.CheckedItems.Count; i++)
{
    QueryParam += ", " + ((clsItemList)checkedListBox1.CheckedItems[i]).Value;
}

【问题讨论】:

  • 您不能在这样的参数中传递列名列表。您必须更改存储过程以从中生成 SQL,然后使用 sp_executesql 之类的东西运行它。
  • @juharrdo 你有什么建议如何编辑我的存储过程?
  • 您要查找的术语是“动态 SQL”。虽然要小心你传入的内容,以免自己暴露在 SQL 注入中。
  • 看看this SO answer 是否有帮助。正如@John 指出的那样 - 这是一把双刃剑,所以要小心
  • @aminvincent,存储过程的 SELECT 必须返回读者正在等待的别名。动态 SELECT 没用。

标签: c# sql-server winforms stored-procedures


【解决方案1】:

您必须为参数使用适当长度的构造函数,如下所示,以避免问题。Read more on SQLParameter

public SqlParameter(string parameterName, System.Data.SqlDbType dbType, int size);

cmd.Parameters.Add(new SqlParameter("QueryParam", SqlDbType.NVarChar,1000)).Value = QueryParam;

您需要再做一些更改:

  • 您定义过程的方式是错误的。您必须将过程定义为动态 sql,以便 @queryparam 连接到 SELECT 查询,如下所示:
DECLARE @selectStmt NVARCHAR(MAX) = ''
DECLARE @sqldefinition NVARCHAR(4000) = '@EventId nvarchar(255), @PeriodId nvarchar(255)'

SET @selectStmt += 'select distinct top 10000 ' + @QueryParam  +
'from ign..Chance_Generated cg
inner join ign..Contact c on cg.ContactID = c.ContactId
left join ign..CustomerAddress ca on ca.parentid = cg.contactid 
left join ign..new_cardlevelconfig cl on cl.new_cardlevelconfigid = c.new_cardlevel
left join ign..new_country co on co.new_countryid = c.new_country 
left join ign..new_province po on po.new_provinceId = c.new_Province
left join ign..StringMap sm on sm.AttributeValue = c.new_IDType
left join ign..new_city cy on cy.new_cityId = c.new_CityCounty
left join ign..new_transactionheader th on cg.New_Name COLLATE DATABASE_DEFAULT = th.new_name COLLATE DATABASE_DEFAULT
where cg.EventId= '''+ @EventId +''' and (ca.AddressNumber = ''1'' or ca.AddressNumber is null) and (sm.AttributeName is null or sm.AttributeName = ''new_idtype'') 
and cg.periodId = ''' + @PeriodId + ''';' 

EXEC @sp_executesql @selectStmt, @sqldefinition, @EventId , @PeriodId 
``

- Always refer the tables with proper schema in the query

ign.SchemaName.new_country ign.SchemaName.new_province


【讨论】:

  • 如果您不介意,您可以像我上面的问题代码一样编辑您对 ign 架构的答案吗?
  • 我如何处理这个错误先生System.Data.SqlClient.SqlException: 'Procedure or function GetMembersDetailGenerateChanceTop10000 has too many arguments specified.我实现并运行我的程序SqlDataReader reader = cmd.ExecuteReader()表示错误
  • 错误本身说明了什么问题?您传递的参数多于存储过程中定义的参数。对于SELECT,您必须为存储过程指定相同数量的参数。
  • 我认为您正在传递许多 sql 参数,正如@iVad 所说。您应该只添加三个参数为cmd.Parameters.Add( ...
  • 这应该将@QueryParam(或在本例中为@sqldefinition)连接到生成的sql中,但它只是试图将列名作为参数传递,就像在OP中一样。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-03-12
  • 1970-01-01
  • 2014-05-10
  • 1970-01-01
  • 2013-01-08
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多