我有查询更新和插入大量数据:
update [TableA]
set ColumnA1 = 'ValueA1'
DECLARE @MyCursor CURSOR;
DECLARE @MyField char(16);
BEGIN
SET @MyCursor = CURSOR FOR
select ColumnA1 from [TableA]
Where ColumnA2 = ValueA2
OPEN @MyCursor
FETCH NEXT FROM @MyCursor
INTO @MyField
WHILE @@FETCH_STATUS = 0
BEGIN
FETCH NEXT FROM @MyCursor
INTO @MyField
Insert Into TableB(ColumnB1, ColumnB2, ColumnB3, ColumnB4, ColumnB5, ColumnB6)
Values(@MyField, ValueB1, ValueB2, ValueB3, ValueB4, ValueB5)
END;
CLOSE @MyCursor ;
DEALLOCATE @MyCursor;
END;
首先我注意到我更新了TableA,然后使用更新的TableA记录将一些日志信息插入TableB。我想以事务方式执行整个sql命令。我担心使用begin transaction
锁定整个sql server数据库。我已经使用了Entity Framework,但我还不知道如何在不锁定sql server的情况下处理这类命令。
答案 0 :(得分:1)
根据Hamlets评论,没有“整个数据库”lock。最接近的可能是将数据库放入single user mode。
但是,在高度争用的数据上对行或表进行广泛(独占)锁定会影响同时尝试读取已提交数据或其他编写器的其他查询的性能。
e.g。由于下面的更新可能会更改所有行,因此在TableA上几乎肯定会TABLOCK
,因此如果您将整个发布的SQL包装在事务中,TableA
也将在游标持续时间内被锁定。 / p>
UPDATE [TableA]
SET ColumnA1 = 'ValueA1';
似乎光标本身是不必要的并且是不可取的 - 只要有可能,用基于集合的方法替换RBAR方法。以下插入应该是等效的:
INSERT Into TableB(ColumnB1, ColumnB2, ColumnB3, ColumnB4, ColumnB5, ColumnB6)
SELECT ColumnA1, ValueB1, ValueB2, ValueB3, ValueB4, ValueB5
FROM [TableA]
WHERE ColumnA2 = ValueA2;
要回答您的最终问题,默认情况下,实体框架会将.SaveChanges()
call包装到a transaction - 到目前为止,对DbContext
所做的所有更改都只会在内存中进行跟踪。
IMO,像EntityFramework这样的ORMS不适合大型批量/批量操作,例如你在这里提供的sql,因为你几乎无法控制生成的Sql。
如果您要将此批量作业转移到.Net,我可以建议
您将能够使用共享SqlConnection
控制上述事务(SqlBulkCopy有一个构造函数采用SqlConnection)。
顺便说一句,如果您的要求不要求您在事务下执行整个操作序列,则可以考虑将更新+插入批处理批量转换为<每次提交5000,以避免lock escalation issues。