• 如何自动拼接 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();
        }
    }

    演示示例:下载

  • 相关阅读:
    【Angular】笔记(2):从前端基本知识认识Angular
    【Angular】笔记(1):使用.NET Framework类比认识Angular
    【C#】笔记(2):哈希表
    【WinForm】杂记(6):C#之DataTable类(总结)
    【C#】笔记(1):科学计算
    【WinForm】杂记(5):C#导入Excel到DataTable
    【WinForm】杂记(4):C#编写和调用exe程序(带参数)
    从mixin到new和prototype:Javascript原型机制详解
    亲身体验函数的柯里化
    简单理解jsonp原理
  • 原文地址:https://www.cnblogs.com/liuzhengdao/p/5705468.html
Copyright © 2020-2023  润新知