本文是参考阿里的Java编码规范修改的C#版本,自整理并编写,欢迎指正!
-
编程规约
(一)命名规约
1.【强制】代码中当且仅当私有成员可以使用下划线开始
反例:public string _name
2.【强制】代码中的命名严禁使用拼音与英文混合的方式,更不能允许直接使用中文的方式。
说明:正确的英文拼写和语法,可以让读者易于理解,避免歧义。注意,即使纯拼音命名方式也要避免采用。
反例:IsPiLiang [是否批量操作] / Kase [卡色] / numLing [领用数量]
3.【强制】类名、类的属性、方法名、命名空间使用UpperCamelCase大写驼峰 风格,英文单词首字母大写,必须遵从驼峰形式,但以下情形例外(领域模型的相关明明)CEO / DBO 等。
正例:SysuserController / ItemInfo / TcpHelper / GetInfo()
反例:sysuserController / Iteminfo / TCPHelper / getInfo()
4.【强制】参数名、成员变量、局部变量都统一使用lowerCamelCase 小驼峰风格,除首单词外其他单词首字母大写,必须遵从驼峰形式。
正例:localCache / userList
5.【强制】常量命名全部大写,单词间用下划线隔开,力求语意表达完整清楚,不要嫌名字长。
正例:MAX_STOCK_COUNT
反例:MAX_COUNT
6.【强制】抽象类命名使用 Abstract或Base开头;异常类命名使用Exception结尾;测试类命名以它要测试的类名称开始,以Test结尾。
7.【强制】杜绝完全不规范的缩写,避免望文不知义。
反例:AbstractClass“缩写”命名成AbsClass,condition“缩写”成condi,此类随意缩写严重降低了代码的可阅读性。
8.【推荐】如果使用了设计模式,建议在类名中体现出具体模式。
说明:将设计模式体现在名字中,有利于阅读者快速理解架构设计思想。
正例:public class SysuserController
Public class OrderFactory
Public class TcpProxy
9.【推荐】类中的声明、方法和属性加上有效的Summery注释。
正例:
/// <summary>
/// 该类是用于对用户的控制器操作类
/// </summary>
public class SysuserController
{
/// <summary>
/// 用户名
/// </summary>
public string name;
/// <summary>
/// 获得用户名
/// </summary>
Public string getName()
{
// return this.name;
}
}
10.【参考】枚举类名建议带上E前缀或Enum后缀,枚举成员名称需要全大写,单词间用下滑线隔开。
说明:枚举其实就是特殊的常量类i,切构造方法被默认强制是私有。
正例:枚举名字:EState / DealStatusEnum,成员名:SUCCESS / UNKOWN_REASON
(二)常量定义
1.【强制】不允许出现任何魔幻值(即未经定义的常量)直接出现在代码中。
反例:string key = “cacheKey_” + user.Name;
正例:string keyPrefix = “cacheKey_”;
string key = keyPrefix + user.Name;
2.【推荐】不要使用一个常量类,来维护所有的常量,应该按常量的功能进行归类,分开维护,或交由具体的业务类中定义常量。如:缓存相关的常量放在类:CacheConstant中。系统配置相关的常量放在类:SysConfigConstant中。
说明:大而全的常量类,非得使用查找功能才能定位到修改的常量,不利于理解和维护。
(三)格式规约
1.【强制】大括号的使用约定。如果大括号内为空,则简介的写成 { } 即可,不需要换行;如果是非空代码块,则左括号和右括号各占一行,内容块另起一行。
正例:
public string getName() { }
public string getName()
{
return this.Name;
}
反例:
public string getName() {
return this.Name;
}
2.【强制】if / for / while / switch / do 等保留字与左右括号之间都必须加空格。
3.【强制】任何运算符左右必须加一个空格。
说明:运算符包括赋值运算符 = 、逻辑运算符&&、加减乘除符号、三目运算符等。
4.【强制】缩进采用4个空格,尽量不要使用tab字符。
说明:根据vs设定,tab默认已是4个空格,不需要做变动。
正例:
public static void main(string[] args)
{
// 缩进4个空格
string name = “Jack”;
// 运算符的所有必须空一格
bool isMe = true;
// 关键字if与括号之间必须有一格空格,括号内的方法体不需要空格
if (isMe == true)
{
Console.WriteLine(“my name is ” + name);
}
else
{
Console.WriteLine(“your name is ” + name);
}
}
5.【推荐】单行字符数限制不超过120个,超出徐换行,换行时遵守如下原则:
1)第二行相对第一行缩进4个空格,从第三行开始不再继续缩进,参考示例。
2)运算符与下文一起换行。
3)方法调用的点符号与下文一起。
4)在多个参数超长,都好后进行换行。
5)在括号前不要换行,见反例。
正例:
// 超过120个字符的情况下,换行缩进4个空格,并且方法钱的点符号一起换行
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.UseApplicationInsights()
.Build();
反例:
// 超过120个字符,不要再括号前换行
var host = new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>
().UseApplicationInsights()
.Build();
// 参数很多的方法调用可能超多120个字符,不要在逗号钱换行
Method(args1, args2, args3, args4);
6.【强制】方法参数在定义和传入时,多个参数逗号后必须加空格。
正例:下例中实参的”a”,后边必须要有一个空格。
Method(“a”, “b”, “c”);
7.【推荐】没有必要增加若干空格来使某一行相应的字符对其。
正例:
int a = 3;
long b = 4;
string str = “hello world”;
string str = “hello world”;
说明:增加str这个变量,如果对其,则给a、b都要增加几个空格,在变量比较多的情况下,是一种累赘的事情。
(四)OOP面向对象规约
1.【强制】避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成本,直接用类名来访问即可。
2.【强制】相同的参数类型,相同的业务含义,才可以使用可变参数,避免使用Object
说明:可变参数必须放置在参数列表的最后。
正例:public void sendMessage (string msgContent, params string[] userList)
3.【强制】不能使用过时的类或方法([Obsolate]标识)
说明:C#中对于标记过时的方法,有可能会在新版本的.Net Framework中剔除,因此不建议继续使用此类或方法。
4.【强制】Object 的Equals方法容易抛空引用异常,应使用常量或确定有值得对象来调用Equals。
正例:”Jack”.Equals(user);
反例:user.Equals(“Jack”);
5.【强制】构造方法中禁止加入业务逻辑,如有初始化逻辑等,请放在Init() 方法中。
7.【强制】帮助类或业务接口类,应该使用静态方法定义接口,使用类名.方法名直接调用,避免对象声明。
正例:Helper.ToString();
反例:Helper.Instance.ToString();
7.【推荐】当一个类有多个构造方法,或多个同名方法,这些方法应该按照顺序放置在一起,便于阅读。
8.【推荐】类内方法定义顺序依次是:常量、字段、属性、方法,按照public -> protected -> private 排序。
说明:共有方法是类的调用者和维护者最关心的方法,首屏展示最好;保护方法虽然只是子类关心,也可能是“模板设计模式”下的和新方法;二私有方法一般不需要特别关心,是一个黑盒实现,因此方法信息价值较低。
9.【推荐】循环体内,字符串的拼接方式,使用StringBulder的Append或AppendFormat方法进行扩展。
反例:
string str = “start ”;
for (int i = 0; i < 100; i++)
{
str = str + “hello ”;
}
说明:反编译出的字节码文件显示,每次循环都会new出一个StringBuilder对象,然后进行Append操作,最后通过ToString方法返回string对象,造成内存资源浪费。
10.【推荐】类成员与方法访问控制从严:
1)如果不允许外部直接通过new来创建对象,那么构造方法必须是private。
2)工具类不允许有public或default构造方法。
3)类非static成员变量并且与子类共享,必须是protected。
4)类非static成员变量并且仅在本类使用,必须是private。
5)类static成员变量如果仅在本类使用,必须是private。
6)类成员方法只供内部调用,必须是private。
7)类成员方法只对继承类公开,那么限制为protected。
说明:任何类、方法、参数、变量,严控访问范围,过宽泛的访问范围,不利于模块解耦。思考:如果一个private的方法,想删除就删除,可是一个public的Service方法,或者一个public的成员变量,删除一下,不得手心冒汗吗?变量像自己的小孩,尽量在自己的时限内,变量作用域太大,如果无限制的到处跑,那么你会担心的。
(五)控制语句
1.【强制】在一个switch块内,每个case要么通过break/return等来终止,要么注释说明程序将继续执行到哪一个case为止:在一个switch块内,都必须包含一个default语句,并且放在最后,即使它什么代码都没有。
2.【强制】在 if / else / for / while / do 语句中都必须使用大括号,即使只有一行代码,避免使用下面的形式: if (condition) do something...
3.【推荐】尽量少使用else,if-else的方式可以改写为:
if (condition)
{
... Do something;
return obj;
}
4.【推荐】除常用方法(如GetXXX / IsXXX)外,不要再条件判断中执行其他复杂的语句,将复杂逻辑判断的结果赋值给一个有意义的bool变量,以提高可读性。
说明:很多id语句内的逻辑香坊复杂,阅读者需要分析条件表达式的最终结果,才能明确什么样的条件执行什么样的语句,那么如果阅读者分析逻辑表达式错误呢?
正例:
// 伪代码如下:
bool exsisted = file.Exsist() && obj != null && ...;
If (exsisted)
{
...
}
反例:
if (file.Exsist() && obj != null && ...)
5.【推荐】循环体内的语句要考虑性能,以下操作尽量移至循环体外处理,如定义对象、变量、获取数据库连接,进行不必要的 try-catch操作(这个try-catch是否可以移至循环体外)。
6.【参考】方法中需要进行参数校验的场景:
1)调用频次低的方法。
2)执行时间开销很大的方法,参数教研室间几乎可以忽略不计,但如果因为参数错误导致中间执行回退,或者错误,那么得不偿失。
3)需要极高稳定性和可用性的方法。
4)对外提供的开放接口,不管是Api还是Http接口。
5)敏感权限入口。
7.【参考】方法中不需要参数校验的场景:
1)极有可能呗循环调用的方法,不建议对参数进行校验。但在方法说明里必须注明外部参数检查要求低。
2)底层的方法调用频度都比较高,一般不校验。毕竟是像纯净水过滤的最后一道,参数错误不太可能到底层才会暴露问题。
3)被声明成private只会被自己代码所调用的方法,如果能够确定调用方法的代码传入参数已经做过交叉或肯定不会有问题,此时可以不做校验参数。
(六)注释规约
1.【强制】类、雷属性、类方法的注释必须使用C# Summary 规范,使用:
/// <summary>
/// ....
/// </summary>
格式,不得使用 //... 方式。
说明:在vs中,Summary方式会提示相关的注释,生成Summary可以正确输出相应的注释。工程调用方法是,不进入方法,即可悬浮提示方法、参数、返回值的意义,提高阅读效率。
2.【强制】所有的抽象方法(包括接口中的方法)必须使用Summary注释,除了返回值、参数、异常说明外,还必须指出该方法做了什么事,实现了什么功能。
说明:对于子类的实现要求,或者调用注意事项,请一并说明。
3.【强制】方法内部单行注释,在被注释语句上方另起一行,使用 // 注释。方法内部多行注释使用 /* */注释,注意与代码对齐。
4.【推荐】语气“半吊子”英文来注释,不如用中文注释把问题说清楚。但专有名字与关键字保持英文原文即可。
反例:“TCP连接超时”解释成“传输控制协议连接超时”,理解反而费脑筋。
5.【推荐】代码修改的同事,注释也要进行相应的修改,预期是参数、返回值、异常、核心逻辑等的修改。
说明:代码与注释更新不同步,就像网路与导航软件更新不同步一样,如果导航软件严重滞后,就失去了导航的意义。
6.【参考】注释掉的代码尽可能而配合说明,而不是简单的注释掉。
说明:代码被注释掉有两种可能性:1)后续会恢复此段代码逻辑。2)永久不用。前者如果没有备注信息,难以知晓注释动机。后者建议直接删掉(代码仓库保存了历史代码)。
7.【参考】对于注释的要求:第一、能够准确反应设计思想和代码逻辑;第二、能够描述业务含义,使别的程序员能够迅速了解到代码背后的信息,完全没有注释的大段代码,对于阅读者形同天书,注释是给自己看的,即使隔很长时间,也能够清晰理解当时的思路;注释也是给继任者看的,使其能够快读接替自己的工作。
8.【参考】好的命名、代码结构是自解释的,注释力求精简准确,表达到位。避免出现注释的一个极端:过多滥的注释,代码逻辑一旦修改,修改注释是相当大的负担。
反例:
// put elephant into fridge
Put(elephant, fridge);
方法名put,加上两个有意义的变量名 elephant和fridge,已经说明了这是在干什么,语义清晰的代码不需要额外的注释。
9.【参考】特殊注释标记,请注明标记人与标记时间。注意及时处理这些标记,通过标记扫描,经常清理此类标记。线上故障有时候就是来源于这些标记处的代码。
1)待办事宜(TODO):(标记人、标记时间,[预计处理时间])表示需要实现,但目前还未实现的功能。
(七)接口规约
1.【强制】业务接口的返回格式为:
{
"code": 0,
"message": "这里是返回信息",
"data": {}
}
code 标识接口返回的状态码,message 标识接口返回的信息,data 标识接口返回的数据。此三个单词均使用小写。
2.【强制】返回值code,0标识失败,1标识成功
说明:大多数系统都使用code作为接口调用成功与否的判断标志,但对于code没有统一的规范,现对于新系统发起约束,0为失败,1为成功。
-
异常日志
(一)异常处理
1.【强制】异常不要用来做流程控制,条件控制。因为异常的处理效率比条件分支低。
2.【强制】对大段代码进行try-catch,这是不负责任的表现。catch时请分清稳定代码合肥稳定代码,稳定代码指的是无论如何都不会出错的代码。对于费稳定代码的catch尽量可能的进行区分异常类型,再做对应的异常处理。
3.【强制】捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,就将该异常抛给他的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的内容。
4.【强制】有try块放到了事务代码中,catch异常后,如果要回滚事务,一定要注意手动回滚事务。
5.【强制】finally块必须对资源对象、流对象进行关闭,有异常也要做tyr-catch。
6.【强制】捕获异常与抛异常,必须是完全匹配,或者捕获异常是抛异常的父类。
说明:如果预期对方抛的是绣球,实际接到的是铅球,就会产生意外情况。
7.【推荐】方法的返回值可以是null,不强制返回空集合或空对象等,必须添加注释充分说明什么情况下会返回null值。调用方进行null判断,防止NRE空引用异常问题(NullReferenceException)。
8.【推荐】防止NRE,是程序员的基本修养,注意NRE产生的场景:
1)返回类型为包装数据类型,有可能是null,返回?类型时注意判空。
2)数据库的查询结果可能为null。
3)集合可能是非空的,但集合里的元素有可能是null。
4)级联调用 obj.GetA().GetB().GetC(),一连串调用,容易产生NRE。
9.【推荐】在代码中使用“抛异常”还是“返回错误码”,对于公司外的http/api开放接口,必须使用“错误码”,而应用内部推荐异常抛出;跨应用间的调用,推荐使用统一的返回格式和状态码规范。
10.【参考】避免出现重复的代码
说明:随意复制和黏贴代码,必然会导致代码的重复,在以后需要修改时,需要修改所有的副本,容易遗漏。必要时,抽取公共方法,或者抽象公共类,甚至是公用模块。
(二)日志规约
-
MySQL规约
(一)建表规约
1.【强制】表达是否概念的字段,必须使用is_xxx的方式命名,数据类型是bit
说明:人和字段如果为非负数,必须是unsigned。
2.【强制】表名、字符案名必须是用小写字母或数字,禁止出现数字开头、两个下划线中间值出现数字。数据库字段名的修改代价很大,因为无法进行预发布,所以字段名称需要慎重考虑。
3.【强制】表名不适用复数名词。
说明:表名应该仅仅表示表里的内容实体,不应该表示实体数量,对应于DBO类名也是单数形式,符合表达习惯。
4.【强制】禁用保留字,如desc、range、match等,请参考MySQL官方保留字。
5.【强制】主键索引名为pk_字段名;唯一索引名为uk_字段名;普通索引名则为idx_字段名。
说明:pk_即primary key,uk_即unique key;idx_即index的简称。
6.【强制】小数类型为decimal,禁止使用float和double。
说明:float和double在存储的时候,存在精度损失的问题,很可能在值得比较时,得到不正确的结果。如果存储的数据范围超过decimal的范围,建议将数据拆成证书和小数分开存储。
7.【强制】如果存储的字符串长度几乎相等,使用char定长字符串类型。
8.【强制】varchar是可变长的字符串,不预先分配存储空间,长度不要超过5000,如果存储长度大于此值,定义字段类型为text,独立出来一张表,用主键来对应,避免影响其他字段索引效率。
9.【强制】表必备单个字段:id,create_time,update_time。
说明:其中id比为主键,类型为 unsigned bigint,单表时自增、步长为1。create_time、update_time类型均为datetime类型。
10.【推荐】如果修改字段含义或对字段标识的状态追加时,需要及时更新字段注释。
11.【推荐】字段允许适当的冗余,以提高性能,但是必须考虑数据同步的情况。冗余字段应遵循:
1)不是频繁修改的字段。
2)不是varchar超长字段,更不能是text字段。
正例:商品类目名称使用频率高,字段长度短,名称基本一成不变,可在相关联的表中冗余存储类目名称,避免关联查询。
12.【推荐】单表行数超过500万行或者单表容量超过2GB,才进行分库分表。
说明:如果预计三年后的数据量根本达不到这个级别,请不要再创建表时就分库分表。
13.【参考】合适的字符存储长度,不但节约数据库表空间、借阅索引存储,更重要的是提升检索速度。
正例:无符号值可以避免误存负数,且扩大了表示范围。
(二)索引规约
1.【强制】业务上具有唯一特性的字段,即使是组合字段,也必须建成唯一索引。
说明:不要以为唯一索引印象了insert速度,这个速度损耗可以忽略,但提高查找速度是明显的。另外,即使在应用层做了非常完善的校验控制,只要没有唯一索引,根据墨菲定律,必然后脏数据产生。
2.【强制】超过三个表禁止join。需要join的字段,数据类型必须绝对一致;多表关联查询时,保证被关联的字段需要有索引。
说明:即使双表join,也要注意表索引、SQL性能。
3.【强制】在varchar字段上建索引时,必须指定索引长度,没必要对全字段建立索引。根据实际文本区分度决定索引长度即可。
说明:索引偿付与区分度是一对矛盾体。一般对字符串类型数据,长度为20的索引,,区分度或达到90%以上。可以使用count(distinct left(列名,索引长度))/count(*)的区分度来确定。
4.【强制】页面搜索严禁左模糊或全模糊,如果需要请走搜索引擎来解决。
说明:索引文件具有B-Tree的最左前缀匹配特性,如果左边的值来确定,那么无法使用此索引。
5.【推荐】如果有order by的场景,请注意利用索引的有序性。order by最后的字段是组合索引的一部分,并且放在索引组合顺序的最后,避免出现file_sort的情况,影响查询性能。
正例:where a = XXX and b = YYY order by c; 索引:a_b_c
反例:索引中有范围的查找,那么索引有序性无法利用,如:WHERE a > 10 order by b;索引 a_b 无法排序。
6.【推荐】利用覆盖索引来进行查询操作,避免回表。
说明:如果一本书知道第11章是什么标题,会翻开第11章对应的那一页吗?目录浏览一下就好,这个目录就是起到覆盖索引的所用。
正例:能够建立索引的种类:主键索引、唯一索引、普通索引,二覆盖索引是一种查询的一种效果,用explain的结果,extra列会出现using index。
7.【推荐】利用延迟关联或者子查询优化多分页场景。
说明:MySQL并不是跳过offset行,而是读取offset+N行,然后返回放弃前offset行,返回N行,那当offset特别大的时候,效率就非常低下,要么控制返回的总页数,要么对超过特定阈值的页数进行SQL改写。
正例:先快速定位需要获取的id段,然后再关联:
SELECT a.* FROM 表 1 a, (select id from 表 1 where 条件 LIMIT 100000,20 ) b where a.id=b.id
8.【推荐】SQL 性能优化的目标:至少要达到 range 级别,要求是 ref 级别,如果可以是 consts 最好。
说明:
1)consts 单表中最多只有一个匹配行(主键或者唯一索引),在优化阶段即可读取到数据。
2)ref 指的是使用普通的索引(normal index)。
3)range 对索引进行范围检索。 反例:explain 表的结果,type=index,索引物理文件全扫描,速度非常慢,这个 index 级 别比较 range 还低,与全表扫描是小巫见大巫。
9.【推荐】建组合索引的时候,区分度最高的在最左边。
正例:如果 where a= ? and b= ? ,a 列的几乎接近于唯一值,那么只需要单建 idx_a 索引即可。
说明:存在非等号和等号混合判断条件时,在建索引时,请把等号条件的列前置。如:where a > XXX and b = YYY 那么即使a的区分度更高,也必须把 b放在索引的最前列。
10.【参考】创建索引时避免有如下极端误解:
1)误认为一个查询就需要建一个索引。
2)误认为索引会消耗空间、严重拖慢更新和新增速度。
3)误认为唯一索引一律需要在应用层通过“先查后插”方式解决。
(三)SQL规约
1.【强制】不要使用count(列名)或count(常量)来替代count(*),count(*)是SQL92 定义的 标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。
说明:count(*)会统计值为NULL的行,而count(列名)不会统计此列为NULL值的行。
2.【强制】count(distinct col) 计算该列除 NULL 之外的不重复行数,注意count(distinct col1, col2)如果其中一列全为NULL,那么即使另一列有不同的值,也返回为 0。
3.【强制】当某一列的值全是NULL时,count(col)的返回结果为0,但sum(col)的返回结果为 NULL,因此使用 sum()时需注意NRE问题。
正例:可以使用如下方式来避免sum的NRE问题:SELECT IF(ISNULL(SUM(g)),0,SUM(g)) FROM table;
4.【强制】使用ISNULL()来判断是否为NULL值。
注意:NULL与任何值的直接比较都为 NULL。
说明:
1)NULL<>NULL 的返回结果是NULL,而不是false。
2)NULL=NULL 的返回结果是NULL,而不是true。
3)NULL<>1 的返回结果是NULL,而不是true。
5.【强制】在代码中写分页查询逻辑时,若count为0应直接返回,避免执行后面的分页语句。
6.【强制】不得使用外键与级联,一切外键概念必须在应用层解决。
说明:(概念解释)学生表中的student_id是主键,那么成绩表中的student_id则为外键。如果更新学生表中的student_id,同时触发成绩表中的student_id更新,则为级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。
7.【强制】禁止使用存储过程,存储过程难以调试和扩展,更没有移植性。
8.【强制】数据订正时,删除和修改记录时,要先select,避免出现误删除,确认无误才能执行更新语句。
9.【推荐】in操作能避免则避免,若实在避免不了,需要仔细评估in后边的集合元素数量,控制在1000个之内。
10.【参考】如果有全球化需要,所有的字符存储与表示,均以utf-8编码,那么字符计数方法。
说明:
SELECT LENGTH("轻松工作");返回为12
SELECT CHARACTER_LENGTH("轻松工作");返回为
如果要使用表情,那么使用utfmb4来进行存储,注意它与utf-8编码的区别。
11.【参考】TRUNCATE TABLE比DELETE速度快,且使用的系统和事务日志资源少,但TRUNCATE无事务且不触发trigger,有可能造成事故,故不建议在开发代码中使用此语句。
说明:TRUNCATE TABLE在功能上与不带 WHERE子句的DELETE语句相同。
(四)ORM(对象关系映射)规约
1.【强制】在表查询中,一律不要使用 * 作为查询的字段列表,需要哪些字段必须明确写明。
说明:1)增加查询分析器解析成本。2)增减字段容易与 resultMap 配置不一致。
2.【强制】xml 配置中参数注意:#{},#param# 不要使用${} 此种方式容易出现 SQL 注入。
3.【强制】更新数据表记录时,必须同时更新记录对应的 update_time 字段值为当前时间。
4.【推荐】不要写一个大而全的数据更新接口,不管是不是自己的目标更新字段,都进行update这是不对的。执行 SQL 时,尽量不要更新无改动的字段,一是易出错;二是效率低;
来源:https://www.cnblogs.com/lxmajs/p/6668813.html