如何自动拼接 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, "")
, prop.PropertyType), null);
}
else if (prop.PropertyType.FullName.Equals("System.Decimal"))
{
prop.SetValue(entity
, Convert.ChangeType(MyFuncLib.dtv(row, prop.Name, "")
, prop.PropertyType), null);
}
else if (prop.PropertyType.FullName.Equals("System.Double"))
{
prop.SetValue(entity
, Convert.ChangeType(MyFuncLib.dtv(row, prop.Name, "")
, 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 > )
{
// 模拟数据修改,直接修改dt.Rows[0]
#region update row
dt.Rows[]["UserName"] = "";
dt.Rows[]["NickName"] = "Tom";
dt.Rows[]["Password"] = "";
#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[];
adapter = null;
// 当前数据库中的值
User oldItem = MyFuncLib.InitEntity(oldRow, new User());
// 可能已经发生修改的值
User newItem = MyFuncLib.InitEntity(dt.Rows[], 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();
}
}
演示示例:下载
如何自动拼接 Update语句,仅Update已修改的字段的更多相关文章
- C#的自动拼接Sql语句Insert方法及思路
思路: 1.想想插入语句,大概是这样的一个框架:INSERT INTO 表名 (数据库列名) values (值) 2.这里要3个变量是不固定的,分别是:表名.数据库列名.值: a.表名我们这里很容易 ...
- 带WHERE子句的UPDATE语句
目前演示的几个UPDATE语句都是一次性更新所有行的数据,这无法满足只更新符合特定条件的行的需求,比如“将Tom 的年龄修改为12 岁”.要实现这样的功能只要使用WHERE 子句就可以了,在WHERE ...
- SQL基础语法—update语句
1 update语句介绍 update语句用来修改表中的数据内容 Single-table syntax: UPDATE [LOW_PRIORITY] [IGNORE] table_reference ...
- MySQL update语句和insert插入语句写法完全不一样啊,不要搞混
1.mysql update 语句: update user set name = 'xiaoming',age = 18 where uid = 3000; 更新记录时update操作也不需要写ta ...
- SQL Server UPDATE语句的用法详解
SQL Server UPDATE语句用于更新数据,下面就为您详细介绍SQL Server UPDATE语句语法方面的知识,希望可以让您对SQL Server UPDATE语句有更多的了解. 现实应用 ...
- Oracle 增删改(INSERT、DELETE、UPDATE)语句
Ø 简介 本文介绍 Oracle 中的增删改语句,即 INSERT.DELETE.UPDATE 语句的使用.是时候展现真正的技术了,快上车: 1. 插入数据(INSERT) 2. 修改数据( ...
- sql update语句
如果要更新数据库表中的记录,我们就必须使用UPDATE语句. UPDATE语句的基本语法是: UPDATE <表名> SET 字段1=值1, 字段2=值2, ... WHERE ...; ...
- 独特的deadlock(仅update语句也能造成死锁)
最近遇到了一个看上去很奇怪,分析起来很有意思的死锁问题.这个死锁看上去难以理解.而分析过程中,又使用了很多分析SQL Server死锁的典型方法.记录下来整个分析过程还是很有意义的. 问题重现步骤: ...
- 不恰当的update语句使用主键和索引导致mysql死锁
背景知识: 截至目前,MySQL一共向用户提供了包括DBD.HEAP.ISAM.MERGE.MyIAS.InnoDB以及Gemeni这7种Mysql表类型.其中DBD.InnoDB属于事务安全类表,而 ...
随机推荐
- utf8乱码解决方案[适合tomcat部署的jsp应用]
转:http://blog.csdn.net/cn_gaowei/article/details/6673539 1. java类: CharacterEncodingFilter im ...
- Java 判断是否为汉字 判断是否为乱码 判断字符串是否为双整型数字 整数 数字
/** * 判断是否为汉字 * * @param str * @return */ public static boolean isGBK(String str) { char[] ch ...
- SCO连接SCOM报警
当SCOM中出现红色警报时,在目标计算机上运行一个程序: 1.新建Runbook,添加一个Monitor Alert 2.设置Monitor Alert属性,选择connection,设置警报过滤条件 ...
- discuz!版本号信息改动步骤
建完网站后,就到了改动discuz! 论坛的步骤了,,将其改动为自己喜欢的样子.是非常有意思的,废话不多说了.以下给大家介绍改动的方法. 1.[改动后台-首页的版权]打开ftp.连接网站,进入到:/f ...
- Android adt v22.6.2-1085508 自己主动创建 appcompat_v7 解决方法,最低版本号2.2也不会出现
Android 开发工具升级到22.6.2在创建project时仅仅要选择的最低版本号低于4.0,就会自己主动生成一个项目appcompat_v7,没创建一个新的项目都会自己主动创建,非常是烦恼... ...
- JFinal之学习资源
JFinal官网: http://www.jfinal.com/ JFinal在线API: http://tool.oschina.net/apidocs/apidoc?api=jfinal JFin ...
- 使用Zipalign工具优化Android APK应用记录
生成的Android应用APK文件最好进行优化,因为APK包的本质是一个zip压缩文档,经过优化能使包内未压缩的数据有序的排列,从而减少应用程序运行时的内存消耗.我们可以使用Zipalign工具进行A ...
- java_泛型,设置类型通配符的上限
package ming; import java.util.ArrayList; import java.util.Collection; import java.util.List; class ...
- 关于c#中委托使用小结
一.简述: 委托对与我们编程人员来说,一点都不陌生,在实际工作过程中,或多或少都应该是接触过 但是对与编程新手来说,对与委托的理解和使用应该还是一个坎,但是只要理解清楚了,这个坎也就过去了. 最近也经 ...
- 如何使用MVP模式搭建我们的Android应用?
听到很多人在讨论MVVM,我自己早些时候也写过一篇介绍MVVM的文章(玩转Android之MVVM开发模式实战,炫酷的DataBinding!),实际上,在Android开发领域中,除了MVVM之外, ...