问题
I'm a bit stuck here trying to copy lots of data (a million records total) from three related tables to other three related tables in the same database.
My table design is as follows:
What I need is to be able to copy data from the draft tables over to the non-draft tables in one transaction which I'm able to roll back if anything goes wrong. This is needed because we don't want i.e. Billing
and BillingPriceLine
records to exist in the database, if the bulk insertion of BillingPriceLineSpecificationDraft
copy failed.
However, since I'm using SqlBulkCopy
for copying the records, I am not able to get a hold of the new IDs to make the correct relations between the three new tables. If I perform a read on i.e. the Billing
table in the transaction to get the correct Billing
ID, I get a time out, which is expected since the tables are locked within the transaction.
I have tried setting the IsolationLevel
enum on the transaction (in fact, I went crazy and tried them all ;-)), but they didn't do anything it seems.
Is there any good way of doing this that I'm missing?
Thanks in advance.
回答1:
You can not really do that with bulk copy.
You also do not need to - it is a very bad practice to touch real tables with SqlBulkCopy because whoever wrote that thing, has no understanding of SQL Server locking mechanisms.
- Create 3 temporary tables with the same table structure as the target.
- SqlBulkCopy into those tables (thus avoiding SqlBulkCopy locking screwing you and avoiding the transaction issue).
- Then, in one transaction, issue 3 INSERT INTO statements using the data from the temporary tables as source.
Result is the best of both worlds. Once you have the data in the temporary tables (with possibly fake ID's) you can pull some really interesting SQL to upate the source table in one transaction with the relevant internal ID's for the next table. The SQL gets complex, but this is the best you can do.
Explanation why SqlBulkCopy is bad:
SqlBulkCopy really is highly problematic - it does screw locking. It demands an exclusive table lock (which is "sort of ok"). It asks for it - but it never waits (i.e. if there is any lock on the table, this fails), then it tries again.... after some time.... until a timeout expires. The result is that on a table with activity it is really hard to get SqlBulkCopy to get ANY lock. The proper code would be to wait for the exclusive lock... well.
来源:https://stackoverflow.com/questions/37188875/using-sqlbulkcopy-in-one-transaction-for-multiple-related-tables