SQL 如何在update 时保证唯一性?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SQL 如何在update 时保证唯一性?相关的知识,希望对你有一定的参考价值。

假设有一数据表:员工
有字段:姓名(主键)、性别、年龄、手机号
如何能在update时保存姓名、手机号的唯一性?
SQL中有主键约束、唯一性约束,但在违反约束时,抛出的错误信息非常不友好。
所以就想在前台判断唯一性。
在insert记录时比较简单可以直接先select 姓名,看看这个姓名是否存在。然后再insert新记录。
但在update时好像不行,因为update之前,数据表中肯定已经有这个姓名的人,如果select这个姓名肯定是存在的,这时会被误认为是重复了?
怎么办?

你可以设置姓名,手机号字段分别都是unique。
你要更新信息,就直接更新了呀,他会找到你所要更新的那一行,然后更改信息。不懂你的问题。。
一般如果进行更新操作担心数据库中各表之间出现不同步问题,这个一开始在建表的时候就设置主键外键来控制。追问

姓名和手机号分别设唯一性约束是非常方便,但是在插入和更新时如果违反了约束,弹出的信息非常不友好,比如:
如果违反了姓名唯一性,SQL抛出的错误提示大概是:值***违反了约束“CK_姓名”,列姓名不能为重复的值。
而我想要的提示是:您输入的用户已存在。请重新输入。
所以要么在前台判断,要么解决SQL提示不友好的问题。

参考技术A 如果你select 时加 入 where Name='姓名' and PHONE='手机号'
如果存在的话,就UPDATE
如果不存在就INSERT……
另外 要考虑到重名的存在,但电话一般不会重,姓名与电话同时相同 的更是不可能。1!!追问

这种方式我也想过,也是不对的。
select where Name='姓名' and PHONE='手机号'
如果我本来是想改手机号,那上面这种语句就不会有查询结果,那不能当insert插入呀。我只想更新手机号。

追答

如果你只想改手机号,请问你插入的信息是有效信息吗?没有姓名的手机号?你改了起到什么效果?

追问

客户端是有一个界面的:上面以textbox控件列出了姓名、手机号、性别等信息,只有一个“保存”按钮。这些信息一同使用了数据绑定。要更新肯定是一起更新的。
比如界面是这样:

姓名:张三
性别:男
手机号:135

我修改成:
姓名:张三
性别:男
手机号:136 然后保存

这时怎么避免提示姓名重复?

追答

姓名为什么不能重复?你如何处理公司里两个张三?
在读取的时候弄一个GRID,让你选不就行了?
如果GRID是空的,就INSERT,如果GRID里有记录,选择一个更新吧……

本回答被提问者和网友采纳
参考技术B 可以

如何查看MySQL的binlog数据

binlog,即二进制日志,它记录了数据库上的所有改变.
改变数据库的SQL语句执行结束时,将在binlog的末尾写入一条记录,同时通知语句解析器,语句执行完毕.
binlog格式
基于语句,无法保证所有语句都在从库执行成功,比如update ... limit 1;
基于行,将每一次改动记为binlog中的一行.在执行一个特别复杂的update或者delete操作时,基于行的格式会有优势.
参考技术A 当启动Binlog后,事务会产生Binlog Event,这些Event被看做事务数据的一部分。因此要保证事务的Binlog Event和InnoDB引擎中的数据的一致性。所以带Binlog的CrashSafe要求MySQL宕机重启后能够保证:

- 所有已经提交的事务的数据仍然存在。

- 所有没有提交的事务的数据自动回滚。

- 所有已经提交了的事务的Binlog Event也仍然存在。

- 所有没有提交事务没有记录Binlog Event。

这些要求很好理解,如果重启后数据还在,但是Binlog Event没有了,就没办法复制到其他节点上了。如果重启后,数据没了,但是Binlog Event还在,那么不存在的数据就会被复制到其他节点上,从而导致主从的不一致。

为了保证带Binlog的CrashSafe,MySQL内部使用的两阶段提交(Two Phase Commit)。

2 - MySQL的Two Phase Commit(2PC)

在开启Binlog后,MySQL内部会自动将普通事务当做一个XA事务来处理:
- 自动为每个事务分配一个唯一的ID
- COMMIT会被自动的分成Prepare和Commit两个阶段。
- Binlog会被当做事务协调者(Transaction Coordinator),Binlog Event会被当做协调者日志。
想了解2PC,可以参考文档:【https://en.wikipedia.org/wiki/Two-phase_commit_protocol。】

- 分布式事务ID(XID)

使用2PC时,MySQL会自动的为每一个事务分配一个ID,叫XID。XID是唯一的,每个事务的XID都不相同。XID会分别被Binlog和InnoDB记入日志中,供恢复时使用。MySQ内部的XID由三部分组成:

- 前缀部分

前缀部分是字符串"MySQLXid"

- Server ID部分

当前MySQL的server_id
- query_id部分

为了保证XID的的唯一性,数字部分使用了query_id。MySQL内部会自动的为每一个语句分配一个query_id,全局唯一。

参考代码:sql/xa。h的struct xid_t结构。

- 事务的协调者Binlog

Binlog在2PC中充当了事务的协调者(Transaction Coordinator)。由Binlog来通知InnoDB引擎来执行prepare,commit或者rollback的步骤。事务提交的整个过程如下:

1. 协调者准备阶段(Prepare Phase)

告诉引擎做Prepare,InnoDB更改事务状态,并将Redo Log刷入磁盘。

2. 协调者提交阶段(Commit Phase)

2.1 记录协调者日志,即Binlog日志。

2.2 告诉引擎做commit。
注意:记录Binlog是在InnoDB引擎Prepare(即Redo Log写入磁盘)之后,这点至关重要。

在MySQ的代码中将协调者叫做tc_log。在MySQL启动时,tc_log将被初始化为mysql_bin_log对象。参考sql/binlog.cc中的init_server_components():
if (opt_bin_log) tc_log= &mysql_bin_log;

而在事务提交时,会依次执行:
tc_log->prepare();
tc_log->commit();
参考代码:sql/binlog.cc中的ha_commit_trans()。当mysql_bin_log是tc_log时,prepare和commit的代码在sql/binlog.cc中:

MYSQL_BIN_LOG::prepare();
MYSQL_BIN_LOG::commit();

-协调者日志Xid_log_event
作为协调者,Binlog需要将事务的XID记入日志,供恢复时使用。Xid_log_event有以下几个特点:
- 仅记录query_id
因为前缀部分不变,server_id已经记录在Event Header中,Xid_log_event中只记录query_id部分。
- 标志事务的结束

在Binlog中相当于一个事务的COMMIT语句。

一个事务在Binlog中看起来时这样的:
Query_log_event("BEGIN");DML产生的events; Xid_log_event;

- DDL没有BEGIN,也没有Xid_log_event 。
- 仅InnoDB的DML会产生Xid_log_event
因为MyISAM不支持2PC所以不能用Xid_log_event ,但会有COMMIT Event。
Query_log_event("BEGIN");DML产生的events;Query_log_event("COMMIT");

问题:Query_log_event("COMMIT")和Xid_log_event 有不同的影响吗?

- Xid_log_event 中的Xid可以帮助master实现CrashSafe。
- Slave的CrashSafe不依赖Xid_log_event
事务在Slave上重做时,会重新产生XID。所以Slave服务器的CrashSafe并不依赖于Xid_log_event 。Xid_log_event 和Query_log_event("COMMIT"),只是作为事务的结尾,告诉Slave Applier去提交这个事务。因此二者在Slave上的影响是一样的。

3 - 恢复(Recovery)
这个机制是如何保证MySQL的CrashSafe的呢,我们来分析一下。这里我们假设用户设置了以下参数来保证可靠性:

- 恢复前事务的状态
在恢复开始前事务有以下几种状态:
- InnoDB中已经提交
根据前面2PC的过程,可知Binlog中也一定记录了该事务的的Events。所以这种事务是一致的不需要处理。
- InnoDB中是prepared状态,Binlog中有该事务的Events。
需要通知InnoDB提交这些事务。
- InnoDB中是prepared状态,Binlog中没有该事务的Events。
因为Binlog还没记录,需要通知InnoDB回滚这些事务。
- Before InnoDB Prepare
事务可能还没执行完,因此InnoDB中的状态还没有prepare。根据2PC的过程,Binlog中也没有该事务的events。 需要通知InnoDB回滚这些事务。

- 恢复过程
从上面的事务状态可以看出:恢复时事务要提交还是回滚,是由Binlog来决定的。
- 事务的Xid_log_event 存在,就要提交。
- 事务的Xid_log_event 不存在,就要回滚。

恢复的过程非常简单:
- 从Binlog中读出所有的Xid_log_event
- 告诉InnoDB提交这些XID的事务
- InnoDB回滚其它的事务

以上是关于SQL 如何在update 时保证唯一性?的主要内容,如果未能解决你的问题,请参考以下文章

sql在update更新时如何快速且大批量的更新数据

sql在update更新时如何快速且大批量的更新数据(C#中写的)

如何在SQL Server 2005中回滚UPDATE查询?

mysql分库分表,而且要保证每条数据唯一

大量用户同时进行用户注册时,如何保证用户名唯一

使用UPDATE时如何防止SQL数据库被锁定?