【问题标题】:dynamically build sql statement based on selection criteria of checkboxes根据复选框的选择条件动态构建sql语句
【发布时间】:2011-08-07 19:05:19
【问题描述】:

我有一个 .aspx 页面,该页面在 SQL Server 2005 数据库中将产品分为不同类别。

用户应该能够从任何组合中进行选择。例如,从颜色类别中选择“米色”和“黑色”,从样式类别中选择“挂毯”,应该只产生具有米色+挂毯以及黑色+挂毯的产品。

我正在考虑使用 WHERE 子句来检查: 如果选中任何颜色复选框,则使用 WhereCluaseColor += 复选框值将带有这些复选框值的字符串构建到“WhereClauseColor”字符串变量中;

如果选中任何 STYLES 复选框,则使用另一个名为 WhereClauseStyles 的变量——就像在颜色类别中一样。

最后,构建一个名为 WhereClause 的 sql 字符串: if WhereClauseColor != "" then WhereClause += AND 'color' field in db CONTAINS the WhereClauseColor;

样式的相似之处。

但我没有运气。可能我需要一种完全不同的方法。太糟糕了,客户希望产品是复选框以允许在每个类别下进行多项选择。

WhereClause = "SELECT * FROM [xxxx] WHERE 0 = 1";
/////COLORS
// if (Request.Form["color_gold"] != null) { WhereClause += " OR  xxx.color = 'gold'"; }//// OLD CODE. . 08/04/11
if (Request.Form["color_spice"] != null) { WhereClause += " OR  CONTAINS (xxxx.color,'spice')"; }/// ## NEW CODE: NOTE CONTAINS. . 08/04/11

【问题讨论】:

  • 请重新格式化您的问题 - 段落的存在是有原因的。我还建议发布相关代码。
  • 对不起。以后会更加小心。重新格式化以及发布现有代码的一部分。
  • 您的代码对SQL Injection 开放。看看parameterized queries
  • 我知道。之后会这样做。

标签: c# sql-server-2005 dynamic


【解决方案1】:

您是否尝试过首先在 SSMS 中构建您需要的那种 SQL 选择查询以获得正确的语法,以便在您开始构建动态查询时至少现在您的目标是什么?这就是我的想法。

您还可以在 SQL 中使用临时表完成所有操作,其中第一个查询采用第一个选择的复选框(作为参数传递给存储过程,下一组复选框将在下一节中处理您的存储过程。

【讨论】:

    【解决方案2】:

    我不知道你为什么使用Request.Form。像if (chkSpice.Checked) 这样的东西会更易读。

    如果您必须将 SQL 保存在代码中,并且不能使用存储过程,那么这是您可以做到的一种方法。您不接受用户输入并在 SQL 中传递它,因此不存在 SQL 注入风险。

    如果您知道您的样式和颜色将在数据库中命名,SQL IN 运算符会更有效。

    string styles = String.Empty;
    string colors = String.Empty;
    
    if (chkTapestry.Checked)
        styles = "'Tapestry'";
    else if (chkRug.Checked)
        styles += ",'Rug'";
    
    
    if (chkBlack.Checked)
        colors = "'Black'";
    else if (chkBeige.Checked)
        colors += ",'Beige'";
    
    string sql = "SELECT * FROM [X]";
    
    if (styles.Length > 0 && colors.Length > 0)
        sql += String.Format(" WHERE [Style] IN ({0}) AND [Color] IN ({1})", styles, colors);
    else if (styles.Length > 0)
        sql += String.Format(" WHERE [Style] IN ({0})", styles);
    else if (colors.Length > 0)
        sql += String.Format(" WHERE [Color] IN ({0})", colors);
    

    以上是组装样式和颜色的一种相当手动的方法,并且使用了令人遗憾的样式和颜色名称的硬编码。更好的设计可能是:

    1. 从数据库中选择所有颜色。将它们存储在 DataTable 中。
    2. Data 将颜色 DataTable 绑定到 CheckBoxList 控件以在页面上显示颜色。
    3. 从数据库中选择所有样式。将它们存储在另一个 DataTable 中。
    4. Data 将样式 DataTable 绑定到 CheckBoxList 控件以在页面上显示样式。

    然后,在 PostBack 上:

    1. 使用数据库中的值重新加载您的颜色数据表。接受自您的页面呈现后颜色列表发生变化的小风险,或将 DataTable 跨回发存储在 Session 变量中以降低风险。
    2. 重新加载您的样式数据表。
    3. 在 for 循环中遍历您的颜色 CheckBoxList 项。对于选定的每个项目,使用 for 循环索引从 DataTable 中检索相同的行索引。从 DataRow 中获取颜色名称,并将其添加到要在 WHERE 子句的 Color IN (...) 部分中使用的颜色列表中。这可确保您使用的是数据库中的值,而不是可能被用户篡改的值。
    4. 在 for 循环中遍历您的样式 CheckBoxList 项目,执行与颜色相同的操作。
    5. 按照我在示例代码中所做的那样组装您的 WHERE 子句。
    6. 在您的数据库上执行查询并处理结果集。

    示例代码:

    protected DataTable dtColors
    {
        get
        {
            if (Session["fabrics-dtColors"] == null)
                Session["fabrics-dtColors"] = FetchDataTable("SELECT DISTINCT Color FROM X ORDER BY Color");
            return (DataTable)Session["fabrics-dtColors"]; 
        }
    }
    
    protected DataTable dtStyles
    {
        get
        {
            if (Session["fabrics-dtStyles"] == null)
                Session["fabrics-dtStyles"] = FetchDataTable("SELECT DISTINCT Style FROM X ORDER BY Style");
            return (DataTable)Session["fabrics-dtStyles"]; 
        }
    }
    
    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            cblColors.DataSource = dtColors;
            cblColors.DataBind();
    
            cblStyles.DataSource = dtStyles;
            cblStyles.DataBind();
        }
    }
    
    protected void btnSearch_OnClick(object sender, EventArgs e)
    {
        string colors = String.Empty;
        string styles = String.Empty;
        string sql = "SELECT * FROM [X]";
    
        if (cblColors.SelectedIndex > -1)
        {
            for (int i = 0; i < cblColors.Items.Count; i++)
            {
                if (cblColors.Items[i].Selected)
                {
                    colors += String.Format("'{0}',", dtColors.Rows[i][0]);
                }
            }
    
            colors = colors.TrimEnd(',');
        }
    
        if (cblStyles.SelectedIndex > -1)
        {
            for (int i = 0; i < cblStyles.Items.Count; i++)
            {
                if (cblStyles.Items[i].Selected)
                {
                    styles += String.Format("'{0}',", dtStyles.Rows[i][0]);
                }
            }
    
            styles = styles.TrimEnd(',');
        }
    
        if (styles.Length > 0 && colors.Length > 0)
            sql += String.Format(" WHERE [Style] IN ({0}) AND [Color] IN ({1})", styles, colors);
        else if (styles.Length > 0)
            sql += String.Format(" WHERE [Style] IN ({0})", styles);
        else if (colors.Length > 0)
            sql += String.Format(" WHERE [Color] IN ({0})", colors);
    
        GetSearchResults(sql);
    }
    

    【讨论】:

    • Nekno,我可以稍后尝试您的解决方案。我还必须使用“CONTAINS”子句,因为数据库字段的值与复选框中的值略有不同,因此 IN 或 CONTAINS 是选项,而不是 SQL 中的相等运算符。我稍后会联系。谢谢大家!
    • 哦,我只是放回当前直播站点的链接;如您所见,用户可以从类别中选择任意组合。这使得构建查询变得困难。
    【解决方案3】:

    我一直在开发一个名为 QBuilder 的库,以使这种动态 sql 生成更容易一些。可能对你有点用处。

    http://qbuilder.codeplex.com/

    【讨论】:

      【解决方案4】:
      private List<string> arrColors = new List<String>();
      private List<string> arrStyles = new List<String>();   
      
      if (Request.Form["color_spice"] != null) { 
        arrColors.Add("spice"); }/// ## NEW CODE: NOTE CONTAINS. . 08/04/11
      
      if (Request.Form["color_gold"] != null) { arrColors.Add("gold"); }
      
      string sql = "select * from ado_products_fabrics where [color] in('";
             sql += string.Join("','", this.arrColors.ToArray());
             sql += "')";
             sql += " AND [style] in('";
             sql += string.Join("','", this.arrStyles.ToArray());
             sql += "')";
      
      WhereClause = sql;/
      

      【讨论】:

        【解决方案5】:

        根据您的各种选择在您的应用程序代码中构建表格:SelectedColors、Selected Styles,...。然后将它们作为参数传递给在 WHERE 子句中使用它们的存储过程:

        where ( Color in (select Color from SelectedColors) or
             not exists ( select Color from SelectedColors ) ) and ...
        

        我假设如果没有为给定类别做出选择,例如打印比例,您想接受所有值。

        这将让您有多个选择并防止 SQL 注入攻击。

        【讨论】:

        • 您好,如果没有用户选择符合条件,则不会显示任何结果。谢谢。
        • 而且,是的,您对打印比例等的假设是正确的。我会让您知道这是怎么回事。我会先尝试不使用存储过程....
        • 动态构建表格取得进展。我的动态查询现在看起来像:
        • SELECT * FROM [xxxx] WHERE(颜色(从 System.Web.UI.WebControls.Table 选择颜色)或不存在(从 System.Web.UI.WebControls.Table 选择颜色))但是我收到一个错误:'.' 附近的语法不正确。关键字“表”附近的语法不正确。
        • @user92546 - SQL Server 2005 支持 TABLE 变量,但它支持传递给存储过程的 TABLE 参数。直到 SQL Server 2008 才添加对 TABLE 参数的支持。@Meengla - 您正在尝试构建在 SQL Server 上运行的 SQL 查询。您不能在 SQL 查询中使用 System.Web.UI.WebControls.Table - 这是一种 .Net 数据类型,而不是 SQL 表。您可以执行Color IN (SELECT Color FROM [X]),其中X 是SQL Server 中的一个表。但是您正在从 .Net 中传递选择,因此更简单的方法是使用逗号分隔的颜色列表。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-02-02
        • 1970-01-01
        • 1970-01-01
        • 2019-02-23
        • 2019-12-30
        相关资源
        最近更新 更多