更新放在事务里面我懂,查询放在事务里面真心不懂。没听说过,今天第一次见识了。
“建一个表,用一个字段储存最后一次生成的序号, 每次获取新序列号时开始一个新事务,更新序列号+1,然后select出来作为新序号用。因为每次都有事务,并且是先更新再查询,可以保证不会有重复数字生成。”
这句话不知道是哪位大侠说的,反正我不敢苟同。
假设原始值ID为10,事务A,事务B执行如下
A: Update ID=ID+1
B: Update ID=ID+1
A: Select ID (这时候ID=12)
B: Select ID (ID=12)
如上证明,两个客户端得到了ID=12的序号。
补充:
不好意思,查了一下资料,没用过这么高级的属性。使用事务的最高级别确实可以做到。
TRANSACTION_SERIALIZABLE 可以防止脏读,不可重复读取和幻读,(事务串行化)会降低数据库的效率。附好园里相关文章,
没有找到降低效率的详细资料,有知道的大神帮忙扫盲一下。
继续回答楼主的问题,如果你可以接受最高级别的事务带来的效率损失的话。
后面的就简单了,跟LINQ真没有多大的关系了。
linq to sql 是支持SQL SERVER的存储过程的,
你可以这么写的
var myid=datacontext.pr_getid()
注:pr_getid为存储过程,返回ID值,在存储过程里面你可以写事务的。
请教一下:
1、如果使用事务的最高级别确实可以做到,那么当你有这个需求时你会这样做吗?效率底是低成怎样?什么样的规模才适用最高级别的隔离?
2、是否有更科学的办法?
@feng12345123: 说下我目前采用的方式吧,
也是一个数据表,保存一个数值,每次+1这样。
没有任何其他处理了(实际上就是当并发不存在....)
在没有发现因此造成的单据号重复之前,也没有打算在这上面花更多的成本去处理。
你可能觉得不好,不过这个真得看环境。
比如INTEL的CPU浮点计算问题,那是科学家算到小数点后N位才出现的,INTEL为此花了5亿还是多少?
如果你觉得你是INTEL这个级别的,那就要去研究,如果你是某个小计算器厂商,每年营业额是1千万,那就算了。
@爱编程的大叔: 我想问一下,假如我启动一个事务(TRANSACTION_SERIALIZABLE 级别)当事务中的语句还没执行完毕电脑就断电了,那么据我所知道的,事务中所做的操作应该会回滚,但是事务对数据上锁的操作会取消吗?
@feng12345123: 这个你可以试验的。首先存储过程中间可以暂停个几秒。
然后你可以有足够的时间把电脑的电源线拔掉。
这类的测评和研究其实真的很花时间和钱,所以我一向很尊敬有机会搞这些的人。
我的客户不会给我100万只为了搞这个的。所以我只知道理论知识。
我是典型的实用主义者。你看看罗质祥的视频其实你会发现,
要生存,还是要有实用主义精神的,比如你说屏幕这是,啥JDI的
你买的的最好的屏幕,假设价格500这样吧,你跟厂家说,我虽然只买10万个屏幕,
但是我每个屏幕给你5万,厂家保证屁颠屁颠的保证你的屏幕全面超越苹果的质素。
问题是你这钱从哪儿来?所以再怎么的追求美感,一旦要进入市场,
你一定只能有一些BUG,厂家做的无非是控制谁的BUG少,不影响使用而已。
利益相关,我做过外贸,研究过工厂成本。知道一点DELL和IBM的QA就怎么干活的。
只有研究所的,才可能不管工业成本。因为只是研究。
你要的就是 :“ 如何生成连续不重复的序列号 ”
不要用Linq to sql 从数据库获取,然后处理,再导入 的方法,太烂了,而且效率很低,每生成一个序列号还要在网路走一大圈。
教你一个好方法,让数据库自动生成序列号。建一张保存序列号的根表A,另建一张表B(或者是使用系统表)B中保存两个值,一个值是当前使用到的序列号B_cur,另一个值是序列号跟表的最大序列号B_max。数据库建一个触发器,每当最大序列号和当前序列号间隔差小于1000(这个可以自定义)让触发器自动在序列号跟表里补新的序列号10000个(根据最大序列号向上补),先修改B_max = B_max + 10000 , 然后循环补跟表A。建一个存储过程 返回当前序列号B_cur(如果序列号不需要返回其他值,直接返回B_cur,减少对A的索引操作),然后修改B_cur = B_cur + 1
这种自动生成序列号的方法还是很效率的。(如果你只是要序列号,A表可以不用建立,直接建立B表,里面保留两项值,开始值和当前值)
该用sql就用sql,linq只是一种查询方式,并不能满足所有业务需求,想用他来实现所有业务需求也是不现实的,
在sql上 update 字段+1 output 新值,这个语句每次执行都会有连续的值
LINQ如果你不先获得序号值,怎么更新
LINQ不能直接这样 UPDATE TABLE SET INDEX=INDEX+1
根据你新表存储编号的方案,直接如下操作即可:
直接把上下文放在using中。然后
1、查编号(编号表),2、编号+1(写业务数据),3、编号+1(编号表)
整个提交,L2S中,每次提交都是一次事务操作。
就差你的代码,谢谢!
这个问题我研究过的,这样的操作没有办法防止重复。
客户A 查ID=10,因此编号应该是11
客户B 查ID=10,因此编号也应该是11,
这时候他们两个的数据都还没有写入数据库。
甚至可能还有客户C,查到也是10。
网络上一并发考虑,事情就多了。
因为通常业务表里面的这个单据号不会在数据库中设计为唯一性。
但是客户的业务又需要唯一性的单据号,所以事务是可能成功的,但是就出现了两个或是三个同样的单据号。
当然,如果你把业务单据号弄成数据表唯一性的设计,就可以通过事务这样来处理了。
@feng12345123: 求方案
@爱编程的大叔: 对,我那种在高并发下确实不行,请考虑如下方案:可以在编号表增加时间戳字段,再执行编号表update的时候,加上这个where条件,如果受影响的行数为0,则回滚当前事务(需要开一个显式事务),并重新来一(N)次。
用触发器是解决这个问题的一个方案:
create trigger xxx on insert
as
begin
declare @id int;
select @id = id from inserted;
update table set MyId = isnull((select top 1 MyId+1 from table where id<> @id order by MyId), 1) where id=@id
end
这里,不使用id做连续号(可能意外),而使用MyId做连续号,默认值可以为0,但是,要再次查询才能获得这个值,性能稍有不如。