简体   繁体   English

需要使用来自文本框的逗号分隔值使用 WHERE 更新 SQL 中的列

[英]Need to update a column in SQL with WHERE using comma separated values from textbox

Just to let you guys know I'm not a programmer @ all but I can manage to get code to work sometimes but I'm stumped now.只是想让你们知道我不是程序员@all 但有时我可以设法让代码工作但我现在很难过。 I have a c# web-based project that I finally figured out how to pass comma-separated values from a single textbox to return a result in Datagrid when I click search.我有一个 c# 基于 Web 的项目,我终于想出了如何在单击搜索时从单个文本框传递逗号分隔值以在 Datagrid 中返回结果。 The code looks like this:代码如下所示:

    {
String str = "select row_number() OVER (ORDER BY [sequenceid]) #,[JobNumber],[Item],Quantity,Bay,Trailer,Sequenceid, Produced from vwcabsandcountersbyjob ";

String str1 = "select SUM([Quantity])AS [Items Remaining to Be Loaded] from vwcabsandcountersbyjob";
        
        //CODE THAT ALLOWS MULTIPLE ORDER NUMBERS TO BE ENTERED IN A SINGLE TEXTBOX
        if (!string.IsNullOrEmpty(TextBox1.Text.Trim()))
        {
            List<string> search = new List<string>();
            char[] characters = { ',', '\n' };
            string[] ids = TextBox1.Text.Trim().Split(characters, StringSplitOptions.RemoveEmptyEntries);
            for (int i = 0; i < ids.Length; i++)
            {
                search.Add(ids[i].Trim());
            }

            str += " WHERE (jobnumber IN ('"  + string.Join("','", search.ToArray()) +  "') and loaded is null) ORDER BY ITEM DESC";
        }

        //CODE THAT ALLOWS MULTIPLE ORDER NUMBERS TO BE ENTERED IN A SINGLE TEXTBOX
        if (!string.IsNullOrEmpty(TextBox1.Text.Trim()))
        {
            List<string> search = new List<string>();
            char[] characters = { ',', '\n' };
            string[] ids = TextBox1.Text.Trim().Split(characters, StringSplitOptions.RemoveEmptyEntries);
            for (int i = 0; i < ids.Length; i++)
            {
                search.Add(ids[i].Trim());
            }

            str1 += " WHERE (jobnumber IN ('" + string.Join("','", search.ToArray()) + "') and loaded is null) ";
        }



        SqlCommand xp = new SqlCommand(str, vid);
        SqlCommand xp1 = new SqlCommand(str1, vid);


        //xp.Parameters.Add("@search", SqlDbType.NVarChar).Value = TextBox1.Text;
        xp.Parameters.Add("@search", SqlDbType.NVarChar, 20).Value = TextBox1.Text;

        xp1.Parameters.Add("@search", SqlDbType.NVarChar).Value = TextBox1.Text;

        vid.Open();
        xp.ExecuteNonQuery();
        SqlDataAdapter da = new SqlDataAdapter();
        da.SelectCommand = xp;
        DataSet ds = new DataSet();
        da.Fill(ds, "[jobnumber]");
        GridView1.DataSource = ds;
        GridView1.DataBind();

        vid.Close();

        vid.Open();
        xp1.ExecuteNonQuery();
        SqlDataAdapter da1 = new SqlDataAdapter();
        da1.SelectCommand = xp1;
        DataSet ds1 = new DataSet();
        da1.Fill(ds1, "[jobnumber]");
        GridView2.DataSource = ds1;
        GridView2.DataBind();
        vid.Close();

    }

Now i have a second button that I want to update a field when they press "mark complete".现在我有第二个按钮,当他们按下“标记完成”时,我想更新一个字段。 It works fine with a single entry and here is the code:它适用于单个条目,这是代码:

  protected void Button2_Click(object sender, EventArgs e)
    {
        vid.Open();

        
        SqlCommand xp = new SqlCommand("Update [Job_Master] SET [Completed] = GETDATE() WHERE [job number] =@search", vid);
        SqlCommand xp3 = new SqlCommand("Update [Countertops] SET [Completed] = GETDATE() WHERE [jobnumber] =@search", vid);



        xp.Parameters.Add("@search", SqlDbType.VarChar).Value = TextBox1.Text;
        xp3.Parameters.Add("@search", SqlDbType.NChar).Value = TextBox1.Text;

        xp.ExecuteNonQuery();
        xp3.ExecuteNonQuery();
        vid.Close();

        string message = "This job has been marked complete!";
        string script = "window.onload = function(){ alert('";
        script += message;
        script += "')};";
        ClientScript.RegisterStartupScript(this.GetType(), "SuccessMessage", script, true);

    }

The problem is it only works for one value.问题是它只适用于一个值。 I've tried a million different things but nothing works mainly because I don't know what I'm doing.我已经尝试了一百万种不同的方法,但没有任何效果,主要是因为我不知道自己在做什么。 This is the closest I've come (i added a 3rd button because i didn't want to break button 2 that works for single value):这是我最接近的(我添加了第三个按钮,因为我不想破坏适用于单个值的按钮 2):

   Protected void Button3_Click(object sender, EventArgs e)
    {
        String str3 = "Update [Job_Master] SET [Completed] = GETDATE()";

        if (!string.IsNullOrEmpty(TextBox1.Text.Trim()))
        {
            List<string> search = new List<string>();
            char[] characters = { ',', '\n' };
            string[] ids = TextBox1.Text.Trim().Split(characters, StringSplitOptions.RemoveEmptyEntries);
            for (int i = 0; i < ids.Length; i++)
            {
                search.Add(ids[i].Trim());
            }

            str3 += " WHERE [job number] IN ('" + string.Join("','", search.ToArray()) + "')";
        }

        SqlCommand xp4 = new SqlCommand(str3, vid);


        vid.Open();

        xp4.Parameters.Add("@search", SqlDbType.VarChar).Value = TextBox1.Text;
        

        xp4.ExecuteNonQuery();
      
        vid.Close();

        string message = "This job has been marked complete!";
        string script = "window.onload = function(){ alert('";
        script += message;
        script += "')};";
        ClientScript.RegisterStartupScript(this.GetType(), "SuccessMessage", script, true);

    }

and it doesn't work.它不起作用。 It only updates when I have a single entry.它仅在我有一个条目时更新。 My question is how do use those same values from that textbox work in an update statement?我的问题是如何在更新语句中使用该文本框中的相同值? I hope I'm being clear but will try my best to answer any questions but please remember my vocabulary is very limited when it comes to programming.我希望我说得很清楚,但会尽力回答任何问题,但请记住,在编程方面,我的词汇量非常有限。 Thanks!谢谢!

It would be wise if you just divide your code into smaller parts, group them by their role and responsibility.明智的做法是将代码分成更小的部分,并按它们的角色和职责对它们进行分组。 This would make things much easier to maintain and also to work with.这将使事情更容易维护和使用。

if you see repetitive code, then it's a flag of moving it to a separate method.如果您看到重复的代码,那么这是将其移至单独方法的标志。

A few notes that I've found in your code:我在您的代码中发现的一些注意事项:

  • you should use using clause when possible, like using it with SqlConnection , SqlCommand , and SqlDataAdapter .您应该尽可能使用using子句,例如将它与SqlConnectionSqlCommandSqlDataAdapter一起使用。
  • Multiple unneeded SqlCommand are used.使用了多个不需要的SqlCommand
  • Providing SqlParamter while the query doesn't have any parameter.在查询没有任何参数时提供SqlParamter
  • using string concatenation is not optimal, instead use StringBuilder .使用字符串连接不是最优的,而是使用StringBuilder
  • Your business logic should have its own methods or classes, and can be recalled inside any event.您的业务逻辑应该有自己的方法或类,并且可以在任何事件中调用。
  • Split returns Array , so no need to convert Array to another Array . Split返回Array ,因此无需将Array转换为另一个Array
  • Always, each portion of the code, should be scoped to that portion role only.始终,代码的每个部分都应该仅限于该部分角色。 For instance, OnClick event, should handle the click event only, and not handling the update records.比如OnClick事件,应该只处理点击事件,不处理更新记录。 The update part should be declared and handled outside the event, and you only recall it from inside the event.更新部分应该在事件外部声明和处理,您只能从事件内部调用它。

I have updated the code, which would give you a better view on the above notes:我已经更新了代码,这将使您更好地了解上述注释:

protected void SearchButton_OnClick(object sender, EventArgs args)
{
    //CODE THAT ALLOWS MULTIPLE ORDER NUMBERS TO BE ENTERED IN A SINGLE TEXTBOX
    if (!string.IsNullOrWhiteSpace(TextBox1.Text))
    {
        SearchAndBind(TextBox1.Text);
    }
}

protected void Button2_Click(object sender, EventArgs e)
{
    var textValue = TextBox1.Text.Trim();
    
    if(!string.IsNullOrWhiteSpace(textValue))
    {
        UpdateRecords("UPDATE [Job_Master] SET [Completed] = GETDATE() WHERE [job number] = @search", "@search", textValue);    
        
        UpdateRecords("UPDATE [Countertops] SET [Completed] = GETDATE() WHERE [jobnumber] = @search", "@search", textValue);    
                
        CompletedJobNotification();
    }

}

protected void Button3_Click(object sender, EventArgs e)
{
    string inClause = GetInClause(TextBox1.Text); 

    if(!string.IsNullOrWhiteSpace(inClause))
    {
        StringBuilder query = new StringBuilder("Update [Job_Master] SET [Completed] = GETDATE()");
        
        query.Append(" WHERE [job number] ").Append(inClause);
        
        UpdateRecords(query.ToString());
        
        CompletedJobNotification();
    }
}


private void PopulateData(string query, string srcTable, GridView gridView)
{
    if (!string.IsNullOrWhiteSpace(query))
    {
        using(SqlConnection connection = new SqlConnection(connectionString))
        using(SqlDataAdapter adapter = new SqlDataAdapter(query, connection))
        {
            adapter.Open();
            DataSet ds = new DataSet();
            da.Fill(ds, srcTable);
            gridView.DataSource = ds;
            gridView.DataBind();
        }
        
    }
}

private string GetInClause(string text)
{
    if(!string.IsNullOrWhiteSpace(text))
    {
        char[] characters = { ',', '\n' };
        var ids = text.Trim().Split(characters, StringSplitOptions.RemoveEmptyEntries);
        return "IN ('"  + string.Join("','", ids) +  "')";
    }

    return string.Empty;
}

private void SearchAndBind(string search)
{
    //CODE THAT ALLOWS MULTIPLE ORDER NUMBERS TO BE ENTERED IN A SINGLE TEXTBOX
    if (!string.IsNullOrWhiteSpace(search))
    {
        string inClause = GetInClause(search); 
        
        if(!string.IsNullOrWhiteSpace(inClause))
        {
            var searchWhere = $" WHERE jobnumber loaded IS NULL AND {inClause} ";   

            StringBuilder str = new StringBuilder("SELECT ROW_NUMBER() OVER (ORDER BY [sequenceid]) #,[JobNumber],[Item],Quantity,Bay,Trailer,Sequenceid, Produced FROM vwcabsandcountersbyjob");

            StringBuilder str1 = new StringBuilder("SELECT SUM([Quantity]) AS [Items Remaining to Be Loaded] FROM vwcabsandcountersbyjob");
            
            str.Append(searchWhere).Append(" ORDER BY ITEM DESC ");

            str1.Append(searchWhere);
            
            PopulateData(str.ToString(), "[jobnumber]", GridView1);
            
            PopulateData(str1.ToString(), "[jobnumber]", GridView2);            
        }       
    }   
}

    
private void UpdateRecords(string query, string parameterName = null,string parameterValue = null)  
{
    using(var connection = new SqlConnection(connectionString))
    using(var command = new SqlCommand(query))
    {
        if(!string.IsNullOrWhiteSpace(parameterName) && !string.IsNullOrWhiteSpace(parameterValue))
        {
            command.Parameters.AddWithValue(parameterName, parameterValue);     
        }
            
        connection.Open();
        command.ExecuteNonQuery();
        
    }
}


private void ShowJavaScriptAlert(string message)
{
    if(!string.IsNullOrWhiteSpace(inClause))
    {
        ClientScript.RegisterStartupScript(this.GetType(), "SuccessMessage", $" window.onload = function(){{ alert('{message}')}}; ", true);
    }   
}

private void CompletedJobNotification()
{
    ShowJavaScriptAlert("This job has been marked complete!");
}

Ok, first up, we are writing too much code here.好的,首先,我们在这里写了太多代码。

Next up, we can MOST certainly have a list of parameters, and they can be optional, and we NEVER concatenate user input into the SQL string - we don't have to.接下来,我们可以肯定地拥有一个参数列表,它们可以是可选的,并且我们永远不会将用户输入连接到 SQL 字符串中——我们不必这样做。

Now, I don't have the users data, lets do this:现在,我没有用户数据,让我们这样做:

I can type in a city, or several.我可以输入一个或几个城市。 I will display some hotels, and then ONE button to confirm the hotels and update the database with the confirm date.我会显示一些酒店,然后一个按钮来确认酒店并用确认日期更新数据库。

(the same as what you are doing). (与您正在做的一样)。

Ok, so a text box, and a button.好的,所以有一个文本框和一个按钮。 Like this:像这样:

    Search: <asp:TextBox ID="txtPromptCity" runat="server" Height="17px" Width="336px"></asp:TextBox>
    <asp:Button ID="cmdSearch" runat="server" Text="Search City" Style="margin-left:20px" />

So, you can type in one city, or several (with a, in between).因此,您可以输入一个或多个城市(中间有一个)。 Same as what you need/are doing.与您需要/正在做的一样。

Ok, so when you type in and hit search, we send the results to a grid view.好的,所以当您输入并点击搜索时,我们会将结果发送到网格视图。

So, we have this code for the button:所以,我们有这个按钮代码:

    DataTable rstHotels = new DataTable();
    protected void Page_Load(object sender, EventArgs e)
    {
        if (IsPostBack)
            rstHotels = (DataTable)ViewState["rstHotels"];
    }

    protected void cmdSearch_Click(object sender, EventArgs e)
    {
        string[] strChoices;
        strChoices = txtPromptCity.Text.Split(',');

        using (SqlCommand cmdSQL = new SqlCommand("",
            new SqlConnection(Properties.Settings.Default.TEST4)))
        {
            string strSQL = "";
            int i = 1;
            foreach (string strCity in strChoices)
            {
                if (strSQL != "")
                    strSQL += ",";
                i += 1;
                strSQL += "@" + i;
                cmdSQL.Parameters.Add("@" + i, SqlDbType.NVarChar).Value = strCity;
            }

            cmdSQL.CommandText = "SELECT * from tblHotels WHERE City IN (" + strSQL + ")";

            cmdSQL.Connection.Open();
            rstHotels.Rows.Clear();
            rstHotels.Load(cmdSQL.ExecuteReader());
            ViewState["rstHotels"] = rstHotels;
            GridView1.DataSource = rstHotels;
            GridView1.DataBind();
        }
    }

note how I keep/have the data.table - I persist it at the class level.请注意我如何保留/拥有 data.table - 我将其保留在 class 级别。

So, our output is now this所以,我们的 output 现在是这个

在此处输入图像描述

Ok, so we now have the parameter issue working.好的,所以我们现在有参数问题了。

Now, all you have to do is check box the ones to approve, and then send the data back to the table - along with the approved visit date.现在,您所要做的就是选中要批准的那些,然后将数据连同批准的访问日期一起发送回表格。

That code NOW becomes dead simple.该代码现在变得非常简单。

We can use this:我们可以使用这个:

    protected void cmdConfirm_Click(object sender, EventArgs e)
    {

        foreach (GridViewRow gvRow in GridView1.Rows)
        {
            CheckBox chkVisit = (CheckBox)gvRow.FindControl("chkVisit");

            if (chkVisit.Checked)
                // update Visit date in table
                rstHotels.Rows[gvRow.RowIndex]["VistDate"] = DateTime.Today;

            // now send table changes back to database.

            using (SqlCommand cmdSQL = new SqlCommand("SELECT * from tblHotels where ID = 0", 
             new SqlConnection(My.Settings.TEST4)))
            {
                SqlDataAdapter da = new SqlDataAdapter(cmdSQL);
                SqlCommandBuilder daUpate = new SqlCommandBuilder(da);
                da.Update(rstHotels);
            }
        }
    }

So, note how we send the grid choices back to the table, and then send the table back to the database.因此,请注意我们如何将网格选择发送回表,然后将表发送回数据库。 This makes the whole process easy, and VERY much less code.这使得整个过程变得简单,代码也少得多。

And I would probably add a 2-3 lines more code in the Row bound event, and set the checkbox if the row has a date already.我可能会在 Row 绑定事件中添加 2-3 行代码,如果该行已经有日期,则设置复选框。 And we could I suppose even check if we un-check the checkbox, and again null out the date column row.我什至可以假设我们是否取消选中复选框,然后再次在日期列行中显示 null。

All of these are VERY dead easy with the above approach.使用上述方法,所有这些都非常容易。

So, persisting the table as per above - makes this REALLY easy.因此,按照上面的方式保留表格 - 这真的很容易。 And note the data Keys setting - we can use/get/grab the primary key of each row and not have to display it - but this example does not see to need this ability anyway.并注意数据键设置——我们可以使用/获取/获取每一行的主键,而不必显示它——但这个例子无论如何都不需要这种能力。

The markup I used for above is this:我在上面使用的标记是这样的:

    Search: <asp:TextBox ID="txtPromptCity" runat="server" Height="17px" Width="336px"></asp:TextBox>
    <asp:Button ID="cmdSearch" runat="server" Text="Search City" Style="margin-left:20px" OnClick="cmdSearch_Click" />

    <div style="width:45%;margin-top:20px;margin-left:20px">
        <asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False" DataKeyNames="ID" CssClass="table table-hover">
            <Columns>
                <asp:BoundField DataField="FirstName" HeaderText="FirstName"  />
                <asp:BoundField DataField="LastName" HeaderText="LastName"    />
                <asp:BoundField DataField="HotelName" HeaderText="HotelName"  />
                <asp:BoundField DataField="City" HeaderText="City"            />
                <asp:BoundField DataField="VistDate" HeaderText="Visit Date"  DataFormatString="{0:yyyy-MM-dd}"  />
                <asp:TemplateField HeaderText="Confirm Visit"  ItemStyle-HorizontalAlign="Center">
                    <ItemTemplate>
                        <asp:CheckBox ID="chkVisit" runat="server" />
                    </ItemTemplate>
                </asp:TemplateField>
            </Columns>
        </asp:GridView>

        <asp:Button ID="cmdConfirm" runat="server" Text="Confirm Vists" Width="143px" CssClass="btn-info" OnClick="cmdConfirm_Click" />
        <br />

    </div>

Thanks for all of the help from everyone.感谢大家的帮助。 The actual was that my aspx code was pointing to OnClick="Button2_Click" and not OnClick="Button3_Click".实际是我的 aspx 代码指向 OnClick="Button2_Click" 而不是 OnClick="Button3_Click"。 Once i fixed that and put in the original code it worked.一旦我修复了它并放入原始代码,它就可以工作了。 Having said that you guys have pointed out some great points that i'm going to try and fix.话虽如此,你们已经指出了一些我要尝试修复的要点。 Again, i'm not a programmer and sure appreciate you guys helping.再说一遍,我不是程序员,非常感谢你们的帮助。 Thanks.谢谢。

First of all, good job getting as far as you did.首先,尽你所能。 I think the only thing left is just a simple mistake.我认为唯一剩下的只是一个简单的错误。 I'll provide this which removes a little of the code you don't need我会提供这个删除一些你不需要的代码

Protected void Button3_Click(object sender, EventArgs e)
    {
        String str3 = "Update [Job_Master] SET [Completed] = GETDATE()";

// ** This is a little dangerous. If the TextBox is EMPTY, you will be
// ** Updating ALL rows in the table. I assume that is not what you want.
// ** You should return an error if text box is empty.
        if (!string.IsNullOrEmpty(TextBox1.Text.Trim()))
        {
            char[] characters = { ',', '\n' };
            char[] invalid = { ',',';',' ','\'','"','\\','\t' };
            string[] ids = TextBox1.Text.Trim().Split(characters, StringSplitOptions.RemoveEmptyEntries);
            //
            // ** You don't really need to copy into a list.
            // ** If you want to trim each entry, you can just
            // ** replace in the original array.
            for (int i = 0; i < ids.Length; i++)
            {
                ids[i] = ids[i].Trim();
                // ** Add a check here to make sure id is safe
                // ** to prevent SQL injection.
                if (ids[i].IndexOfAny( invalid ) != -1)
                {
                     return; // should give error, probably.
                }
            }

// *** figure out if it is job_number or jobnumber or (unlikely) job number
            str3 += " WHERE [job_number] IN ('" + string.Join("','", ids) + "')";
        }
        else {
            return; // empty string. Should give error.
        }

        // ** OPEN FIRST
        vid.Open();
        SqlCommand xp4 = new SqlCommand(str3, vid);


        // ** @search not needed 
        // xp4.Parameters.Add("@search", SqlDbType.VarChar).Value = TextBox1.Text;
        

        xp4.ExecuteNonQuery();
      
        vid.Close();

        string message = "This job has been marked complete!";
        string script = "window.onload = function(){ alert('";
        script += message;
        script += "')};";
        ClientScript.RegisterStartupScript(this.GetType(), "SuccessMessage", script, true);

    }

Now, if the IDs are not supposed to be numeric, you have a little more difficulty preventing SQL injection.现在,如果 ID 不应该是数字,则防止 SQL 注入会有点困难。 You'd want to create a parameter for each one (@1, @2, @3, etc...) and add each as a parameter.您希望为每个参数(@1、@2、@3 等)创建一个参数并将每个参数添加为参数。

Or, of course, you could run a separate SQL statement for each ID.或者,当然,您可以为每个 ID 运行单独的 SQL 语句。 Not super efficient but probably fast enough.不是超级高效但可能足够快。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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