簡體   English   中英

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

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

只是想讓你們知道我不是程序員@all 但有時我可以設法讓代碼工作但我現在很難過。 我有一個 c# 基於 Web 的項目,我終於想出了如何在單擊搜索時從單個文本框傳遞逗號分隔值以在 Datagrid 中返回結果。 代碼如下所示:

    {
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();

    }

現在我有第二個按鈕,當他們按下“標記完成”時,我想更新一個字段。 它適用於單個條目,這是代碼:

  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);

    }

問題是它只適用於一個值。 我已經嘗試了一百萬種不同的方法,但沒有任何效果,主要是因為我不知道自己在做什么。 這是我最接近的(我添加了第三個按鈕,因為我不想破壞適用於單個值的按鈕 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);

    }

它不起作用。 它僅在我有一個條目時更新。 我的問題是如何在更新語句中使用該文本框中的相同值? 我希望我說得很清楚,但會盡力回答任何問題,但請記住,在編程方面,我的詞匯量非常有限。 謝謝!

明智的做法是將代碼分成更小的部分,並按它們的角色和職責對它們進行分組。 這將使事情更容易維護和使用。

如果您看到重復的代碼,那么這是將其移至單獨方法的標志。

我在您的代碼中發現的一些注意事項:

  • 您應該盡可能使用using子句,例如將它與SqlConnectionSqlCommandSqlDataAdapter一起使用。
  • 使用了多個不需要的SqlCommand
  • 在查詢沒有任何參數時提供SqlParamter
  • 使用字符串連接不是最優的,而是使用StringBuilder
  • 您的業務邏輯應該有自己的方法或類,並且可以在任何事件中調用。
  • Split返回Array ,因此無需將Array轉換為另一個Array
  • 始終,代碼的每個部分都應該僅限於該部分角色。 比如OnClick事件,應該只處理點擊事件,不處理更新記錄。 更新部分應該在事件外部聲明和處理,您只能從事件內部調用它。

我已經更新了代碼,這將使您更好地了解上述注釋:

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!");
}

好的,首先,我們在這里寫了太多代碼。

接下來,我們可以肯定地擁有一個參數列表,它們可以是可選的,並且我們永遠不會將用戶輸入連接到 SQL 字符串中——我們不必這樣做。

現在,我沒有用戶數據,讓我們這樣做:

我可以輸入一個或幾個城市。 我會顯示一些酒店,然后一個按鈕來確認酒店並用確認日期更新數據庫。

(與您正在做的一樣)。

好的,所以有一個文本框和一個按鈕。 像這樣:

    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" />

因此,您可以輸入一個或多個城市(中間有一個)。 與您需要/正在做的一樣。

好的,所以當您輸入並點擊搜索時,我們會將結果發送到網格視圖。

所以,我們有這個按鈕代碼:

    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();
        }
    }

請注意我如何保留/擁有 data.table - 我將其保留在 class 級別。

所以,我們的 output 現在是這個

在此處輸入圖像描述

好的,所以我們現在有參數問題了。

現在,您所要做的就是選中要批准的那些,然后將數據連同批准的訪問日期一起發送回表格。

該代碼現在變得非常簡單。

我們可以使用這個:

    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);
            }
        }
    }

因此,請注意我們如何將網格選擇發送回表,然后將表發送回數據庫。 這使得整個過程變得簡單,代碼也少得多。

我可能會在 Row 綁定事件中添加 2-3 行代碼,如果該行已經有日期,則設置復選框。 我什至可以假設我們是否取消選中復選框,然后再次在日期列行中顯示 null。

使用上述方法,所有這些都非常容易。

因此,按照上面的方式保留表格 - 這真的很容易。 並注意數據鍵設置——我們可以使用/獲取/獲取每一行的主鍵,而不必顯示它——但這個例子無論如何都不需要這種能力。

我在上面使用的標記是這樣的:

    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>

感謝大家的幫助。 實際是我的 aspx 代碼指向 OnClick="Button2_Click" 而不是 OnClick="Button3_Click"。 一旦我修復了它並放入原始代碼,它就可以工作了。 話雖如此,你們已經指出了一些我要嘗試修復的要點。 再說一遍,我不是程序員,非常感謝你們的幫助。 謝謝。

首先,盡你所能。 我認為唯一剩下的只是一個簡單的錯誤。 我會提供這個刪除一些你不需要的代碼

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);

    }

現在,如果 ID 不應該是數字,則防止 SQL 注入會有點困難。 您希望為每個參數(@1、@2、@3 等)創建一個參數並將每個參數添加為參數。

或者,當然,您可以為每個 ID 運行單獨的 SQL 語句。 不是超級高效但可能足夠快。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM