当前位置: 代码迷 >> C# >> 小弟我这种情况,需要对sql数据表加锁吗?B/S结构
  详细解决方案

小弟我这种情况,需要对sql数据表加锁吗?B/S结构

热度:331   发布时间:2016-04-28 08:37:52.0
我这种情况,需要对sql数据表加锁吗?B/S结构
向表插入一条数据,随后读取出MainID的值。表结构及数据如下图

因为这个MainID是表自动增加的,主键。这个主键的值要在别的表里做外键
代码:
sqlconnection sqlConn=new sqlconnection(……
sqlConn.open();
…… 
……insert into TableName(……  //插入一条记录
……select top 1 MainID from TabelName order by MainID DESC    //读取上面代码插入的MainID值
……
sqlConn.Close();
我本意是:刚刚插入的记录,处于表的最下边,MainID是最大的,“马上”读取出来。插入一条数据,到读取,这极短时间内,会有别的用户插入新信息吗?那样数据就不一致了。
因为写入到读取,处于sqlConn.open()和sqlConn.Close()之间,对于sql来讲,这是一个完整的会话,似乎不需要在写入后对表加锁,保证读取正确的数据。但不敢肯定

------解决思路----------------------
用@@idendity。

详见:https://msdn.microsoft.com/zh-cn/library/ms187342.aspx
------解决思路----------------------
你的意思是不是问“这两条语句是否需要在一个数据库事务(DbTransaction)中完成?”

就算是在同一个事务中完成,一个数据库会并发处理成百上千的任务,在一个会话insert一条语句以及select一条记录的同时,当然会有其它成百上千的会话操作,这个表也一定会被修改的(否则,现在已经很慢、很不适合互联网时代的关系数据库,就更慢了)。所以你的语句肯定不能保证读取的是最后产生的 MainID。

另外,即使写 select @@idendity,甚至即使并没有任何其它并发会话,那么这个其实也是不可靠的。因为假设将来你的数据库有了触发器,那么这个 select 出来的就是触发器所引起的最后的改变的递增值(你完全可以自己写一个测试来重现这个结果),而并不能保证一定就是你刚刚插入的记录的值。这是很多人会犯的一个小错误。

基本上,“插入一条记录,让它产生一个自增值,然后再读取这个值”本身的做法就是一个误区,自增值根本不是干这个用的。比如说你记录一个“用户取消订单”的行为日志,你在业务上并没有主键可定义,就可以用这个方式定义一个主键。这个时候在业务上就根本不需要搞什么“插入日志,然后再读出主键”这种奇怪的事儿!插入记录之后根本不再去查询它一遍。

如果你要强烈地控制主键,那么你应该自定义主键。例如你的应用系统用一个独立的机制来确保产生业务流水号,这就跟“自增值”毫无关系。或者你的应用应该定义一个 guid 值作为主键。不论哪一种方式,你的应用系统自己填写主键值,这样当你 insert 一条记录之后,你也还是用不着再去查询它一遍。
------解决思路----------------------
@@idendity在高并发的时候会有bug
sql = "insert into TableName(column...) output inserted.MainID values(value...); select SCOPE_IDENTITY()   as   MainID   from TableName";
var sqlCmd = new ..(sql, conn);
sqlCmd.AddParameterWithValue(...);
sqlCmd.AddParameterWithValue(...);
var MainID  = (int)sqlCmd.ExecuteScalar();
------解决思路----------------------
引用:
Quote: 引用:

如果是sql2005及以上,用output inserted.MainID不需要加锁,不会出现读取id出错的问题.
你意思是说,插入一条记录,马上读取,确定这期间不会有新纪录插入?

期间不能确定是否有新纪录的插入,但是能确定的是output inserted.MainID返回的是当前的自动增长的值,不会出现你担心的问题.
  相关解决方案