如何自動拼接 Update語句,僅Update已修改的字段


我們通常使用update語句更新數據庫記錄,例如使用update user set username='001', nickname='Tom', age=18 where id = 1語句更新username、nickname或age字段的值。假設,我們只修改了username,並沒有修改nickname和age,那么上面的 sql就顯得多余了,改成update user set username='001' where id = 1才算完美,即哪些字段發生了變化就更新哪些字段。

此外,SQL Server數據庫中有觸發器,可監控到字段值的變更,例如在表user上創建觸發器

create trigger [dbo].[tr_user_update]
on [dbo].[user]
After
update
as
declare @id int;
select @id = id from inserted;
if update(nickname) begin
    --some code
end;

如果使用update user set username='001', nickname='Tom', age=18 where id = 1語句,即便nickname和age的值與數據庫中完全一樣,也會觸發 some code,但這並不是我們期望的。

所以執行update更新前,有必要檢查哪些字段需發生了修改,尤其是需要記錄表變更歷史的情形。本例中,筆者使用System.Reflection.PropertyInfo和DataRow檢查發生更新的字段,並拼接要更新的Update SQL語句。

首先,按照表user創建User.cs類

class User
{
    // 參照用戶表User的字段定義屬性
    // 不包括系統字段
    // 僅包括用戶會修改的字段
    // 屬性名必須和字段名一致
    public string UserName { get; set; }
    public string NickName { get; set; }
    public string Password { get; set; }
    public string Email { get; set; }
    public string Phone { get; set; }
    public int Age { get; set; }
}

其次,創建賦值函數InitEntity(DataRow, Obj)

public static Obj InitEntity<Obj>(System.Data.DataRow row, Obj entity) where Obj : new()
{
    if (entity == null)
        entity = new Obj();
    // 取得entity的類型
    Type type = typeof(Obj);
    // 取得entity的屬性
    System.Reflection.PropertyInfo[] props = type.GetProperties();
    // 遍歷entity屬性集合,按照屬性類型給其賦值。通過entity屬性名從row中取得對應的值。
    foreach (System.Reflection.PropertyInfo prop in props)
    {
        if (prop.PropertyType.FullName.Equals("System.Int32"))
        {
            prop.SetValue(entity
                , Convert.ChangeType(MyFuncLib.dtv(row, prop.Name, "0")
                , prop.PropertyType), null);
        }
        else if (prop.PropertyType.FullName.Equals("System.Decimal"))
        {
            prop.SetValue(entity
                , Convert.ChangeType(MyFuncLib.dtv(row, prop.Name, "0")
                , prop.PropertyType), null);
        }
        else if (prop.PropertyType.FullName.Equals("System.Double"))
        {
            prop.SetValue(entity
                , Convert.ChangeType(MyFuncLib.dtv(row, prop.Name, "0")
                , prop.PropertyType), null);
        }
        else if (prop.PropertyType.FullName.Equals("System.Boolean"))
        {
            prop.SetValue(entity
                , Convert.ChangeType(MyFuncLib.dtv(row, prop.Name, "false")
                , prop.PropertyType), null);
        }
        else if (prop.PropertyType.FullName.Equals("System.DateTime"))
        {
            if (MyFuncLib.dtv(row, prop.Name, null) != null)
            {
                prop.SetValue(entity
                    , Convert.ChangeType(MyFuncLib.dtv(row, prop.Name, null)
                    , prop.PropertyType), null);
            }
        }
        else
        {
            prop.SetValue(entity
                , MyFuncLib.dtv(row, prop.Name, string.Empty), null);
        }
    }
    return entity;
}

顯示用戶數據時,將數據保存在一個DataTable dt中

private void Form1_Load(object sender, EventArgs e)
{
    // 初始化表時,讀取數據
    SqlConnection conn = new SqlConnection();
    conn.ConnectionString = "";
    conn.Open();
    SqlDataAdapter adapter = new SqlDataAdapter();
    adapter.SelectCommand.CommandText = "select * from user where id = 1";
    adapter.Fill(dt);
    adapter = null;
    conn.Close();
}

修改數據后,將變更存入dt的第一條記錄newRow中。保存數據前從數據庫中讀取記錄存入oldRow,然后比較oldRow和newRow差異,遇到差異時拼接Update SQL語句。

private void btnSave_Click(object sender, EventArgs e)
{
    // 理論上只有一條記錄值
    if (dt.Rows.Count > 0)
    {
        // 模擬數據修改,直接修改dt.Rows[0]
        #region update row
        dt.Rows[0]["UserName"] = "001";
        dt.Rows[0]["NickName"] = "Tom";
        dt.Rows[0]["Password"] = "123456";
        #endregion

        // 打開數據庫
        SqlConnection conn = new SqlConnection();
        conn.ConnectionString = "";
        conn.Open();

        // 修改前讀取數據庫中的記錄
        DataTable dtTemp = new DataTable();
        SqlDataAdapter adapter = new SqlDataAdapter();
        adapter.SelectCommand.CommandText = "select * from user where id = 1";
        adapter.Fill(dtTemp);
        DataRow oldRow = dtTemp.Rows[0];
        adapter = null;
        // 當前數據庫中的值
        User oldItem = MyFuncLib.InitEntity(oldRow, new User());
        // 可能已經發生修改的值
        User newItem = MyFuncLib.InitEntity(dt.Rows[0], new User());

        // 標識當前記錄是否發生了修改
        bool amended = false;
        // Update Sql
        StringBuilder sql = new StringBuilder();
        sql.AppendLine("update user set modifiedDate = getDate()");

        // 定義Update Command
        SqlCommand comdUpdate = new SqlCommand();
        // 遍歷User類屬性
        System.Reflection.PropertyInfo[] props = typeof(User).GetProperties();
        foreach (System.Reflection.PropertyInfo prop in props)
        {
            // 排除id等系統字段
            if (!prop.Name.Equals("id"))
            {
                // 僅當值發生修改時才拼接SQL語句
                if (!prop.GetValue(oldItem, null).Equals(prop.GetValue(newItem, null)))
                {
                    // 拼接Update語句
                    sql.AppendLine(string.Format(",[{0}] = @{0}", prop.Name));
                    // 同時添加參數
                    comdUpdate.Parameters.AddWithValue(
                        string.Format("@{0}", prop.Name)
                        , prop.GetValue(newItem, null).ToString());
                    // 只要有一個字段值發生了變化,就設置amended = true
                    amended = true;
                    // 此處可插入日志代碼,用於對當前表變更歷史的記錄
                }
            }
        }
        if (amended)
        {
            // 執行拼接的Update Sql
            comdUpdate.CommandText = sql.ToString();
            comdUpdate.Connection = conn;
            comdUpdate.ExecuteNonQuery();
        }
        // 關閉SQL連接
        conn.Close();
    }
}

演示示例:下載


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM